/** * Circle-IR Analyzer * * Main entry point for analyzing source code and producing Circle-IR output. * This is the core static analyzer. LLM-based verification and discovery are out of scope for this library. * * The analysis pipeline runs forty sequential passes over a shared CodeGraph: * 1. TaintMatcherPass — config-based source/sink extraction * 2. ConstantPropagationPass — dead-code detection, symbol table, field taint * 3. LanguageSourcesPass — language-specific sources/sinks (JS, Python, getters) * 4. SinkFilterPass — four-stage false-positive elimination * 5. TaintPropagationPass — DFG-based flow verification * 6. InterproceduralPass — cross-method taint propagation * 7. DeadCodePass — CFG blocks unreachable from entry (CWE-561) * 8. MissingAwaitPass — unawaited async calls in JS/TS (CWE-252) * 9. NPlusOnePass — DB/HTTP calls inside loop bodies (CWE-1049) * 10. MissingPublicDocPass — public methods/types without doc comments * 11. TodoInProdPass — TODO/FIXME/HACK markers in production code * 12. StringConcatLoopPass — string += inside loops, O(n²) allocations (CWE-1046) * 13. SyncIoAsyncPass — blocking *Sync calls inside async functions (CWE-1050) * 14. UncheckedReturnPass — ignored boolean return from File.delete etc. (CWE-252) * 15. NullDerefPass — null-assigned var dereferenced without guard (CWE-476) * 16. ResourceLeakPass — stream/connection opened but never closed (CWE-772) * 17. VariableShadowingPass — inner scope re-declares outer name (CWE-1109) * 18. LeakedGlobalPass — assignment without declaration in JS/TS (CWE-1109) * 19. UnusedVariablePass — local variable declared but value never read (CWE-561) * 20. DependencyFanOutPass — module imports 20+ other modules (architecture smell) * 21. StaleDocRefPass — doc comment references unknown symbol (CWE: none) * 22. InfiniteLoopPass — loops with no reachable exit edge (CWE-835) * 23. DeepInheritancePass — class inheritance depth > 5 (CWE-1086) * 24. RedundantLoopPass — loop-invariant .length/.size()/Math.* (CWE-1050) * 25. UnboundedCollectionPass — collection grows in loop with no size limit (CWE-770) * 26. SerialAwaitPass — sequential awaits with no data dependency (performance) * 27. ReactInlineJsxPass — inline objects/functions in JSX props (performance) * 28. SwallowedExceptionPass — catch blocks with no throw/log/return (CWE-390) * 29. BroadCatchPass — catch(Exception) / bare except (CWE-396) * 30. UnhandledExceptionPass — throw/raise outside any try/catch (CWE-390) * 31. DoubleClosePass — resource closed twice in same method (CWE-675) * 32. UseAfterClosePass — method call on resource after close() (CWE-672) * 33. CleanupVerifyPass — close() does not post-dominate acquisition (CWE-772) * 34. MissingOverridePass — overriding method lacks @Override (Java) * 35. UnusedInterfaceMethodPass — interface method never called in file * 36. BlockingMainThreadPass — blocking crypto/*Sync calls in request handlers (CWE-1050) * 37. ExcessiveAllocationPass — collection/object allocation inside loop bodies (CWE-770) * 38. MissingStreamPass — whole-file read without streaming (performance) * 39. GodClassPass — class with high WMC/LCOM2/CBO metrics (CWE-1060) * 40. NamingConventionPass — class/method names violate language conventions * 41. ScanSecretsPass — hardcoded credentials: provider regexes + Shannon entropy (CWE-798) * 42. Spring4ShellPass — Spring MVC implicit form-data binding RCE (CVE-2022-22965, CWE-94) * 43. MissingSanitizerGatePass — HTML output reached without sanitizer call on dominating path (CWE-79, speculative) * * Removed from default pipeline (raw IR signals still available for circle-ir-ai): * – MissingGuardDomPass — false positives in framework-auth codebases (see pass file) * – FeatureEnvyPass — fires on legitimate delegation patterns (see pass file) */ import type { CircleIR, AnalysisResponse, ProjectAnalysis, ProjectProfile } from './types/index.js'; import type { TaintConfig } from './types/config.js'; import { type SupportedLanguage } from './core/index.js'; import { isFalsePositive } from './analysis/index.js'; import { type DependencyFanOutOptions } from './analysis/passes/dependency-fan-out-pass.js'; import { type UnboundedCollectionOptions } from './analysis/passes/unbounded-collection-pass.js'; import { type NamingConventionOptions } from './analysis/passes/naming-convention-pass.js'; import { type SecurityHeadersOptions } from './analysis/passes/security-headers-pass.js'; export interface AnalyzerOptions { /** * Path to tree-sitter.wasm for parser initialization. */ wasmPath?: string; /** * Pre-compiled WebAssembly.Module for tree-sitter.wasm. * For Cloudflare Workers where dynamic WASM compilation is blocked. */ wasmModule?: WebAssembly.Module; /** * Paths to language-specific WASM files. */ languagePaths?: Partial>; /** * Pre-compiled WebAssembly.Module for language grammars. * For Cloudflare Workers where dynamic WASM compilation is blocked. */ languageModules?: Partial>; /** * Custom taint configuration. */ taintConfig?: TaintConfig; /** * Per-pass configuration options. */ passOptions?: PassOptions; /** * Passes to disable entirely. Use pass names (e.g., 'naming-convention'). */ disabledPasses?: string[]; /** * Wall-time budget (ms) for the entire cross-file phase * (`CrossFilePass.run()` — direct flows, interprocedural, field-binding, * cross-instance aliasing). When exceeded, the remaining sub-phases are * skipped, any taint paths produced so far are kept, and the resulting * `ProjectAnalysis.cross_file_budget_exceeded` flag is set to `true`. * * - `0` disables the breaker (unlimited). * - Omitting the field uses the default of `300_000` (5 minutes), chosen * to comfortably cover large monorepos (~2K files) at post-3.89.0 * pre-index speeds while still catching pathological hangs. * - Consumers operating in CI on >5K-file projects may want to bump this. * * Added in circle-ir 3.89.0 to mitigate #141 (langchain4j 30-min hang). */ crossFileBudgetMs?: number; /** * Defensive per-file finding cap (#142). * * A single file producing more than this many findings is treated as a * structural failure of the analysis pipeline (cross-product blow-up, * mislabelled sink class, or pathological generated code) rather than a * legitimate detection burst. When the cap is exceeded, all individual * findings for that file are dropped and replaced by a single * `saturated-file` advisory carrying the suppressed count, so the signal * stays visible without flooding downstream consumers. * * - `0` disables the cap (unlimited; pre-3.92.0 behaviour). * - Omitting the field uses the default of `1000`, chosen well above the * realistic per-file ceiling (~200 for jedis-shape library facades) and * below the empirical structural-failure floor observed on langchain4j * (~10K findings on a single file before the cross-file phase hang). * * The cap is applied after the pipeline runs but before the result is * returned, so per-pass instrumentation (#145 PR B) still observes the * uncapped findings stream for diagnostic purposes. * * Added in circle-ir 3.92.0 as a defensive tripwire; #143 (the proposed * (source, sink) coalescing schema) was closed as unjustified by * empirical capture data, leaving this as the standalone safeguard. */ perFileFindingCap?: number; /** * Opt into emission of speculative (`confidence: 'medium' | 'low'`) findings. * * Default `false`: only `confidence: 'high'` (or unset, which is the * pre-3.94.0 default for every existing pass) findings reach the consumer. * Speculative findings emitted by dominator/heuristic passes such as the * forthcoming `missing-sanitizer-gate` (#153) are dropped silently. * * Set `true` when a downstream verifier is going to adjudicate the * speculative findings before user presentation; the engine then preserves * the full unfiltered finding stream and the caller is responsible for * filtering the `'medium'`/`'low'` entries. * * Filtering happens in `analyze()` after `emitFindingsInstrumentation` and * before `applyPerFileFindingCap`, so per-pass diagnostics still observe * the full uncapped, unfiltered stream. * * Added in circle-ir 3.94.0 as the pre-req infrastructure for #153. */ includeSpeculative?: boolean; /** * Enable the Tier 1/2/3 entry-point classifier gate that suppresses * `interprocedural_param` taint sources on library-API methods that are * not reachable from a recognised HTTP / RPC / lifecycle entry point. * * Default `true`. The gate has been the unconditional behaviour for * Java since 3.88.0 (#128) and was extended to cover Netty wire-message * handlers in 3.93.0 (#154). 3.95.0 surfaces it as an option so callers * can disable it for debugging, recall-vs-precision tuning, or third- * party harness comparisons that need the un-gated source set. * * Language behaviour: * - `language === 'java'`: gate fires; library-API methods drop their * `interprocedural_param` sources. * - All other languages: gate is a no-op (the classifier returns * `TIER_UNKNOWN` for non-Java, which does not match the drop predicate). * * Set `false` to receive the pre-#128 source set on Java. Useful for: * - Diagnosing why an expected interprocedural flow is missing. * - Comparing against IRIS / CodeQL baselines that don't gate. * - Recall-targeted runs in `circle-ir-ai`. * * Added in 3.95.0 (cognium-dev#137). */ enableEntryPointGate?: boolean; /** * Project profile for the analysis, used by the post-pipeline profile * transform to gate severity changes on findings tagged * `library-api-surface:caller-responsibility` (Sprint 47). See * `docs/ARCHITECTURE.md` ADR-008 for the full decision tree. * * Three forms supported: * - omitted (or `'unknown'`) → 3.105.0 behavior preserved (no * profile-conditional transform applied). * - single `ProjectProfile` string → applies to every file in the scan. * - `Map` → per-file profile (for monorepos with * mixed library/application modules). * * Pillar I: circle-ir never reads the filesystem to detect the profile. * Detection is the caller's responsibility (cognium-dev CLI does it; * circle-ir-ai may provide a richer detector). When the caller cannot * resolve a file, supplying `'unknown'` is the safe default. * * Added in circle-ir 3.106.0 (#169). */ projectProfile?: ProjectProfile | Map; /** * Optional dependency-manifest context for the project. * * When supplied, the `deserialization-safety-gate` pass consults it to * drop or downgrade deserialization sinks whose surrounding library * configuration renders them non-exploitable. Currently supported * shapes (cognium-dev #258): * * - `java.pomXml` — raw `pom.xml` string. The gate parses the * `` property and any `com.alibaba:fastjson` * `` block; when the effective version is a * `*_noneautotype` classifier, autotype is stripped from Fastjson * and `JSON.parseObject` / `JSON.parse` sinks are dropped for that * file. Other manifests (Gradle build.gradle, Python * requirements.txt / pyproject, npm package.json, Cargo.toml) are * accepted here in future scopes. * * Pillar I: circle-ir does not read the filesystem to obtain this * context. The caller (cognium-dev CLI or `analyzeProject`) reads the * manifest once and passes the raw string per file. Absent field → * gate no-ops silently and every sink emits at its default severity. */ dependencyContext?: DependencyContext; } /** * Dependency-manifest context accepted by `AnalyzerOptions.dependencyContext`. * cognium-dev #258. */ export interface DependencyContext { /** Java project manifests. */ java?: { /** Raw `pom.xml` content (Maven). Parsed by the gate on demand. */ pomXml?: string; /** * Raw `build.gradle` (Groovy DSL) or `build.gradle.kts` (Kotlin DSL) * content. Parsed by the gate on demand. When both `pomXml` and * `buildGradle` are supplied, the gate consults `pomXml` first and * falls back to `buildGradle` if `pomXml` does not resolve the * dependency. Added by cognium-dev #261 (Gradle-first slice). */ buildGradle?: string; /** * Raw `gradle/libs.versions.toml` content (Gradle version-catalog). * When present alongside `buildGradle`, the gate follows any * `libs.` reference in the build script through the * `[libraries]` and `[versions]` sections of the catalog to resolve * the effective declared version. Added by cognium-dev #261 * (Gradle catalog slice). */ libsVersionsToml?: string; }; /** * Python project manifests (added cognium-dev #261 Python slice). Both * are consulted by the deserialization-safety gate; `requirementsTxt` * takes precedence and `pyprojectToml` is a fallback. Currently used * for PyYAML version detection (drops `yaml.load(...)` sinks under * pyyaml ≥ 6.0 unless the call explicitly passes an unsafe `Loader=` * keyword argument). */ python?: { /** Raw `requirements.txt` content (pip). */ requirementsTxt?: string; /** Raw `pyproject.toml` content (Poetry / PEP 621). */ pyprojectToml?: string; }; /** * JavaScript / TypeScript project manifest (added cognium-dev #261 * npm slice, 3.181.0). * * **Plumbing shipped ahead of gate integration.** Currently no gate * consumes `packageJson`. Same assessment as the Rust slice: no * widely-known JS deserializer has a clean version-safety story * analogous to Fastjson `*_noneautotype` / PyYAML ≥ 6.0. * `node-serialize` is inherently unsafe regardless of version; * `serialize-javascript` variants don't ship a safe-fork * classifier. The field + `resolveDepFromPackageJson` helper are * shipped so future gate work doesn't need another round of * API-surface additions when a real JS FP-driving case appears. */ js?: { /** Raw `package.json` content (npm / pnpm / yarn). */ packageJson?: string; }; /** * Rust project manifest (added cognium-dev #261 Rust slice). * * **Plumbing shipped ahead of gate integration.** Currently no gate * consumes `cargoToml`: `serde_yaml` is deprecated (no safe successor * to gate on), `bincode` has no safe-mode feature, and no other Rust * deserialization sink in `DEFAULT_SINKS` has a clean version-safety * story. The field + `resolveDepFromCargoToml` helper are shipped so * future work can attach a gate without another round of API-surface * additions when a real Rust FP-driving case appears. */ rust?: { /** Raw `Cargo.toml` content. */ cargoToml?: string; }; } /** * Per-pass configuration options. * Each key corresponds to a pass name with pass-specific settings. */ export interface PassOptions { /** Options for NamingConventionPass (#88). */ namingConvention?: NamingConventionOptions; /** Options for DependencyFanOutPass (#72). */ dependencyFanOut?: DependencyFanOutOptions; /** Options for UnboundedCollectionPass (#31). */ unboundedCollection?: UnboundedCollectionOptions; /** Options for SecurityHeadersPass (#89). */ securityHeaders?: SecurityHeadersOptions; } /** * Initialize the analyzer. Must be called before analyze(). */ export declare function initAnalyzer(options?: AnalyzerOptions): Promise; /** * Analyze source code and produce Circle-IR output. */ export declare function analyze(code: string, filePath: string, language: SupportedLanguage, options?: AnalyzerOptions): Promise; /** * Analyze code and return a simplified API response format. */ export declare function analyzeForAPI(code: string, filePath: string, language: SupportedLanguage, options?: AnalyzerOptions): Promise; /** * Check if the analyzer is initialized. */ export declare function isAnalyzerInitialized(): boolean; /** * Reset the analyzer (mainly for testing). */ export declare function resetAnalyzer(): void; /** * Analyze a set of files as a project, finding cross-file taint flows. * * Runs single-file `analyze()` on each file in order, then uses * `ProjectGraph` + `CrossFileResolver` to surface flows that cross file * boundaries. The per-file `CircleIR` outputs are preserved unchanged in * `ProjectAnalysis.files`. * * `findings` is always empty — it requires LLM enrichment which is out of * scope for this library (see CLAUDE.md and SPEC.md section 11). */ export declare function analyzeProject(files: Array<{ code: string; filePath: string; language: SupportedLanguage; }>, options?: AnalyzerOptions): Promise; export { isFalsePositive }; //# sourceMappingURL=analyzer.d.ts.map