gitnexus 1.6.8-rc.20 → 1.6.8-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/ai-context.js +1 -0
- package/dist/cli/skill-gen.js +1 -0
- package/dist/core/ingestion/cfg/emit.d.ts +16 -1
- package/dist/core/ingestion/cfg/emit.js +10 -3
- package/dist/core/ingestion/cfg/reaching-defs.d.ts +7 -0
- package/dist/core/ingestion/cfg/reaching-defs.js +9 -0
- package/dist/core/ingestion/cfg/types.d.ts +91 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +38 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +419 -3
- package/dist/core/ingestion/pipeline.d.ts +16 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +8 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +119 -3
- package/dist/core/ingestion/taint/emit.d.ts +124 -0
- package/dist/core/ingestion/taint/emit.js +204 -0
- package/dist/core/ingestion/taint/match.d.ts +153 -0
- package/dist/core/ingestion/taint/match.js +278 -0
- package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
- package/dist/core/ingestion/taint/path-codec.js +190 -0
- package/dist/core/ingestion/taint/propagate.d.ts +216 -0
- package/dist/core/ingestion/taint/propagate.js +664 -0
- package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
- package/dist/core/ingestion/taint/site-safety.js +98 -0
- package/dist/core/ingestion/taint/source-sink-config.d.ts +94 -23
- package/dist/core/ingestion/taint/source-sink-config.js +11 -11
- package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
- package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
- package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
- package/dist/core/ingestion/taint/typescript-model.js +102 -0
- package/dist/core/run-analyze.d.ts +8 -1
- package/dist/core/run-analyze.js +14 -0
- package/dist/mcp/local/local-backend.d.ts +29 -0
- package/dist/mcp/local/local-backend.js +241 -1
- package/dist/mcp/resources.js +1 -0
- package/dist/mcp/tools.d.ts +9 -0
- package/dist/mcp/tools.js +53 -0
- package/dist/storage/parse-cache.js +10 -1
- package/dist/storage/repo-manager.d.ts +18 -0
- package/package.json +1 -1
- package/skills/gitnexus-guide.md +11 -0
package/dist/cli/ai-context.js
CHANGED
|
@@ -136,6 +136,7 @@ This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${s
|
|
|
136
136
|
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
|
|
137
137
|
- When exploring unfamiliar code, use \`query({query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
|
|
138
138
|
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use \`context({name: "symbolName"})\`.
|
|
139
|
+
- For security review, \`explain({target: "fileOrSymbol"})\` lists taint findings (source→sink flows; needs \`analyze --pdg\`).
|
|
139
140
|
|
|
140
141
|
## Never Do
|
|
141
142
|
|
package/dist/cli/skill-gen.js
CHANGED
|
@@ -485,6 +485,7 @@ const renderSkillMarkdown = (community, projectName, members, files, entryPoints
|
|
|
485
485
|
lines.push(`1. \`context({name: "${firstEntry}"})\` \u2014 see callers and callees`);
|
|
486
486
|
lines.push(`2. \`query({query: "${community.label.toLowerCase()}"})\` \u2014 find related execution flows`);
|
|
487
487
|
lines.push('3. Read key files listed above for implementation details');
|
|
488
|
+
lines.push('4. `explain({target: "<file or symbol>"})` — persisted taint findings (source→sink data flows), when indexed with `--pdg`');
|
|
488
489
|
lines.push('');
|
|
489
490
|
return lines.join('\n');
|
|
490
491
|
};
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* kind cannot be its own edge type and is queried via `reason`.
|
|
20
20
|
*/
|
|
21
21
|
import type { KnowledgeGraph } from '../../graph/types.js';
|
|
22
|
-
import type { FunctionCfg } from './types.js';
|
|
22
|
+
import type { BindingEntry, FunctionCfg } from './types.js';
|
|
23
23
|
/**
|
|
24
24
|
* Default per-function CFG edge cap. A pathological generated function could
|
|
25
25
|
* otherwise emit an unbounded edge set; the cap bounds graph growth and is
|
|
@@ -56,6 +56,13 @@ export interface CfgEmitResult {
|
|
|
56
56
|
/** Number of functions that hit the cap. */
|
|
57
57
|
cappedFunctions: number;
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* The single BasicBlock id template (module doc). Exported for the M3 taint
|
|
61
|
+
* emit path (taint/emit.ts), whose TAINTED/SANITIZES edges must address the
|
|
62
|
+
* SAME persisted block nodes — a re-derived copy of this template would
|
|
63
|
+
* silently dangle the moment either drifted.
|
|
64
|
+
*/
|
|
65
|
+
export declare const basicBlockId: (filePath: string, functionStartLine: number, functionStartColumn: number, blockIndex: number) => string;
|
|
59
66
|
/**
|
|
60
67
|
* Whether an untrusted `cfgSideChannel` element is safe to feed to
|
|
61
68
|
* {@link emitFileCfgs}. Deliberately NOT full FunctionCfg validation — it
|
|
@@ -106,6 +113,14 @@ export interface ReachingDefEmitResult {
|
|
|
106
113
|
/** Total statement-level facts the solver produced (pre-dedup telemetry). */
|
|
107
114
|
facts: number;
|
|
108
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Stable identity for a binding inside edge ids (#2082 M2 KTD3/KTD9):
|
|
118
|
+
* `name:declLine:declCol` for declared bindings, `name@module` for synthetic
|
|
119
|
+
* ones. Distinct same-name bindings never share a key; identifier characters
|
|
120
|
+
* cannot contain the id separators. Exported for the M3 taint emit path —
|
|
121
|
+
* TAINTED/SANITIZES ids key bindings with the same discipline.
|
|
122
|
+
*/
|
|
123
|
+
export declare const bindingKey: (b: BindingEntry) => string;
|
|
109
124
|
/**
|
|
110
125
|
* Compute reaching definitions per function and persist the bounded
|
|
111
126
|
* REACHING_DEF projection (#2082 M2 U4).
|
|
@@ -28,7 +28,13 @@ export const DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION = 4000;
|
|
|
28
28
|
export const REACHING_DEF_FACTS_PER_EDGE_CAP = 4;
|
|
29
29
|
/** Derived emit-path fact limit at the default edge cap (bench/doc anchor). */
|
|
30
30
|
export const DEFAULT_PDG_MAX_REACHING_DEF_FACTS_PER_FUNCTION = REACHING_DEF_FACTS_PER_EDGE_CAP * DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION;
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The single BasicBlock id template (module doc). Exported for the M3 taint
|
|
33
|
+
* emit path (taint/emit.ts), whose TAINTED/SANITIZES edges must address the
|
|
34
|
+
* SAME persisted block nodes — a re-derived copy of this template would
|
|
35
|
+
* silently dangle the moment either drifted.
|
|
36
|
+
*/
|
|
37
|
+
export const basicBlockId = (filePath, functionStartLine, functionStartColumn, blockIndex) => `BasicBlock:${filePath}:${functionStartLine}:${functionStartColumn}:${blockIndex}`;
|
|
32
38
|
/**
|
|
33
39
|
* Whether an untrusted `cfgSideChannel` element is safe to feed to
|
|
34
40
|
* {@link emitFileCfgs}. Deliberately NOT full FunctionCfg validation — it
|
|
@@ -185,9 +191,10 @@ export function emitFileCfgs(graph, cfgs, maxEdgesPerFunction = DEFAULT_MAX_CFG_
|
|
|
185
191
|
* Stable identity for a binding inside edge ids (#2082 M2 KTD3/KTD9):
|
|
186
192
|
* `name:declLine:declCol` for declared bindings, `name@module` for synthetic
|
|
187
193
|
* ones. Distinct same-name bindings never share a key; identifier characters
|
|
188
|
-
* cannot contain the id separators.
|
|
194
|
+
* cannot contain the id separators. Exported for the M3 taint emit path —
|
|
195
|
+
* TAINTED/SANITIZES ids key bindings with the same discipline.
|
|
189
196
|
*/
|
|
190
|
-
const bindingKey = (b) => b.synthetic ? `${b.name}@module` : `${b.name}:${b.declLine}:${b.declColumn}`;
|
|
197
|
+
export const bindingKey = (b) => b.synthetic ? `${b.name}@module` : `${b.name}:${b.declLine}:${b.declColumn}`;
|
|
191
198
|
/**
|
|
192
199
|
* Compute reaching definitions per function and persist the bounded
|
|
193
200
|
* REACHING_DEF projection (#2082 M2 U4).
|
|
@@ -41,6 +41,13 @@ export interface ProgramPoint {
|
|
|
41
41
|
readonly stmtIndex: number;
|
|
42
42
|
readonly line: number;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Canonical `block:stmt` string key for a program point. Colon-separated to
|
|
46
|
+
* match the codebase's `blockIndex:stmtIndex` id conventions. Shared by the
|
|
47
|
+
* taint propagation engine (dedup/state keys) and the taint emit path
|
|
48
|
+
* (persisted edge-id material) so the two never drift.
|
|
49
|
+
*/
|
|
50
|
+
export declare function pointKey(p: ProgramPoint): string;
|
|
44
51
|
/** One def→use fact: the definition at `def` reaches the use at `use`. */
|
|
45
52
|
export interface DefUseFact {
|
|
46
53
|
/** Index into {@link FunctionDefUse.bindings}. */
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical `block:stmt` string key for a program point. Colon-separated to
|
|
3
|
+
* match the codebase's `blockIndex:stmtIndex` id conventions. Shared by the
|
|
4
|
+
* taint propagation engine (dedup/state keys) and the taint emit path
|
|
5
|
+
* (persisted edge-id material) so the two never drift.
|
|
6
|
+
*/
|
|
7
|
+
export function pointKey(p) {
|
|
8
|
+
return `${p.blockIndex}:${p.stmtIndex}`;
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* def-site key: packs (blockIndex, stmtIndex) into one number. The stride is
|
|
3
12
|
* a per-BLOCK statement bound, and `maxFunctionLines` caps LINES, not
|
|
@@ -40,6 +40,90 @@ export interface BindingEntry {
|
|
|
40
40
|
*/
|
|
41
41
|
readonly synthetic?: boolean;
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* One occurrence of a binding inside a call/new site's argument position
|
|
45
|
+
* (#2083 M3 U1). A bare `number` is a DIRECT occurrence (binding index into
|
|
46
|
+
* {@link FunctionCfg.bindings}); a `[bindingIdx, viaSiteIdx]` tuple marks an
|
|
47
|
+
* occurrence that reaches this argument THROUGH the nested site at
|
|
48
|
+
* `viaSiteIdx` (an index into the SAME statement's {@link StatementFacts.sites}
|
|
49
|
+
* array). The tag is load-bearing for sanitizer interposition (plan KTD4a):
|
|
50
|
+
* a flat per-arg binding set cannot distinguish `exec(escape(x))` (kill) from
|
|
51
|
+
* `exec(x)` (finding) — the single most common safe pattern would
|
|
52
|
+
* false-positive without it.
|
|
53
|
+
*/
|
|
54
|
+
export type SiteArgOccurrence = number | readonly [number, number];
|
|
55
|
+
/**
|
|
56
|
+
* One call site, constructor call, or value-position member read harvested
|
|
57
|
+
* from a statement (#2083 M3 U1, plan KTD2). Worker-side substrate for the M3
|
|
58
|
+
* taint pass: the M2 facts carry no expression structure, and the main thread
|
|
59
|
+
* cannot re-parse (the #1983 OOM shape). Spec-AGNOSTIC — records structure
|
|
60
|
+
* only, never source/sink/sanitizer-ness (matching is a main-thread concern).
|
|
61
|
+
*
|
|
62
|
+
* Integer indices: binding fields (`receiver`/`object`/`resultDefs`/arg
|
|
63
|
+
* occurrences) index {@link FunctionCfg.bindings}; site references (`parent`,
|
|
64
|
+
* via-tags) index the OWNING statement's `sites` array. JSON-plain; NO field
|
|
65
|
+
* here may be named `nodeId` (durable parsedfile-store reviver hazard — see
|
|
66
|
+
* {@link BindingEntry}).
|
|
67
|
+
*/
|
|
68
|
+
export interface SiteRecord {
|
|
69
|
+
readonly kind: 'call' | 'new' | 'member-read';
|
|
70
|
+
/**
|
|
71
|
+
* Dotted callee path for call/new sites whose callee chain is rooted at an
|
|
72
|
+
* identifier/`this`/`super` (`child_process.exec`, `req.body.toString`).
|
|
73
|
+
* Optional chaining is normalized (`a?.b()` ⇒ `a.b`); string-literal
|
|
74
|
+
* subscripts fold into the path (`cp["exec"]` ⇒ `cp.exec`). Absent when the
|
|
75
|
+
* chain is not statically resolvable (dynamic key, call-rooted chain).
|
|
76
|
+
*/
|
|
77
|
+
readonly callee?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Binding index of the callee chain's ROOT identifier when the callee is a
|
|
80
|
+
* member chain (`userInput.trim()` ⇒ `userInput`). Method calls launder
|
|
81
|
+
* taint without it (plan KTD5 receiver-position TITO). Absent for bare
|
|
82
|
+
* calls (`exec(x)`) and non-identifier roots.
|
|
83
|
+
*/
|
|
84
|
+
readonly receiver?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Per-argument-position occurrence entries (trailing empty positions are
|
|
87
|
+
* trimmed; absent when no argument carries a binding occurrence). For
|
|
88
|
+
* `template: true` sites every substitution occurrence aggregates at
|
|
89
|
+
* position 0 (tagged templates have no positional argument list).
|
|
90
|
+
*/
|
|
91
|
+
readonly args?: ReadonlyArray<readonly SiteArgOccurrence[]>;
|
|
92
|
+
/**
|
|
93
|
+
* Bindings defined by a declarator/assignment whose ENTIRE value (after
|
|
94
|
+
* unwrapping parens/`await`/`as`/`!`) is this call — `const b = escape(t)`
|
|
95
|
+
* ⇒ `[b]`. Per-declarator: `const a = t, b = escape(t)` attaches `[b]`
|
|
96
|
+
* only. Kill placement (KTD4b) keys on this: a sanitizer kills exactly the
|
|
97
|
+
* defs that receive its result directly.
|
|
98
|
+
*/
|
|
99
|
+
readonly resultDefs?: readonly number[];
|
|
100
|
+
/**
|
|
101
|
+
* `[siteIdx, argIdx]` of the innermost enclosing call/new site argument
|
|
102
|
+
* position this site occurs in (`exec(escape(x))` ⇒ escape's parent is
|
|
103
|
+
* `[execSiteIdx, 0]`). Absent for top-level sites.
|
|
104
|
+
*/
|
|
105
|
+
readonly parent?: readonly [number, number];
|
|
106
|
+
/**
|
|
107
|
+
* Index of the FIRST spread argument (`exec(...args)` ⇒ 0). Presence means
|
|
108
|
+
* position matching must degrade soundly (any sink position ≥ this index —
|
|
109
|
+
* plan KTD2/U2). A number (not boolean) because the matcher needs the index.
|
|
110
|
+
*/
|
|
111
|
+
readonly spread?: number;
|
|
112
|
+
/** Tagged-template call (`sql\`…${id}\``) — argument positions are not positional. */
|
|
113
|
+
readonly template?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* String-literal first argument when the callee is bare `require` —
|
|
116
|
+
* CommonJS aliases resolve like ESM imports on the main thread (KTD7).
|
|
117
|
+
*/
|
|
118
|
+
readonly requireArg?: string;
|
|
119
|
+
/** Member read: binding index of the object root (`req.body` ⇒ `req`). */
|
|
120
|
+
readonly object?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Member read: property name (`req.body` ⇒ `'body'`; `req["body"]`
|
|
123
|
+
* included; dynamic `req[key]` is never recorded — documented KTD10 FN).
|
|
124
|
+
*/
|
|
125
|
+
readonly property?: string;
|
|
126
|
+
}
|
|
43
127
|
/**
|
|
44
128
|
* Def/use facts for one harvested statement (or construct header), in
|
|
45
129
|
* execution order within its block (#2082 M2 U1). `defs`/`uses` are indices
|
|
@@ -55,12 +139,19 @@ export interface BindingEntry {
|
|
|
55
139
|
* treating them as must-defs would falsely kill the prior def on the
|
|
56
140
|
* not-taken path (a taint false negative on core JS idioms). Optional —
|
|
57
141
|
* absent means none.
|
|
142
|
+
*
|
|
143
|
+
* `sites` (#2083 M3 U1): call/member-read structure for the taint pass —
|
|
144
|
+
* see {@link SiteRecord}. Optional and omit-when-empty; absent on pre-M3
|
|
145
|
+
* channels and on statements with no calls or member reads. Sites inside
|
|
146
|
+
* nested functions are NOT recorded (consistent with def/use invisibility —
|
|
147
|
+
* the enclosing `arr.forEach(...)` call IS, with receiver `arr`).
|
|
58
148
|
*/
|
|
59
149
|
export interface StatementFacts {
|
|
60
150
|
readonly line: number;
|
|
61
151
|
readonly defs: readonly number[];
|
|
62
152
|
readonly uses: readonly number[];
|
|
63
153
|
readonly mayDefs?: readonly number[];
|
|
154
|
+
readonly sites?: readonly SiteRecord[];
|
|
64
155
|
}
|
|
65
156
|
/** A basic block: a maximal straight-line run of statements between leaders. */
|
|
66
157
|
export interface BasicBlockData {
|
|
@@ -64,6 +64,16 @@ export declare class TsHarvester {
|
|
|
64
64
|
* here falsely kills the prior def on the not-taken path).
|
|
65
65
|
*/
|
|
66
66
|
private conditionalDepth;
|
|
67
|
+
/**
|
|
68
|
+
* Call/new node id → bindings whose declarator/assignment VALUE is exactly
|
|
69
|
+
* that call (#2083 M3 U1). Registered by the declarator/assignment handlers
|
|
70
|
+
* BEFORE the value walk, consumed by {@link visitCall} when it reaches the
|
|
71
|
+
* node — the indirection keeps result-def attribution per-declarator
|
|
72
|
+
* (`const a = t, b = escape(t)` attaches `[b]` to the escape site only) and
|
|
73
|
+
* top-level-only (`const c = cond ? escape(b) : b` attaches nothing — the
|
|
74
|
+
* bypass occurrence must keep `c` taintable, plan KTD4a).
|
|
75
|
+
*/
|
|
76
|
+
private readonly resultDefTargets;
|
|
67
77
|
constructor(fnNode: SyntaxNode);
|
|
68
78
|
/** The completed binding table — pass to `CfgBuilder.finish`. */
|
|
69
79
|
table(): readonly BindingEntry[];
|
|
@@ -103,4 +113,32 @@ export declare class TsHarvester {
|
|
|
103
113
|
private walkValue;
|
|
104
114
|
/** Assignment-target walk: identifiers bind; member/subscript targets are uses. */
|
|
105
115
|
private walkDefPattern;
|
|
116
|
+
/** Strip value-transparent wrappers (`(x)`, `x!`, `x as T`, `await x`). */
|
|
117
|
+
private unwrapValueWrappers;
|
|
118
|
+
/**
|
|
119
|
+
* When `value`'s root (after unwrapping) is a call/new node, remember that
|
|
120
|
+
* its site should carry `resultDefs: defs` — consumed by {@link visitCall}
|
|
121
|
+
* once the value walk reaches the node.
|
|
122
|
+
*/
|
|
123
|
+
private registerResultDefs;
|
|
124
|
+
/**
|
|
125
|
+
* Explicit call/new handler: records a call site (callee path, receiver,
|
|
126
|
+
* per-arg occurrence entries, spread/template markers, require literal,
|
|
127
|
+
* result defs) while reproducing EXACTLY the uses the old default descent
|
|
128
|
+
* recorded — callee chain root + dynamic subscript indices + arguments.
|
|
129
|
+
*/
|
|
130
|
+
private visitCall;
|
|
131
|
+
/**
|
|
132
|
+
* Member/subscript chain walk shared by value position, write position, and
|
|
133
|
+
* callee position. Use-recording is identical to the old default descent
|
|
134
|
+
* (chain-root identifier once, dynamic subscript index expressions, full
|
|
135
|
+
* walk of non-identifier roots) — NO double-recording. Member-read sites:
|
|
136
|
+
* at most ONE per chain — the INNERMOST access — and only when the chain
|
|
137
|
+
* root is an identifier and the access's key is static (`.prop` or a
|
|
138
|
+
* string-literal subscript); `skipFinalRead` suppresses it when that access
|
|
139
|
+
* is the final one (callee / write target). Optional chaining (`?.`) never
|
|
140
|
+
* appears in the output (field-based traversal normalizes it); dynamic
|
|
141
|
+
* computed keys record nothing (documented KTD10 FN).
|
|
142
|
+
*/
|
|
143
|
+
private walkChain;
|
|
106
144
|
}
|