Documentation
¶
Overview ¶
Package recontext ports src/sec_af/context.py — the strategy-aware RECON projections that every HUNT prompt embeds.
The package is named `recontext` rather than `context` for the obvious reason: a package called `context` inside this module would shadow the standard library's in every file that imports both, and every reasoner in the port takes a context.Context.
Two kinds of output live here and they are used very differently:
- prune_recon_for_strategy returns a DICT that travels over the wire as the `recon_context` kwarg of a `.call` (reasoners/phases.py:294), so only its key SET matters, not its rendering;
- every recon_context_for_* / *_hints_for_context function returns a STRING that is substituted into a prompt template and shipped to the model, so its bytes matter exactly. Those are golden-tested against the Python functions run over the same fixture (testdata/recon_fixture.json).
Index ¶
- Constants
- Variables
- func FrameworkHintsForContext(recon schemas.ReconResult) string
- func GetContextForStrategy(strategy schemas.HuntStrategy, recon schemas.ReconResult) string
- func GetFrameworkHints(frameworks []string) string
- func GetLanguageHints(languages []string) string
- func LanguageHintsForContext(recon schemas.ReconResult) string
- func PruneReconForStrategy(recon schemas.ReconResult, strategy string) (map[string]any, error)
- func ReconContextForAPISecurity(recon schemas.ReconResult) string
- func ReconContextForAuth(recon schemas.ReconResult) string
- func ReconContextForConfigSecrets(recon schemas.ReconResult) string
- func ReconContextForCrypto(recon schemas.ReconResult) string
- func ReconContextForDataExposure(recon schemas.ReconResult) string
- func ReconContextForInjection(recon schemas.ReconResult) string
- func ReconContextForLogic(recon schemas.ReconResult) string
- func ReconContextForSupplyChain(recon schemas.ReconResult) string
- func ReconContextGeneric(recon schemas.ReconResult) string
- type FrameworkPattern
- type LanguagePattern
Constants ¶
const ( MaxPrimaryItems = 15 // _MAX_PRIMARY_ITEMS MaxSecondaryItems = 10 // _MAX_SECONDARY_ITEMS )
Truncation limits, ported from context.py's module constants.
Variables ¶
var BaseReconFields = []string{"languages", "frameworks", "lines_of_code", "file_count"}
BaseReconFields ports _BASE_RECON_FIELDS: the ReconResult fields every strategy projection keeps regardless of which sections it asked for.
var FrameworkPatterns = map[string]FrameworkPattern{ "django": { SecurityFeatures: []string{ "ORM queries are parameterized by default", "Templates auto-escape variables by default", "CsrfViewMiddleware enforces CSRF tokens for unsafe methods", }, DoNotFlag: []string{ "Django ORM filter/exclude (parameterized)", "CSRF with CsrfViewMiddleware active", "XSS in Django templates (auto-escaped)", }, WatchFor: []string{"raw() queries", "mark_safe()", "|safe filter", "CSRF_COOKIE_SECURE=False"}, CommonVulns: []string{ "SQL injection via raw SQL and string formatting", "XSS via unsafe template escape bypasses", "CSRF weakening via middleware/settings overrides", }, }, "flask": { SecurityFeatures: []string{ "Jinja2 auto-escapes in HTML templates", "Werkzeug handles request parsing safely by default", "Blueprint/middleware patterns can centralize auth checks", }, DoNotFlag: []string{ "Jinja2 auto-escaped template variables", "Parameterized SQLAlchemy query usage", "Server-side session signing with strong SECRET_KEY", }, WatchFor: []string{ "render_template_string() with user input", "debug=True in production paths", "string-concatenated SQL in execute()", "hardcoded SECRET_KEY", }, CommonVulns: []string{ "SSTI through dynamic template rendering", "XSS when auto-escaping is bypassed", "session tampering risk with weak secret/config", }, }, "fastapi": { SecurityFeatures: []string{ "Pydantic request validation reduces malformed input", "Dependency injection supports reusable auth guards", "OpenAPI schema generation improves contract visibility", }, DoNotFlag: []string{ "Pydantic model validation errors", "Dependency-based auth checks clearly enforced", "Parameterized SQLAlchemy/async driver usage", }, WatchFor: []string{ "Depends() omitted on privileged routes", "raw SQL in text()/execute() with interpolation", "CORS allow_origins=['*'] with credentials", "unsafe deserialization in background tasks", }, CommonVulns: []string{ "Auth bypass on unprotected routes", "SQL injection in manually assembled queries", "CORS misconfiguration exposing credentialed APIs", }, }, "express": { SecurityFeatures: []string{ "Router middleware can enforce auth and rate limits", "helmet can apply secure HTTP headers", "Validated schema middleware can constrain input", }, DoNotFlag: []string{ "Parameterized ORM/database queries", "helmet defaults correctly applied", "router-level auth middleware consistently enforced", }, WatchFor: []string{ "res.send()/res.json() leaking sensitive internals", "string-built SQL in query()", "trust proxy misconfiguration", "open CORS with credentials", }, CommonVulns: []string{ "Authz gaps from missing middleware on routes", "NoSQL/SQL injection from unsanitized request bodies", "Open redirect and SSRF through unvalidated URLs", }, }, "nextjs": { SecurityFeatures: []string{ "React JSX auto-escapes output by default", "API routes can share centralized auth middleware", "Server/client boundaries reduce accidental secret exposure", }, DoNotFlag: []string{ "React JSX escaped rendering", "getServerSideProps/getServerSession with proper auth checks", "Typed route handlers with validated schema guards", }, WatchFor: []string{ "dangerouslySetInnerHTML", "unprotected API routes under /api", "secret leakage to client bundles", "rewrites/redirects from untrusted user input", }, CommonVulns: []string{ "XSS through dangerous HTML rendering", "IDOR/auth bypass in API handlers", "Sensitive env/config exposure in client-side code", }, }, "spring": { SecurityFeatures: []string{ "Spring Security provides auth, CSRF, and filter chain defaults", "JPA/Hibernate prepared parameter binding by default", "Bean validation can enforce request constraints", }, DoNotFlag: []string{ "PreparedStatement/JPA named parameter usage", "Spring Security CSRF/auth filters clearly active", "Thymeleaf auto-escaped output", }, WatchFor: []string{ "@PreAuthorize missing on sensitive methods", "JdbcTemplate/raw Statement with concatenation", "csrf().disable() on browser session flows", "Actuator endpoints exposed without auth", }, CommonVulns: []string{ "Authz bypass from weak method-level security", "SQL injection in raw JDBC queries", "Sensitive management endpoint exposure", }, }, "rails": { SecurityFeatures: []string{ "ActiveRecord parameterization for hash/array queries", "ERB templates auto-escape by default", "Built-in CSRF protection for non-GET requests", }, DoNotFlag: []string{ "ActiveRecord where with hash conditions", "Rails protect_from_forgery active", "ERB escaped output without raw/html_safe", }, WatchFor: []string{ "where/order/find_by_sql with string interpolation", "raw()/html_safe on untrusted content", "skip_before_action on auth filters", "mass assignment via permit!", }, CommonVulns: []string{ "SQL injection in manual query fragments", "XSS via unsafe output helpers", "Authz bypass from skipped controller guards", }, }, "aspnet": { SecurityFeatures: []string{ "Razor encodes output by default", "Model binding and data annotations aid validation", "Anti-forgery tokens support CSRF defense", }, DoNotFlag: []string{ "Entity Framework LINQ parameterized queries", "Razor default HTML encoding", "ValidateAntiForgeryToken in state-changing MVC actions", }, WatchFor: []string{ "Html.Raw() on untrusted input", "FromBody models without validation", "Authorize missing on privileged endpoints", "custom SQL built via string interpolation", }, CommonVulns: []string{ "XSS through Html.Raw and unencoded output", "Auth/authz bypass on unsecured controllers", "SQL injection in handcrafted query strings", }, }, "react": { SecurityFeatures: []string{ "JSX escapes strings before DOM rendering", "Component model encourages explicit data flow", "Framework discourages direct DOM mutation", }, DoNotFlag: []string{ "Standard JSX expression rendering", "textContent assignment for untrusted text", "sanitized HTML via trusted DOMPurify policy", }, WatchFor: []string{ "dangerouslySetInnerHTML", "untrusted URL assignment to href/src", "eval/new Function in client code", "token/secret exposure in bundles", }, CommonVulns: []string{ "DOM XSS through unsafe HTML injection", "Open redirect via unvalidated navigation targets", "Sensitive data exposure in frontend artifacts", }, }, "vue": { SecurityFeatures: []string{ "Mustache template interpolation escapes HTML", "Component props/events provide explicit boundaries", "Router guards can enforce auth flows", }, DoNotFlag: []string{ "escaped template interpolation {{ value }}", "validated route guards protecting private routes", "sanitized content rendered through safe components", }, WatchFor: []string{ "v-html with untrusted data", "dynamic component/template compilation", "unsafe URL bindings in href/src", "client-side auth checks without server enforcement", }, CommonVulns: []string{ "XSS through v-html and unsafe render paths", "Auth bypass from client-only route protection", "Open redirect patterns in router navigation", }, }, "angular": { SecurityFeatures: []string{ "Template binding sanitization for HTML/URL contexts", "HttpClient and interceptor patterns support central controls", "AOT compilation limits runtime template injection vectors", }, DoNotFlag: []string{ "default Angular template binding sanitization", "HttpClient usage with validated request schemas", "route guards consistently applied", }, WatchFor: []string{ "bypassSecurityTrustHtml/Url/Script", "[innerHTML] with unsanitized input", "direct DOM APIs via ElementRef/nativeElement", "auth only in client guard without server checks", }, CommonVulns: []string{ "XSS when sanitizer is explicitly bypassed", "Token leakage in local storage/logging", "Authorization gaps due to client-only enforcement", }, }, }
FrameworkPatterns ports _framework_hints.py FRAMEWORK_PATTERNS, keyed by the NORMALIZED framework name (see normalizeFramework).
var LanguagePatterns = map[string]LanguagePattern{ "python": { InjectionSinks: []string{"cursor.execute", "os.system", "subprocess.run", "eval", "exec", "pickle.loads"}, SafePatterns: []string{ "parameterized queries with %s placeholders", "subprocess.run with list args (no shell=True)", }, DoNotFlag: []string{ "Django ORM queries (uses parameterized queries internally)", "SQLAlchemy text() with bound params", }, FrameworkSpecifics: "Check for Django/Flask/FastAPI-specific patterns. Django auto-escapes templates. Flask Jinja2 auto-escapes.", }, "javascript": { InjectionSinks: []string{"eval", "Function()", "innerHTML", "document.write", "child_process.exec", "new Function"}, SafePatterns: []string{"DOMPurify.sanitize()", "textContent assignment", "parameterized pg queries"}, DoNotFlag: []string{"React JSX expressions (auto-escaped)", "Angular template bindings (sanitized by default)"}, FrameworkSpecifics: "Check for React/Vue/Angular-specific XSS patterns. React auto-escapes JSX. Vue v-html is dangerous.", }, "typescript": { InjectionSinks: []string{"eval", "Function()", "innerHTML", "document.write", "child_process.exec"}, SafePatterns: []string{"DOMPurify.sanitize()", "textContent assignment", "Prisma parameterized queries"}, DoNotFlag: []string{"React JSX expressions", "Angular template bindings", "Prisma ORM queries"}, FrameworkSpecifics: "TypeScript adds type safety but doesn't prevent injection. Check for any type assertions near user input.", }, "go": { InjectionSinks: []string{"fmt.Sprintf into SQL", "exec.Command with user input", "template.HTML()", "os.Exec"}, SafePatterns: []string{ "database/sql with ? placeholders", "html/template (auto-escapes)", "exec.Command with separate args", }, DoNotFlag: []string{ "GORM parameterized queries", "html/template default escaping", "database/sql prepared statements", }, FrameworkSpecifics: "Go's html/template auto-escapes. text/template does NOT. Check for text/template serving HTML.", }, "java": { InjectionSinks: []string{ "Statement.execute", "Runtime.exec", "ProcessBuilder with concatenated strings", "ScriptEngine.eval", }, SafePatterns: []string{"PreparedStatement with ?", "JNDI lookup with allowlist", "OWASP ESAPI encoding"}, DoNotFlag: []string{"JPA/Hibernate named parameters", "Spring Security CSRF protection", "PreparedStatement usage"}, FrameworkSpecifics: "Check Spring Boot auto-config. Thymeleaf auto-escapes. JSP needs explicit escaping.", }, "ruby": { InjectionSinks: []string{"eval", "system", "exec", "send", "public_send", "ERB.new with user input"}, SafePatterns: []string{"ActiveRecord parameterized queries", "Rack::Utils.escape_html", "sanitize helper in Rails"}, DoNotFlag: []string{ "ActiveRecord where with hash conditions", "Rails CSRF protection", "Rails html_safe on constants", }, FrameworkSpecifics: "Rails auto-escapes ERB templates. raw/html_safe bypasses escaping. Check for mass assignment.", }, "csharp": { InjectionSinks: []string{"SqlCommand with concatenation", "Process.Start with user input", "Razor @Html.Raw()"}, SafePatterns: []string{"SqlParameter", "Entity Framework LINQ", "Razor auto-encoding"}, DoNotFlag: []string{"Entity Framework LINQ queries", "ASP.NET anti-forgery tokens", "Razor default encoding"}, FrameworkSpecifics: "ASP.NET Core Razor auto-encodes. @Html.Raw() is dangerous. Check for [ValidateAntiForgeryToken].", }, }
LanguagePatterns ports _language_hints.py LANGUAGE_PATTERNS.
Keyed by the LOWERCASE language name, because get_language_hints lowercases every detected language before the lookup.
var StrategyContextMap = map[string][]string{
"injection": {"architecture", "data_flows", "security_context"},
"xss": {"architecture", "data_flows", "security_context"},
"ssrf": {"architecture", "data_flows", "security_context"},
"auth": {"architecture", "security_context"},
"crypto": {"security_context"},
"dos": {"architecture", "data_flows"},
"data_exposure": {"architecture", "data_flows", "config"},
"supply_chain": {"dependencies"},
"config_secrets": {"config", "architecture"},
"api_security": {"architecture", "security_context", "data_flows"},
"business_logic": {"architecture", "data_flows", "security_context"},
}
StrategyContextMap ports STRATEGY_CONTEXT_MAP: strategy VALUE (the HuntStrategy enum's string, e.g. "business_logic") -> the ReconResult field names its hunter needs.
Python parity: strategies absent from this table — "logic_bugs" is not a separate value (it is an alias of "business_logic"), but "python_specific" and "javascript_specific" genuinely are missing — fall through to the full model_dump() in PruneReconForStrategy.
Functions ¶
func FrameworkHintsForContext ¶
func FrameworkHintsForContext(recon schemas.ReconResult) string
FrameworkHintsForContext ports framework_hints_for_context — "Build framework-specific hints from recon-detected frameworks."
func GetContextForStrategy ¶
func GetContextForStrategy(strategy schemas.HuntStrategy, recon schemas.ReconResult) string
GetContextForStrategy ports get_context_for_strategy:
builder = builders.get(strategy, recon_context_generic) return builder(recon)
XSS, SSRF and DOS have entries in STRATEGY_CONTEXT_MAP but NOT here, so their prompt context is the generic summary while their pruned dict still drops the sections they do not need. (Their hunter modules build their own JSON context block instead — agents/hunt/{xss,ssrf,dos}.py.)
func GetFrameworkHints ¶
GetFrameworkHints ports _framework_hints.py get_framework_hints.
Python parity: the normalized names are deduplicated while PRESERVING FIRST APPEARANCE ORDER (Python builds `ordered_unique` with a linear `in` check on a list, not a set), so `["Next", "next.js"]` yields one NEXTJS section, and unknown frameworks silently drop out of the output but still consume their slot in the dedup list.
func GetLanguageHints ¶
GetLanguageHints ports _language_hints.py get_language_hints:
detected = [lang.lower() for lang in languages]
hints = []
for lang in detected:
patterns = LANGUAGE_PATTERNS.get(lang)
if patterns is None: continue
section = [f"Language: {lang.upper()}"]
... four optional lines ...
hints.append("\n".join(section))
if not hints: return "No language-specific hints available for detected languages."
return "LANGUAGE-SPECIFIC GUIDANCE:\n" + "\n\n".join(hints)
Python parity notes:
- There is NO deduplication, unlike get_framework_hints. `["Python", "python"]` emits the PYTHON section twice, and this reproduces that.
- The heading uses `lang.upper()` on the ALREADY-LOWERCASED name, so the spelling always comes from the table key, never from the caller's casing.
- Go's strings.ToLower/ToUpper are Unicode-aware like Python's str.lower()/str.upper(), with the usual rare divergences on characters whose case mapping changes length (Python maps U+0130 to two code points, Go to one). No language name in the table is affected.
func LanguageHintsForContext ¶
func LanguageHintsForContext(recon schemas.ReconResult) string
LanguageHintsForContext ports language_hints_for_context — "Build language-specific hints from recon-detected languages."
func PruneReconForStrategy ¶
PruneReconForStrategy ports prune_recon_for_strategy:
required_fields = STRATEGY_CONTEXT_MAP.get(strategy)
if required_fields is None:
return recon.model_dump()
include_fields = set(_BASE_RECON_FIELDS)
include_fields.update(required_fields)
return recon.model_dump(include=include_fields)
The returned map is the pydantic model_dump() shape: keys are the snake_case pydantic field names (== the Go json tags), values are the nested models. afx.ToMap keeps the values TYPED rather than round-tripping them through JSON, which matters for two reasons: a float field with an integral value stays a float (`10.0`, not `10`) when pyfmt.Dumps renders it, and the nested structs keep their declaration order for anything that renders them.
Python parity notes:
- KEY ORDER. `model_dump(include={...})` emits the surviving keys in ReconResult DECLARATION order (architecture, data_flows, dependencies, config, security_context, languages, frameworks, lines_of_code, file_count, recon_duration_seconds), not in the include-set's order. A Go map has no order at all, which is immaterial here: the only consumer is `recon_context=strategy_context` on a `.call`, i.e. a JSON object where member order carries no meaning.
- `recon_duration_seconds` is in NO strategy's projection and is not a base field, so it survives only on the unmapped-strategy path that returns the full dump.
- The error return has no Python counterpart; afx.ToMap only fails on a non-struct argument, which cannot happen for a ReconResult.
func ReconContextForAPISecurity ¶
func ReconContextForAPISecurity(recon schemas.ReconResult) string
ReconContextForAPISecurity ports recon_context_for_api_security.
func ReconContextForAuth ¶
func ReconContextForAuth(recon schemas.ReconResult) string
ReconContextForAuth ports recon_context_for_auth.
func ReconContextForConfigSecrets ¶
func ReconContextForConfigSecrets(recon schemas.ReconResult) string
ReconContextForConfigSecrets ports recon_context_for_config_secrets.
func ReconContextForCrypto ¶
func ReconContextForCrypto(recon schemas.ReconResult) string
ReconContextForCrypto ports recon_context_for_crypto.
func ReconContextForDataExposure ¶
func ReconContextForDataExposure(recon schemas.ReconResult) string
ReconContextForDataExposure ports recon_context_for_data_exposure.
func ReconContextForInjection ¶
func ReconContextForInjection(recon schemas.ReconResult) string
ReconContextForInjection ports recon_context_for_injection.
Python parity: the flow list prefers UNSANITIZED flows and only falls back to every flow when there are none — `unsanitized_flows if unsanitized_flows else recon.data_flows.flows`, a truthiness test on the filtered list.
func ReconContextForLogic ¶
func ReconContextForLogic(recon schemas.ReconResult) string
ReconContextForLogic ports recon_context_for_logic — the projection HuntStrategy.BUSINESS_LOGIC (a.k.a. LOGIC_BUGS) uses.
func ReconContextForSupplyChain ¶
func ReconContextForSupplyChain(recon schemas.ReconResult) string
ReconContextForSupplyChain ports recon_context_for_supply_chain.
func ReconContextGeneric ¶
func ReconContextGeneric(recon schemas.ReconResult) string
ReconContextGeneric ports recon_context_generic — the fallback projection for every strategy without a dedicated builder, and the recon summary hunt_phase feeds to the CWE-expansion gate (reasoners/phases.py:277).
Types ¶
type FrameworkPattern ¶
type FrameworkPattern struct {
SecurityFeatures []string
DoNotFlag []string
WatchFor []string
CommonVulns []string
}
FrameworkPattern is one entry of Python's FRAMEWORK_PATTERNS (`dict[str, dict[str, list[str]]]` — every entry has all four keys, and get_framework_hints indexes them directly rather than using .get()).
type LanguagePattern ¶
type LanguagePattern struct {
InjectionSinks []string
SafePatterns []string
DoNotFlag []string
FrameworkSpecifics string
}
LanguagePattern is one entry of Python's LANGUAGE_PATTERNS.
Python types it `dict[str, list[str] | str]` and reads it with `.get(key)`, so a missing key and an empty list behave identically (both falsy, both skip the line). A Go struct with zero values reproduces that exactly.