Skip to content

VNX-344: Use of Invariant Value in Dynamically Changing Context

A Content-Security-Policy nonce or an OAuth state parameter that must be fresh per response or per login is written into the source as a constant, in Go, Java, JavaScript/TypeScript, Python, PHP and HTML.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-344
Severity Medium
SARIF level warning
Kind sast
Languages Go, Java, JavaScript / TypeScript, Python, PHP, HTML
CWE CWE-344
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C
Tags invariant-value cwe-344 csp oauth
Source internal/sast/rules/vnx-344.rego

Overview

VNX-344 maps to CWE-344: Use of Invariant Value in Dynamically Changing Context. It reports values whose security depends on being different every time, written as constants:

  • a Content-Security-Policy nonce source with a literal value ('nonce-abc123') in a header string, a helmet/CSP directive list or an HTML <meta> policy, and a <script>/<style> nonce attribute with a literal value;
  • an OAuth authorisation request whose state is a constant: a literal state=value in an authorize URL (one that carries response_type= or redirect_uri=, or whose path is /authorize), authorization_url()/authorize_redirect()/create_authorization_url() given state="literal", Go oauth2.Config.AuthCodeURL("literal"), league/oauth2-client getAuthorizationUrl(['state' => 'literal']), and in JavaScript/TypeScript a state: 'literal' option given to simple-oauth2 authorizeURL(), openid-client authorizationUrl() or passport.authenticate() (passport’s state: true is not reported).

Why This Matters

A CSP nonce only works because an attacker cannot guess it. A fixed nonce is visible to anyone who loads the page once, so injected script that carries it runs and the policy protects nothing. A fixed OAuth state gives the callback no way to tell a login this browser started from one an attacker forced on it (login CSRF).

Not reported: nonces and states interpolated from a variable (f-strings, template literals, %s, concatenation, {{ }} and <%= %> templates, JSX attributes), placeholders substituted at runtime ('nonce-__NONCE__'), REST filters such as ?client_id=5&state=open, comments, test code, and identifiers that merely contain the words. Constant IVs, nonces and salts passed to cipher or KDF calls are VNX-CRYPTO-010’s concern; hard-coded secrets belong to the secrets rules.

What Gets Flagged

w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'nonce-Zm9vYmFyMTIz'")
<script nonce="2726c7f26c" src="/main.js"></script>
url, _ = oauth.authorization_url("https://idp.example.com/authorize", state="xyz123")
url := googleConf.AuthCodeURL("random-state-string", oauth2.AccessTypeOffline)

Remediation

  1. Generate a CSP nonce per response from a cryptographic generator (secrets.token_urlsafe(16), crypto.randomBytes(16), crypto/rand) and put the same value in the header and in the page’s nonce attributes.
  2. Generate the OAuth state per authorisation request, store it in the session, and compare it on the callback before exchanging the code.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-344
// nosec vnx-344

Or record an ignore rule: vulnetix ignore add --rule vnx-344 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-344.rego, cli/internal/sast/docs/vnx-344.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.