NoirAIContext
NoirAIContext enriches each endpoint with an AIContext — the
guards / callees / sinks / validators / signals an LLM (or a human
triage pass) needs to reason about the route. The heavy lifting
lives in the collaborators required above:
PatternDefinition/Patterns— the declarative detection catalogs (sinks, validators, guards, parameter classes).SourceReader— cached source reads + snippet extraction.PatternMatcher— the stateless name/snippet detection engine.Builder— orchestrates every populate step per endpoint.
This file keeps only the public module surface: building context for
a batch of endpoints, and the --ai-context=… feature filter.
Constants
Every name --ai-context= accepts, including the all alias.
Authentication guard — "verifies who you are". Login required, JWT verification, session check.
Authorization guard — "verifies what you can do". Role, permission, ability, policy checks. Distinct from authentication because the common security gap is "logged in but no further check" → horizontal / vertical privilege escalation.
CORS open-with-credentials. Wildcard * origin combined with
credentials: true is the textbook CORS misconfiguration —
browsers actually block this combination at the spec level for
security reasons, so seeing it in code means either the
config is broken or the developer is reaching for something
the spec forbids. Either way, worth a sharp signal.
The check is order-independent across two regex windows so
origin: '*' followed by credentials: true and the reverse
both fire.
Negative protection: explicit CSRF bypass. Emit as a SIGNAL (not a guard) so the LLM knows protection is intentionally disabled here and the endpoint warrants extra scrutiny.
CSRF protection. Different layer from authn/authz — protects against cross-site request forgery via tokens / SameSite cookies / origin checks. Absent on state-changing endpoints is usually worth a review note.
Accepted from the user as "every bucket". Not a bucket itself, so it is
kept out of FEATURES and added back only where user input is validated.
The --ai-context bucket vocabulary, in emission order.
There used to be four copies of this list — two in options.cr (the CLI
help text and the validator), one in NoirAIContext.parse_feature_set,
one in OutputBuilderCommon#ai_context_feature_filter — and they
disagreed. sources was in the two that decide what gets emitted and
missing from the two that decide what the CLI accepts, so
--ai-context=sources was rejected for a bucket the augmentor and every
output builder fully implement. The only way to see sources was to ask
for all of them, and --ai-context=guards,sources was unreachable.
Anything that needs the vocabulary reads it here.
JWT verification bypass — explicit "trust whatever we
received without checking" shapes across pyjwt / jsonwebtoken
/ java-jwt / ruby-jwt. Each pattern is a single-line dead
giveaway, surfaced as a sharp signal alongside csrf_exempt /
cors_open. The kind is jwt_unsafe so consumers can sort it
next to csrf_exempt.
Sink kinds that only make sense on a mobile deep-link endpoint — a
WebView load or an intent launch fed by inbound deep-link data. They
live in the global SINK_PATTERNS so the catalog stays in one place,
but the Builder evaluates them only for endpoint.mobile? endpoints:
an HTTP route handler never opens a WebView or launches an Android
intent, so firing these against every route is conceptually wrong
(and a latent FP source as the name/source patterns broaden).
The non-mobile view of the sink catalog, computed once. Used for HTTP endpoints so the two mobile-only sinks above are never matched against server-side route/callee snippets.
Rate limiting / throttling. Often the only defence on credential-handling endpoints against credential stuffing / brute-force. Confidence is moderate because not every endpoint needs rate limiting (only credential / lookup-style ones).
Class methods
Parses a --ai-context=… value into the set of buckets that survive the
filter. An empty value, or one naming all, means every bucket.
Names are case-folded to match the CLI, which lowercases before storing:
a config-file ai_context_features: "Guards" reaches here unmodified and
would otherwise match no bucket at all.
Names in raw that are outside the accepted vocabulary, in the user's
original spelling so an error message can echo the typo as written.
--ai-context= validates through this; so does the effective option
value after config + CLI are merged. Without the second check a typo in
a config file's ai_context_features reached parse_feature_set,
matched no bucket, and silently emptied every endpoint's AI context —
the same value on the command line was a hard error.
Instance methods
Clears AIContext buckets the user didn't request. Mirrors the
plain-text builder's feature filter so JSON/YAML/SARIF/Postman/
OAS — which serialize the struct directly — show the same
subset the user asked for via --ai-context=guards,sinks.
features holds bucket names from NoirAIContext::FEATURES. An empty
set, or one naming every bucket, is a no-op.