DynamicCorsOriginWithoutVary
warning native off by defaultGroup: Probable bugs
A cacheable response whose CORS origin varies by request needs Vary: Origin. Include the variation so shared caches distinguish those responses. This inspection is disabled by default because the intended policy or use can vary.
Example
php
<?php
header("Access-Control-Allow-Origin: ".$_SERVER["HTTP_ORIGIN"]); header("Cache-Control: public, max-age=60");Reported:
- line 2: Include Vary: Origin for cacheable origin-dependent responses.
Detection
- Resolved header builds Access-Control-Allow-Origin from $_SERVER["HTTP_ORIGIN"] and same straight-line scope explicitly permits shared caching via Cache-Control public/max-age without Vary: Origin or Vary: *. Requires cacheability evidence; dynamic unrelated origins excluded.
- The supported initial proof uses an origin header followed by a contiguous block of known header operations. Private, no-store or no-cache directives do not establish shared cacheability. Unknown later operations discard final header-state proof; explicit replacement and removal must be accounted for.
- The header() statements directly before the origin header, with nothing else in between, belong to the same response. The nearest earlier Vary header naming Origin or
*covers the origin, unless a later single-argument Vary header in the block replaces it. A Vary header passed a second argument may append rather than replace, so it neither removes existing coverage nor hides an older Vary header. An earlier header() call whose string is unknown discards the proof. Any other statement ends the backward scan. - Source provenance additionally requires an untouched lexical superglobal prefix. Exclude earlier explicit writes to or reference escapes of that superglobal and any earlier write or reference target involving GLOBALS. Reject all earlier object construction and unknown calls, including calls with no arguments, method calls and static calls; only resolved strlen, is_string, ctype_alnum, ctype_digit or in_array are supported pure-call exceptions. Traverse at most 4096 AST nodes, excluding nested variable scopes; exhaustion or unsupported source mutation discards the request-origin proof.
- Resolve builtin functions, classes, constants and methods; user-defined lookalikes are excluded. Follow only bounded local proof in one lexical scope.
- Emit one finding per violating operation, not per supporting evidence statement. When the proof is inconclusive, nothing is reported.
Exceptions (no report)
- Do not report unresolved or shadowed builtin names, unknown values, or incomplete receiver/type provenance. Unknown calls, aliases escaping, and intervening writes invalidate local proof. Similar code outside the stated detection contract is not a finding.
- Both standard suppression forms suppress this native ID.
Fix
None. Choosing a repair requires intent or additional runtime information.
PHP versions
Available throughout supported PHP 5.3–8.5 where the referenced builtin/extension is available. Respect syntax/version-specific receiver contracts; unavailable APIs are not this rule’s concern.
Configure
In custos.json:
json
{
"rules": {
"DynamicCorsOriginWithoutVary": {
"enabled": true
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
php
// @custos-ignore DynamicCorsOriginWithoutVary