agent-gov-core 0.4.2 → 0.4.3
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/CHANGELOG.md +0 -0
- package/README.md +1 -1
- package/dist/finding.d.ts +16 -0
- package/dist/finding.js +10 -0
- package/dist/locators.js +47 -0
- package/package.json +1 -1
- package/schemas/finding.schema.json +4 -0
package/CHANGELOG.md
CHANGED
|
Binary file
|
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ The JSON schema at [`schemas/finding.schema.json`](./schemas/finding.schema.json
|
|
|
66
66
|
- `isSeverity(v)`, `isToolKind(v)`, `isNamespacedKind(v)` — type guards
|
|
67
67
|
- `kind(tool, name)` — build a namespaced kind without hand-assembling the dotted string
|
|
68
68
|
- `createFinding({tool, name, severity, message, ...})` — convenience constructor that calls `kind()` and `fingerprintFinding()` for you
|
|
69
|
-
- `fingerprintFinding(finding)` — 16-character hex hash of `(kind, file, line, column)`. Stable across runs and message rewordings, so a meta-reviewer can dedupe
|
|
69
|
+
- `fingerprintFinding(finding)` — 16-character hex hash of `(kind, file, line, column, salientKey?)`. Stable across runs and message rewordings, so a meta-reviewer can dedupe. Pass `salientKey` (since v0.4.3) when multiple distinct findings can fire at the same site
|
|
70
70
|
- `validateFinding(value)` — runtime check against `schemas/finding.schema.json`, returns `{ ok, errors[] }`
|
|
71
71
|
|
|
72
72
|
### Config readers
|
package/dist/finding.d.ts
CHANGED
|
@@ -26,6 +26,16 @@ export interface Finding {
|
|
|
26
26
|
location?: FindingLocation;
|
|
27
27
|
/** Stable identifier for dedupe across runs. Recommended: hash of (kind, location, salient fields). */
|
|
28
28
|
fingerprint?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Optional discriminator that participates in the fingerprint hash. Set this
|
|
31
|
+
* when a single (kind, file, line) site can legitimately host multiple distinct
|
|
32
|
+
* findings — e.g. two suspicious imports on the same line, two MCP servers in
|
|
33
|
+
* the same JSON object, two npm dependencies declared in one package.json line.
|
|
34
|
+
* Without it, the meta-reviewer would dedupe them into one. Use a stable value
|
|
35
|
+
* that doesn't drift across reruns (package name, server name, rule id) — not
|
|
36
|
+
* a timestamp or counter.
|
|
37
|
+
*/
|
|
38
|
+
salientKey?: string;
|
|
29
39
|
/** Optional structured metadata; downstream meta-reviewers may inspect it. */
|
|
30
40
|
data?: Record<string, unknown>;
|
|
31
41
|
}
|
|
@@ -57,6 +67,12 @@ export interface CreateFindingSpec {
|
|
|
57
67
|
detail?: string;
|
|
58
68
|
location?: FindingLocation;
|
|
59
69
|
data?: Record<string, unknown>;
|
|
70
|
+
/**
|
|
71
|
+
* See {@link Finding.salientKey}. Pass when the same (kind, file, line) site
|
|
72
|
+
* can produce multiple distinct findings that must not collapse to one
|
|
73
|
+
* fingerprint.
|
|
74
|
+
*/
|
|
75
|
+
salientKey?: string;
|
|
60
76
|
/** Optional explicit fingerprint. If omitted, {@link fingerprintFinding} is computed. */
|
|
61
77
|
fingerprint?: string;
|
|
62
78
|
}
|
package/dist/finding.js
CHANGED
|
@@ -62,6 +62,8 @@ export function createFinding(spec) {
|
|
|
62
62
|
finding.detail = spec.detail;
|
|
63
63
|
if (spec.location !== undefined)
|
|
64
64
|
finding.location = spec.location;
|
|
65
|
+
if (spec.salientKey !== undefined)
|
|
66
|
+
finding.salientKey = spec.salientKey;
|
|
65
67
|
if (spec.data !== undefined)
|
|
66
68
|
finding.data = spec.data;
|
|
67
69
|
finding.fingerprint = spec.fingerprint ?? fingerprintFinding(finding);
|
|
@@ -98,6 +100,10 @@ export function fingerprintFinding(finding) {
|
|
|
98
100
|
fileNormalized,
|
|
99
101
|
finding.location?.line ?? '',
|
|
100
102
|
finding.location?.column ?? '',
|
|
103
|
+
// salientKey lets multiple distinct findings at the same (kind, file, line)
|
|
104
|
+
// site keep separate fingerprints. Empty string when absent so the hash
|
|
105
|
+
// shape is stable across findings that don't need a discriminator.
|
|
106
|
+
finding.salientKey ?? '',
|
|
101
107
|
];
|
|
102
108
|
return createHash('sha256').update(parts.join('|')).digest('hex').slice(0, 16);
|
|
103
109
|
}
|
|
@@ -109,6 +115,7 @@ const FINDING_ALLOWED_KEYS = new Set([
|
|
|
109
115
|
'detail',
|
|
110
116
|
'location',
|
|
111
117
|
'fingerprint',
|
|
118
|
+
'salientKey',
|
|
112
119
|
'data',
|
|
113
120
|
]);
|
|
114
121
|
const LOCATION_ALLOWED_KEYS = new Set(['file', 'line', 'column', 'endLine', 'endColumn']);
|
|
@@ -145,6 +152,9 @@ export function validateFinding(value) {
|
|
|
145
152
|
if (v.fingerprint !== undefined && typeof v.fingerprint !== 'string') {
|
|
146
153
|
errors.push('fingerprint must be a string when present');
|
|
147
154
|
}
|
|
155
|
+
if (v.salientKey !== undefined && typeof v.salientKey !== 'string') {
|
|
156
|
+
errors.push('salientKey must be a string when present');
|
|
157
|
+
}
|
|
148
158
|
if (v.data !== undefined && (v.data === null || typeof v.data !== 'object' || Array.isArray(v.data))) {
|
|
149
159
|
errors.push('data must be an object when present');
|
|
150
160
|
}
|
package/dist/locators.js
CHANGED
|
@@ -64,9 +64,19 @@ export function lineOfTomlKey(text, dottedKey, scope) {
|
|
|
64
64
|
let inTargetTable = prefix.length === 0;
|
|
65
65
|
let currentTable = [];
|
|
66
66
|
const targetHeader = prefix.join('.');
|
|
67
|
+
// Track multi-line basic (`"""`) and literal (`'''`) string state. A leaf-key
|
|
68
|
+
// pattern can otherwise match against decoy text inside a multi-line string
|
|
69
|
+
// value — see lineOfTomlKey regression tests.
|
|
70
|
+
let inMultilineString = null;
|
|
67
71
|
for (let i = 0; i < lines.length; i++) {
|
|
68
72
|
const lineNumber = i + 1;
|
|
69
73
|
const raw = lines[i];
|
|
74
|
+
const stateAtLineStart = inMultilineString;
|
|
75
|
+
inMultilineString = updateMultilineStringState(raw, inMultilineString);
|
|
76
|
+
// If we entered this line inside a multi-line string, never match. The key
|
|
77
|
+
// pattern there is part of a string literal, not a real assignment.
|
|
78
|
+
if (stateAtLineStart !== null)
|
|
79
|
+
continue;
|
|
70
80
|
const trimmed = raw.trim();
|
|
71
81
|
const headerMatch = /^\[\[?\s*([^\]]+?)\s*\]\]?\s*(#.*)?$/.exec(trimmed);
|
|
72
82
|
if (headerMatch) {
|
|
@@ -93,6 +103,43 @@ export function lineOfTomlKey(text, dottedKey, scope) {
|
|
|
93
103
|
}
|
|
94
104
|
return 0;
|
|
95
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Walk a line and update multi-line string state. Each unescaped occurrence of
|
|
108
|
+
* `"""` toggles basic-multiline; each `'''` toggles literal-multiline; the
|
|
109
|
+
* other delimiter is inert while we're inside the first. Returns the state at
|
|
110
|
+
* end-of-line so the next iteration knows whether it's inside a string.
|
|
111
|
+
*/
|
|
112
|
+
function updateMultilineStringState(line, current) {
|
|
113
|
+
let state = current;
|
|
114
|
+
let pos = 0;
|
|
115
|
+
while (pos <= line.length - 3) {
|
|
116
|
+
const window = line.substr(pos, 3);
|
|
117
|
+
if (state === null) {
|
|
118
|
+
if (window === '"""') {
|
|
119
|
+
state = '"""';
|
|
120
|
+
pos += 3;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (window === "'''") {
|
|
124
|
+
state = "'''";
|
|
125
|
+
pos += 3;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else if (state === '"""' && window === '"""') {
|
|
130
|
+
state = null;
|
|
131
|
+
pos += 3;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
else if (state === "'''" && window === "'''") {
|
|
135
|
+
state = null;
|
|
136
|
+
pos += 3;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
pos++;
|
|
140
|
+
}
|
|
141
|
+
return state;
|
|
142
|
+
}
|
|
96
143
|
function scopeLineFilter(text, scope) {
|
|
97
144
|
if (!scope)
|
|
98
145
|
return () => true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-gov-core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Shared primitives for the AI-agent governance suite: Finding schema, JSONC/TOML readers, line locators, MCP command normalization, shell tokenization, and GitHub Action helpers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"fingerprint": { "type": "string" },
|
|
44
|
+
"salientKey": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "Optional discriminator that participates in the fingerprint hash. Set when a single (kind, file, line) site can produce multiple distinct findings (e.g. two suspicious imports on one line). Use a stable value — package name, server name, rule id — not a timestamp."
|
|
47
|
+
},
|
|
44
48
|
"data": { "type": "object" }
|
|
45
49
|
}
|
|
46
50
|
}
|