Incorrect Provision of Specified Functionality

Description

Incorrect Provision of Specified Functionality is a vulnerability where code does not operate according to its published specifications or documented behavior, potentially leading to incorrect usage by callers who rely on that documentation. When a function or API behaves differently than documented—returning wrong error codes, omitting advertised validation, or having undocumented side effects—callers who trust the specification may use the code incorrectly, leading to security vulnerabilities or reliability issues. This disconnect between documented and actual behavior creates exploitable conditions when callers make security decisions based on expected but non-existent functionality.

Risk

When code deviates from specifications, callers' security assumptions become invalid. Functions that claim to validate input but don't may allow injection attacks. Error codes that don't reflect actual errors lead to improper error handling. Security libraries that don't perform advertised checks provide false confidence while leaving systems vulnerable. API consumers build systems assuming documented behavior, creating systemic vulnerabilities when that behavior differs. The risk compounds when multiple systems integrate, each assuming the other follows specifications. Version updates that subtly change behavior without updating documentation create regression vulnerabilities.

Solution

Ensure code strictly conforms to its documented specifications. When specifications cannot be met, update documentation to reflect actual behavior and communicate changes to consumers. Implement comprehensive tests that verify behavior matches specifications. Use contract-based programming or assertions to enforce documented pre/post-conditions. Conduct code reviews specifically checking specification compliance. Version APIs properly and clearly document behavioral changes between versions. Provide error returns that accurately reflect the actual error condition. When wrapping third-party functionality, verify it behaves as documented before relying on it.

Common Consequences

ImpactDetails
OtherScope: Other

Quality Degradation - Applications malfunction when dependent code doesn't behave as specified, causing unpredictable failures.
Access ControlScope: Access Control

Bypass Protection Mechanism - Security checks that don't perform advertised validation allow bypasses.
IntegrityScope: Integrity

Unexpected State - Systems enter inconsistent states when functions don't maintain documented invariants.

Example Code

Vulnerable Code

// Vulnerable: Servlet claims to validate but doesn't
/**
 * Process user input.
 * @param input User input - validated for SQL injection
 * @return processed result
 */
@WebServlet("/process")
public class VulnerableServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {

        String input = request.getParameter("data");

        try {
            // Documentation claims SQL injection validation
            // but NO VALIDATION IS ACTUALLY PERFORMED!
            String result = processData(input);  // Vulnerable!
            response.getWriter().write(result);
        } catch (Exception e) {
            // Vulnerable: Returns 200 OK even on error!
            response.setStatus(200);  // Should be error status
            response.getWriter().write("Processed");
        }
    }

    private String processData(String input) throws SQLException {
        // Directly uses unvalidated input in query
        return db.query("SELECT * FROM data WHERE value = '" + input + "'");
    }
}

// Vulnerable: Returns wrong status code on error
@WebServlet("/resource")
public class VulnerableStatusServlet extends HttpServlet {

    /**
     * Get resource by ID.
     * Returns 404 if resource not found, 500 on server error.
     */
    @Override
    protected void doGet(HttpServletRequest request,
                        HttpServletResponse response)
            throws ServletException, IOException {

        String id = request.getParameter("id");

        try {
            Resource res = findResource(id);
            writeResponse(response, res);
        } catch (ResourceNotFoundException e) {
            // Correct: 404 for not found
            response.sendError(404, "Resource not found");
        } catch (IOException e) {
            // Vulnerable: Returns 404 (not found) instead of 500 (server error)!
            // Callers may think resource doesn't exist when server actually failed
            response.sendError(404, "Resource not found");  // WRONG!
        }
    }
}
// Vulnerable: PKCS#11 library with incorrect error reporting
// Documented: Returns CKR_SIGNATURE_INVALID if signature verification fails

CK_RV vulnerable_verify_signature(
    CK_SESSION_HANDLE session,
    CK_BYTE_PTR data,
    CK_ULONG data_len,
    CK_BYTE_PTR signature,
    CK_ULONG sig_len
) {
    // Attempt verification
    int result = internal_verify(data, data_len, signature, sig_len);

    if (result != VERIFY_SUCCESS) {
        // Vulnerable: Returns OK even when signature is invalid!
        // Callers trust this library to return errors on invalid signatures
        return CKR_OK;  // Should be CKR_SIGNATURE_INVALID
    }

    return CKR_OK;
}

// Caller trusting the specification
void caller_function(CK_BYTE_PTR data, CK_BYTE_PTR sig) {
    CK_RV rv = vulnerable_verify_signature(session, data, len, sig, sig_len);

    if (rv == CKR_OK) {
        // Specification says OK means valid signature
        // But invalid signatures also return OK!
        trust_data(data);  // Trusts potentially forged data
    }
}
# Vulnerable: Password validation function that doesn't work as documented
def validate_password(password):
    """
    Validate password meets security requirements.

    Requirements:
    - Minimum 8 characters
    - At least one uppercase letter
    - At least one lowercase letter
    - At least one digit
    - At least one special character

    Returns:
        True if password is valid, False otherwise
    """
    # Vulnerable: Only checks length, not other requirements!
    if len(password) >= 8:
        return True  # Documentation says it checks everything!
    return False

# Caller trusts the documentation
def register_user(username, password):
    if not validate_password(password):
        raise ValueError("Password doesn't meet requirements")

    # Caller assumes password has uppercase, digit, special char...
    # But it might just be "aaaaaaaa" - meeting only length requirement
    create_user(username, password)
// Vulnerable: Cache that doesn't honor TTL as documented
/**
 * Thread-safe cache with time-to-live support.
 * Items are automatically removed after TTL expires.
 */
public class VulnerableCache<K, V> {
    private final Map<K, CacheEntry<V>> cache = new ConcurrentHashMap<>();
    private final long defaultTtlMs;

    /**
     * Get value from cache.
     * @return value if present and not expired, null otherwise
     */
    public V get(K key) {
        CacheEntry<V> entry = cache.get(key);
        if (entry == null) {
            return null;
        }

        // Vulnerable: TTL check is documented but not implemented!
        // Expired entries are still returned
        // if (entry.isExpired()) {
        //     cache.remove(key);
        //     return null;
        // }

        return entry.getValue();  // Returns expired entries!
    }
}

// Security implications: auth tokens that should have expired
// continue to be treated as valid

Fixed Code

// Fixed: Servlet with proper validation and error handling
@WebServlet("/process")
public class SecureServlet extends HttpServlet {

    /**
     * Process user input.
     * @param input User input - validated for SQL injection
     * @return processed result
     * @throws ValidationException if input contains SQL injection patterns
     */
    @Override
    protected void doPost(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {

        String input = request.getParameter("data");

        try {
            // Fixed: Actually validate as documented
            validateInput(input);

            String result = processData(input);
            response.setStatus(HttpServletResponse.SC_OK);
            response.getWriter().write(result);

        } catch (ValidationException e) {
            // Fixed: Proper error response
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().write("Invalid input: " + e.getMessage());

        } catch (SQLException e) {
            // Fixed: Correct error code for server errors
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write("Processing error");
            logger.error("Database error", e);
        }
    }

    private void validateInput(String input) throws ValidationException {
        // Fixed: Implement the documented validation
        if (input == null) {
            throw new ValidationException("Input required");
        }
        // Check for SQL injection patterns
        if (containsSqlInjection(input)) {
            throw new ValidationException("Invalid characters in input");
        }
    }

    private String processData(String input) throws SQLException {
        // Fixed: Use prepared statement
        PreparedStatement stmt = db.prepareStatement(
            "SELECT * FROM data WHERE value = ?");
        stmt.setString(1, input);
        return executeAndFormat(stmt);
    }
}

// Fixed: Correct error status codes
@WebServlet("/resource")
public class SecureStatusServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request,
                        HttpServletResponse response)
            throws ServletException, IOException {

        String id = request.getParameter("id");

        try {
            Resource res = findResource(id);
            writeResponse(response, res);
        } catch (ResourceNotFoundException e) {
            // Fixed: 404 for not found - correct
            response.sendError(HttpServletResponse.SC_NOT_FOUND,
                             "Resource not found");
        } catch (IOException e) {
            // Fixed: 500 for server error - matches documentation
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                             "Server error");
            logger.error("Error retrieving resource", e);
        }
    }
}
// Fixed: PKCS#11 library with correct error reporting
CK_RV secure_verify_signature(
    CK_SESSION_HANDLE session,
    CK_BYTE_PTR data,
    CK_ULONG data_len,
    CK_BYTE_PTR signature,
    CK_ULONG sig_len
) {
    // Validate inputs
    if (data == NULL || signature == NULL) {
        return CKR_ARGUMENTS_BAD;
    }

    // Attempt verification
    int result = internal_verify(data, data_len, signature, sig_len);

    // Fixed: Return correct status as documented
    switch (result) {
        case VERIFY_SUCCESS:
            return CKR_OK;

        case VERIFY_INVALID_SIGNATURE:
            // Fixed: Return error for invalid signature
            return CKR_SIGNATURE_INVALID;

        case VERIFY_KEY_ERROR:
            return CKR_KEY_TYPE_INCONSISTENT;

        default:
            return CKR_FUNCTION_FAILED;
    }
}
# Fixed: Password validation matching documentation
import re

def validate_password(password: str) -> bool:
    """
    Validate password meets security requirements.

    Requirements:
    - Minimum 8 characters
    - At least one uppercase letter
    - At least one lowercase letter
    - At least one digit
    - At least one special character

    Returns:
        True if password is valid, False otherwise
    """
    if password is None:
        return False

    # Fixed: Implement ALL documented requirements
    if len(password) < 8:
        return False

    if not re.search(r'[A-Z]', password):
        return False

    if not re.search(r'[a-z]', password):
        return False

    if not re.search(r'\d', password):
        return False

    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
        return False

    return True

# Alternative: Return detailed validation results
from dataclasses import dataclass
from typing import List

@dataclass
class PasswordValidationResult:
    is_valid: bool
    failures: List[str]

def validate_password_detailed(password: str) -> PasswordValidationResult:
    """Validate password with detailed failure reasons."""
    failures = []

    if password is None or len(password) < 8:
        failures.append("Must be at least 8 characters")

    if not re.search(r'[A-Z]', password or ''):
        failures.append("Must contain uppercase letter")

    if not re.search(r'[a-z]', password or ''):
        failures.append("Must contain lowercase letter")

    if not re.search(r'\d', password or ''):
        failures.append("Must contain digit")

    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password or ''):
        failures.append("Must contain special character")

    return PasswordValidationResult(
        is_valid=len(failures) == 0,
        failures=failures
    )
// Fixed: Cache honoring TTL as documented
public class SecureCache<K, V> {
    private final Map<K, CacheEntry<V>> cache = new ConcurrentHashMap<>();
    private final long defaultTtlMs;

    public V get(K key) {
        CacheEntry<V> entry = cache.get(key);
        if (entry == null) {
            return null;
        }

        // Fixed: Implement TTL check as documented
        if (entry.isExpired()) {
            cache.remove(key);  // Remove expired entry
            return null;  // Return null for expired entries
        }

        return entry.getValue();
    }

    // Fixed: Add tests that verify TTL behavior
    @Test
    public void testExpiredEntryReturnsNull() {
        cache.put("key", "value", Duration.ofMillis(100));
        Thread.sleep(150);
        assertNull(cache.get("key"));  // Must return null after TTL
    }
}

CVE Examples

  • CVE-2002-1446: PKCS#11 library error checking returned "OK" status despite invalid signatures.
  • CVE-2001-1559: System call returned wrong value, leading to NULL pointer dereference.
  • CVE-2003-0187: Linked list implementation caused large timeouts on unconfirmed connections.
  • CVE-1999-1446: "Clear History" UI function didn't actually clear the visited URLs list.

References

  1. MITRE Corporation. "CWE-684: Incorrect Provision of Specified Functionality." https://cwe.mitre.org/data/definitions/684.html
  2. Meyer, Bertrand. "Design by Contract."
  3. OWASP. "API Security Guidelines."