agent-gov-core 0.3.1
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/LICENSE +21 -0
- package/README.md +92 -0
- package/dist/action.d.ts +31 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +79 -0
- package/dist/action.js.map +1 -0
- package/dist/finding.d.ts +115 -0
- package/dist/finding.d.ts.map +1 -0
- package/dist/finding.js +177 -0
- package/dist/finding.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonc.d.ts +20 -0
- package/dist/jsonc.d.ts.map +1 -0
- package/dist/jsonc.js +117 -0
- package/dist/jsonc.js.map +1 -0
- package/dist/locators.d.ts +29 -0
- package/dist/locators.d.ts.map +1 -0
- package/dist/locators.js +127 -0
- package/dist/locators.js.map +1 -0
- package/dist/mcp.d.ts +32 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +120 -0
- package/dist/mcp.js.map +1 -0
- package/dist/shell.d.ts +33 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +200 -0
- package/dist/shell.js.map +1 -0
- package/dist/test-utils.d.ts +65 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +90 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/toml.d.ts +28 -0
- package/dist/toml.d.ts.map +1 -0
- package/dist/toml.js +0 -0
- package/dist/toml.js.map +1 -0
- package/package.json +51 -0
- package/schemas/finding.schema.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Conal Hg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# agent-gov-core
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/agent-gov-core)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
Shared primitives for the AI-agent governance suite — a small library that ScopeTrail, PolicyMesh, CapabilityEcho, TaskBound, and SessionTrail all consume so common parsers, locators, and the `Finding` schema live in one place instead of five.
|
|
7
|
+
|
|
8
|
+
Zero runtime dependencies. ESM, TypeScript, target ES2022.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install agent-gov-core
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## The canonical Finding
|
|
17
|
+
|
|
18
|
+
Every tool in the suite emits findings against the same schema. The `kind` field is a namespaced string `<tool>.<slug>` so a downstream meta-reviewer can dedupe across tools.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { kind, type Finding } from 'agent-gov-core';
|
|
22
|
+
|
|
23
|
+
const finding: Finding = {
|
|
24
|
+
tool: 'scope_trail',
|
|
25
|
+
kind: kind('scope_trail', 'permission_allow_widened'),
|
|
26
|
+
severity: 'high',
|
|
27
|
+
message: 'Claude permission allowlist now includes Bash(npm *).',
|
|
28
|
+
location: { file: '.claude/settings.json', line: 12 },
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The JSON schema at [`schemas/finding.schema.json`](./schemas/finding.schema.json) enforces the dotted-kind shape — any tool emitting unprefixed kinds will fail validation.
|
|
33
|
+
|
|
34
|
+
## What's in the library
|
|
35
|
+
|
|
36
|
+
### Finding schema and helpers
|
|
37
|
+
- `Finding`, `Severity`, `ToolKind`, `FindingLocation` — canonical types
|
|
38
|
+
- `SEVERITIES`, `TOOL_KINDS` — runtime arrays of the enum values
|
|
39
|
+
- `isSeverity(v)`, `isToolKind(v)`, `isNamespacedKind(v)` — type guards
|
|
40
|
+
- `kind(tool, name)` — build a namespaced kind without hand-assembling the dotted string
|
|
41
|
+
- `createFinding({tool, name, severity, message, ...})` — convenience constructor that calls `kind()` and `fingerprintFinding()` for you
|
|
42
|
+
- `fingerprintFinding(finding)` — 16-character hex hash of `(kind, file, line, column)`. Stable across runs and message rewordings, so a meta-reviewer can dedupe
|
|
43
|
+
- `validateFinding(value)` — runtime check against `schemas/finding.schema.json`, returns `{ ok, errors[] }`
|
|
44
|
+
|
|
45
|
+
### Config readers
|
|
46
|
+
- `readJsonObjectWithSource(path)` — JSONC reader, string-aware comment + trailing-comma stripping, position-preserving
|
|
47
|
+
- `stripJsonComments(text)` — same logic exposed for in-memory text
|
|
48
|
+
- `readTomlObject(path)` — TOML reader (sections, arrays of tables, inline tables, multi-line strings, dotted/quoted keys)
|
|
49
|
+
- `parseToml(text)` — same exposed for text
|
|
50
|
+
|
|
51
|
+
### Line locators
|
|
52
|
+
- `lineOfJsonKey(text, key)` — 1-based line of `"key":`
|
|
53
|
+
- `lineOfJsonStringValue(text, value, scope?)` — 1-based line of a JSON-encoded value, optionally scoped to a byte range
|
|
54
|
+
- `lineOfTomlKey(text, dottedKey)` — 1-based line of a TOML key
|
|
55
|
+
|
|
56
|
+
### MCP command normalization
|
|
57
|
+
- `normalizeMcpCommand({ command, args, url, serverUrl, env, cwd })` — canonical identity string for an MCP server entry. Drops neutral flags (`-y`, `--yes`), resolves npx/uvx invocations, includes env+cwd in the identity. Used to dedupe `mcp_command_mismatch` false positives when servers are equivalent but syntactically different (`npx -y foo@1.2.3` vs `npx foo@1.2.3`).
|
|
58
|
+
|
|
59
|
+
### Shell tokenization
|
|
60
|
+
- `tokenizeShell(command)` — quote-aware split on `;`, `|`, `&&`, `||` plus trivial obfuscation neutralization (`c""url` → `curl`, `c\\url` → `curl`)
|
|
61
|
+
- `getCommandHead(subcommand)` — extract the leading verb after tokenization
|
|
62
|
+
|
|
63
|
+
### GitHub Action helpers
|
|
64
|
+
- `rankSeverity(s)` — numeric rank `none=0 … critical=4`
|
|
65
|
+
- `passesSeverityThreshold(s, threshold)`, `anyAtOrAbove(findings, threshold)` — fail-on plumbing
|
|
66
|
+
- `emitFindingAnnotation(f)` — render a Finding as a `::warning file=…,line=…,title=…::…` GitHub workflow annotation
|
|
67
|
+
|
|
68
|
+
### Test fixtures (`agent-gov-core/test-utils`)
|
|
69
|
+
Secondary entry point used by consumer test suites. Zero overhead in production — only loaded when test files import it.
|
|
70
|
+
|
|
71
|
+
- `writeFiles(dir, { relPath: content })` — write a map of files under `dir`, creating parent directories
|
|
72
|
+
- `makeGitRepo({ initialFiles?, initialMessage? })` → `{ repo, commit, head, git, cleanup }` — temp repo on branch `main` with placeholder identity; `commit()` writes files and commits, returning the new SHA
|
|
73
|
+
- `makeOldNewFixture({ old, new })` → `{ old, new, cleanup }` — two sibling temp directories for diff-mode CLI tests
|
|
74
|
+
|
|
75
|
+
## Principles
|
|
76
|
+
|
|
77
|
+
- **Zero runtime dependencies.** Real TOML, JSONC, MCP normalization, shell tokenization — all hand-written or vendored, no transitive supply chain.
|
|
78
|
+
- **MIT.** No telemetry. No network calls anywhere in the library.
|
|
79
|
+
- **Semver, with the contract frozen at v1.0.** Until then, minor versions may include breaking changes (the v0.2 schema regex tightening is one example).
|
|
80
|
+
- **Per-tool reasoning stays in each tool.** This library is the substrate, not the orchestrator.
|
|
81
|
+
|
|
82
|
+
## Used by
|
|
83
|
+
|
|
84
|
+
- [ScopeTrail](https://github.com/Conalh/ScopeTrail) — agent permission drift in PRs (`scope_trail.*` findings)
|
|
85
|
+
- [PolicyMesh](https://github.com/Conalh/PolicyMesh) — cross-surface agent policy contradictions (`policy_mesh.*`)
|
|
86
|
+
- [CapabilityEcho](https://github.com/Conalh/CapabilityEcho) — capability drift through code, not config (`capability_echo.*`)
|
|
87
|
+
- [TaskBound](https://github.com/Conalh/TaskBound) — scope creep after the agent runs (`task_bound.*`)
|
|
88
|
+
- [SessionTrail](https://github.com/Conalh/SessionTrail) — runtime behavior across agent session transcripts (`session_trail.*`)
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT.
|
package/dist/action.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Finding, Severity } from './finding.js';
|
|
2
|
+
/** Numeric rank: low=1, medium=2, high=3, critical=4. */
|
|
3
|
+
export declare function rankSeverity(severity: Severity): number;
|
|
4
|
+
/**
|
|
5
|
+
* `true` when `severity` is at least as severe as `threshold` — i.e. the run
|
|
6
|
+
* should FAIL. Returns `false` (= pass) if `severity` is below threshold.
|
|
7
|
+
*/
|
|
8
|
+
export declare function passesSeverityThreshold(severity: Severity, threshold: Severity): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Returns true if any finding meets or exceeds the threshold.
|
|
11
|
+
*/
|
|
12
|
+
export declare function anyAtOrAbove(findings: readonly Finding[], threshold: Severity): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Build a GitHub Actions workflow command line for a finding.
|
|
15
|
+
*
|
|
16
|
+
* `critical` and `high` map to `::error`; everything else maps to `::warning`.
|
|
17
|
+
* `notice` is intentionally not used — surfacing low findings as `warning` makes
|
|
18
|
+
* them visible in the Files Changed tab.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* emitFindingAnnotation({
|
|
22
|
+
* tool: 'capability_echo',
|
|
23
|
+
* kind: 'capability_echo.workflow_permission_write',
|
|
24
|
+
* severity: 'high',
|
|
25
|
+
* message: 'Workflow grants contents: write to PR-triggered jobs.',
|
|
26
|
+
* location: { file: '.github/workflows/ci.yml', line: 12 },
|
|
27
|
+
* });
|
|
28
|
+
* // → '::error file=.github/workflows/ci.yml,line=12,title=[capability_echo.workflow_permission_write] high::Workflow grants contents: write to PR-triggered jobs.'
|
|
29
|
+
*/
|
|
30
|
+
export declare function emitFindingAnnotation(finding: Finding): string;
|
|
31
|
+
//# sourceMappingURL=action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAStD,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,GAAG,OAAO,CAExF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,GAAG,OAAO,CAKvF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAe9D"}
|
package/dist/action.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const SEVERITY_RANK = {
|
|
2
|
+
low: 1,
|
|
3
|
+
medium: 2,
|
|
4
|
+
high: 3,
|
|
5
|
+
critical: 4,
|
|
6
|
+
};
|
|
7
|
+
/** Numeric rank: low=1, medium=2, high=3, critical=4. */
|
|
8
|
+
export function rankSeverity(severity) {
|
|
9
|
+
return SEVERITY_RANK[severity];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `true` when `severity` is at least as severe as `threshold` — i.e. the run
|
|
13
|
+
* should FAIL. Returns `false` (= pass) if `severity` is below threshold.
|
|
14
|
+
*/
|
|
15
|
+
export function passesSeverityThreshold(severity, threshold) {
|
|
16
|
+
return rankSeverity(severity) >= rankSeverity(threshold);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Returns true if any finding meets or exceeds the threshold.
|
|
20
|
+
*/
|
|
21
|
+
export function anyAtOrAbove(findings, threshold) {
|
|
22
|
+
for (const f of findings) {
|
|
23
|
+
if (passesSeverityThreshold(f.severity, threshold))
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build a GitHub Actions workflow command line for a finding.
|
|
30
|
+
*
|
|
31
|
+
* `critical` and `high` map to `::error`; everything else maps to `::warning`.
|
|
32
|
+
* `notice` is intentionally not used — surfacing low findings as `warning` makes
|
|
33
|
+
* them visible in the Files Changed tab.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* emitFindingAnnotation({
|
|
37
|
+
* tool: 'capability_echo',
|
|
38
|
+
* kind: 'capability_echo.workflow_permission_write',
|
|
39
|
+
* severity: 'high',
|
|
40
|
+
* message: 'Workflow grants contents: write to PR-triggered jobs.',
|
|
41
|
+
* location: { file: '.github/workflows/ci.yml', line: 12 },
|
|
42
|
+
* });
|
|
43
|
+
* // → '::error file=.github/workflows/ci.yml,line=12,title=[capability_echo.workflow_permission_write] high::Workflow grants contents: write to PR-triggered jobs.'
|
|
44
|
+
*/
|
|
45
|
+
export function emitFindingAnnotation(finding) {
|
|
46
|
+
const level = finding.severity === 'critical' || finding.severity === 'high'
|
|
47
|
+
? 'error'
|
|
48
|
+
: 'warning';
|
|
49
|
+
const params = [];
|
|
50
|
+
if (finding.location?.file)
|
|
51
|
+
params.push(`file=${escapeProperty(finding.location.file)}`);
|
|
52
|
+
if (finding.location?.line != null)
|
|
53
|
+
params.push(`line=${finding.location.line}`);
|
|
54
|
+
if (finding.location?.column != null)
|
|
55
|
+
params.push(`col=${finding.location.column}`);
|
|
56
|
+
if (finding.location?.endLine != null)
|
|
57
|
+
params.push(`endLine=${finding.location.endLine}`);
|
|
58
|
+
if (finding.location?.endColumn != null)
|
|
59
|
+
params.push(`endColumn=${finding.location.endColumn}`);
|
|
60
|
+
params.push(`title=${escapeProperty(`[${finding.kind}] ${finding.severity}`)}`);
|
|
61
|
+
const message = escapeData(finding.message);
|
|
62
|
+
return `::${level} ${params.join(',')}::${message}`;
|
|
63
|
+
}
|
|
64
|
+
// per GitHub Actions docs
|
|
65
|
+
function escapeData(s) {
|
|
66
|
+
return s
|
|
67
|
+
.replace(/%/g, '%25')
|
|
68
|
+
.replace(/\r/g, '%0D')
|
|
69
|
+
.replace(/\n/g, '%0A');
|
|
70
|
+
}
|
|
71
|
+
function escapeProperty(s) {
|
|
72
|
+
return s
|
|
73
|
+
.replace(/%/g, '%25')
|
|
74
|
+
.replace(/\r/g, '%0D')
|
|
75
|
+
.replace(/\n/g, '%0A')
|
|
76
|
+
.replace(/:/g, '%3A')
|
|
77
|
+
.replace(/,/g, '%2C');
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.js","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAEA,MAAM,aAAa,GAA6B;IAC9C,GAAG,EAAE,CAAC;IACN,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;IACP,QAAQ,EAAE,CAAC;CACZ,CAAC;AAEF,yDAAyD;AACzD,MAAM,UAAU,YAAY,CAAC,QAAkB;IAC7C,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAkB,EAAE,SAAmB;IAC7E,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAC,SAAS,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAA4B,EAAE,SAAmB;IAC5E,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,uBAAuB,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,UAAU,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM;QAC1E,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO,CAAC,QAAQ,EAAE,IAAI;QAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzF,IAAI,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAI,IAAI;QAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjF,IAAI,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,IAAI;QAAE,MAAM,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACpF,IAAI,OAAO,CAAC,QAAQ,EAAE,OAAO,IAAI,IAAI;QAAE,MAAM,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1F,IAAI,OAAO,CAAC,QAAQ,EAAE,SAAS,IAAI,IAAI;QAAE,MAAM,CAAC,IAAI,CAAC,aAAa,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IAChG,MAAM,CAAC,IAAI,CAAC,SAAS,cAAc,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;IAEhF,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,KAAK,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC;AACtD,CAAC;AAED,0BAA0B;AAC1B,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,cAAc,CAAC,CAAS;IAC/B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export type Severity = 'low' | 'medium' | 'high' | 'critical';
|
|
2
|
+
/**
|
|
3
|
+
* Closed set of tool identifiers in the AI-agent governance suite. Adding a new
|
|
4
|
+
* tool requires updating this union, {@link TOOL_KINDS}, the `tool` enum in
|
|
5
|
+
* `schemas/finding.schema.json`, and the `kind` pattern regex in both this file
|
|
6
|
+
* and the schema — they are kept in lockstep by the test suite.
|
|
7
|
+
*/
|
|
8
|
+
export type ToolKind = 'scope_trail' | 'policy_mesh' | 'capability_echo' | 'task_bound' | 'session_trail';
|
|
9
|
+
export interface FindingLocation {
|
|
10
|
+
file: string;
|
|
11
|
+
line?: number;
|
|
12
|
+
column?: number;
|
|
13
|
+
endLine?: number;
|
|
14
|
+
endColumn?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Finding {
|
|
17
|
+
/** Originating tool. */
|
|
18
|
+
tool: ToolKind;
|
|
19
|
+
/** Namespaced kind: `<tool_kind>.<short_slug>` (e.g. `scope_trail.permission_allow_widened`). */
|
|
20
|
+
kind: string;
|
|
21
|
+
severity: Severity;
|
|
22
|
+
/** Human-readable headline. Single line. */
|
|
23
|
+
message: string;
|
|
24
|
+
/** Optional longer-form explanation; can be multi-line. */
|
|
25
|
+
detail?: string;
|
|
26
|
+
location?: FindingLocation;
|
|
27
|
+
/** Stable identifier for dedupe across runs. Recommended: hash of (kind, location, salient fields). */
|
|
28
|
+
fingerprint?: string;
|
|
29
|
+
/** Optional structured metadata; downstream meta-reviewers may inspect it. */
|
|
30
|
+
data?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
export declare const SEVERITIES: readonly Severity[];
|
|
33
|
+
export declare const TOOL_KINDS: readonly ToolKind[];
|
|
34
|
+
export declare function isSeverity(value: unknown): value is Severity;
|
|
35
|
+
export declare function isToolKind(value: unknown): value is ToolKind;
|
|
36
|
+
/**
|
|
37
|
+
* Build a namespaced finding kind like `scope_trail.permission_allow_widened`
|
|
38
|
+
* without hand-assembling the dotted string. The `name` slug must match
|
|
39
|
+
* `[a-z0-9_]+` — the same pattern the JSON schema enforces.
|
|
40
|
+
*
|
|
41
|
+
* @throws if `name` contains characters outside the allowed slug class.
|
|
42
|
+
*/
|
|
43
|
+
export declare function kind<T extends ToolKind>(tool: T, name: string): `${T}.${string}`;
|
|
44
|
+
/**
|
|
45
|
+
* Runtime guard matching the JSON schema's `kind` pattern. Useful for
|
|
46
|
+
* tools that want to assert their finding constructors produce valid
|
|
47
|
+
* namespaced kinds before emit.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isNamespacedKind(value: unknown): value is `${ToolKind}.${string}`;
|
|
50
|
+
/** Constructor spec for {@link createFinding}. */
|
|
51
|
+
export interface CreateFindingSpec {
|
|
52
|
+
tool: ToolKind;
|
|
53
|
+
/** Slug appended to `tool` to form `kind`. Must match `[a-z0-9_]+`. */
|
|
54
|
+
name: string;
|
|
55
|
+
severity: Severity;
|
|
56
|
+
message: string;
|
|
57
|
+
detail?: string;
|
|
58
|
+
location?: FindingLocation;
|
|
59
|
+
data?: Record<string, unknown>;
|
|
60
|
+
/** Optional explicit fingerprint. If omitted, {@link fingerprintFinding} is computed. */
|
|
61
|
+
fingerprint?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Convenience constructor that assembles a {@link Finding} with a validated
|
|
65
|
+
* namespaced `kind` and a deterministic fingerprint. Equivalent to building the
|
|
66
|
+
* object literal by hand plus calling {@link kind} and {@link fingerprintFinding}.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const f = createFinding({
|
|
70
|
+
* tool: 'scope_trail',
|
|
71
|
+
* name: 'permission_allow_widened',
|
|
72
|
+
* severity: 'high',
|
|
73
|
+
* message: 'Claude Code allow rule widened to Bash(npm *)',
|
|
74
|
+
* location: { file: '.claude/settings.json', line: 12 },
|
|
75
|
+
* });
|
|
76
|
+
* // f.kind === 'scope_trail.permission_allow_widened'
|
|
77
|
+
* // f.fingerprint === '<stable hex>'
|
|
78
|
+
*/
|
|
79
|
+
export declare function createFinding(spec: CreateFindingSpec): Finding;
|
|
80
|
+
/**
|
|
81
|
+
* Stable 16-character hex fingerprint for a finding, derived from its routing
|
|
82
|
+
* fields (`kind`, `location.file`, `location.line`, `location.column`). Two
|
|
83
|
+
* findings emitted by the same tool against the same site collapse to the same
|
|
84
|
+
* fingerprint, so a downstream meta-reviewer can dedupe across runs.
|
|
85
|
+
*
|
|
86
|
+
* The fingerprint deliberately ignores `message`, `detail`, and `data` — those
|
|
87
|
+
* fields can drift across versions without changing the underlying issue.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* fingerprintFinding({
|
|
91
|
+
* tool: 'task_bound',
|
|
92
|
+
* kind: 'task_bound.out_of_scope_file',
|
|
93
|
+
* severity: 'medium',
|
|
94
|
+
* message: 'Touched file outside stated task',
|
|
95
|
+
* location: { file: 'src/index.ts', line: 42 },
|
|
96
|
+
* });
|
|
97
|
+
* // → '7e1c9b3a4d8f6e02'
|
|
98
|
+
*/
|
|
99
|
+
export declare function fingerprintFinding(finding: Finding): string;
|
|
100
|
+
export interface FindingValidationResult {
|
|
101
|
+
ok: boolean;
|
|
102
|
+
errors: string[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Runtime check that a value conforms to the canonical Finding schema
|
|
106
|
+
* (`schemas/finding.schema.json`). Returns the first error per offending
|
|
107
|
+
* field rather than throwing — meta-reviewers can collect errors across a
|
|
108
|
+
* batch and report them in aggregate.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* const result = validateFinding(jsonFromDisk);
|
|
112
|
+
* if (!result.ok) console.error(result.errors.join('\n'));
|
|
113
|
+
*/
|
|
114
|
+
export declare function validateFinding(value: unknown): FindingValidationResult;
|
|
115
|
+
//# sourceMappingURL=finding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding.d.ts","sourceRoot":"","sources":["../src/finding.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAChB,aAAa,GACb,aAAa,GACb,iBAAiB,GACjB,YAAY,GACZ,eAAe,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,wBAAwB;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,uGAAuG;IACvG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAA0C,CAAC;AAErF,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAMzC,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,IAAI,MAAM,EAAE,CAOhF;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,GAAG,QAAQ,IAAI,MAAM,EAAE,CAEjF;AAED,kDAAkD;AAClD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,QAAQ,CAAC;IACf,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAY9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAQ3D;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAeD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAkCvE"}
|
package/dist/finding.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
export const SEVERITIES = ['low', 'medium', 'high', 'critical'];
|
|
3
|
+
export const TOOL_KINDS = [
|
|
4
|
+
'scope_trail',
|
|
5
|
+
'policy_mesh',
|
|
6
|
+
'capability_echo',
|
|
7
|
+
'task_bound',
|
|
8
|
+
'session_trail',
|
|
9
|
+
];
|
|
10
|
+
export function isSeverity(value) {
|
|
11
|
+
return typeof value === 'string' && SEVERITIES.includes(value);
|
|
12
|
+
}
|
|
13
|
+
export function isToolKind(value) {
|
|
14
|
+
return typeof value === 'string' && TOOL_KINDS.includes(value);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build a namespaced finding kind like `scope_trail.permission_allow_widened`
|
|
18
|
+
* without hand-assembling the dotted string. The `name` slug must match
|
|
19
|
+
* `[a-z0-9_]+` — the same pattern the JSON schema enforces.
|
|
20
|
+
*
|
|
21
|
+
* @throws if `name` contains characters outside the allowed slug class.
|
|
22
|
+
*/
|
|
23
|
+
export function kind(tool, name) {
|
|
24
|
+
if (!/^[a-z0-9_]+$/.test(name)) {
|
|
25
|
+
throw new Error(`agent-gov-core/kind: name '${name}' must match [a-z0-9_]+ (kebab, camelCase, and dots are rejected)`);
|
|
26
|
+
}
|
|
27
|
+
return `${tool}.${name}`;
|
|
28
|
+
}
|
|
29
|
+
const KIND_PATTERN = /^(scope_trail|policy_mesh|capability_echo|task_bound|session_trail)\.[a-z0-9_]+$/;
|
|
30
|
+
/**
|
|
31
|
+
* Runtime guard matching the JSON schema's `kind` pattern. Useful for
|
|
32
|
+
* tools that want to assert their finding constructors produce valid
|
|
33
|
+
* namespaced kinds before emit.
|
|
34
|
+
*/
|
|
35
|
+
export function isNamespacedKind(value) {
|
|
36
|
+
return typeof value === 'string' && KIND_PATTERN.test(value);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convenience constructor that assembles a {@link Finding} with a validated
|
|
40
|
+
* namespaced `kind` and a deterministic fingerprint. Equivalent to building the
|
|
41
|
+
* object literal by hand plus calling {@link kind} and {@link fingerprintFinding}.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* const f = createFinding({
|
|
45
|
+
* tool: 'scope_trail',
|
|
46
|
+
* name: 'permission_allow_widened',
|
|
47
|
+
* severity: 'high',
|
|
48
|
+
* message: 'Claude Code allow rule widened to Bash(npm *)',
|
|
49
|
+
* location: { file: '.claude/settings.json', line: 12 },
|
|
50
|
+
* });
|
|
51
|
+
* // f.kind === 'scope_trail.permission_allow_widened'
|
|
52
|
+
* // f.fingerprint === '<stable hex>'
|
|
53
|
+
*/
|
|
54
|
+
export function createFinding(spec) {
|
|
55
|
+
const finding = {
|
|
56
|
+
tool: spec.tool,
|
|
57
|
+
kind: kind(spec.tool, spec.name),
|
|
58
|
+
severity: spec.severity,
|
|
59
|
+
message: spec.message,
|
|
60
|
+
};
|
|
61
|
+
if (spec.detail !== undefined)
|
|
62
|
+
finding.detail = spec.detail;
|
|
63
|
+
if (spec.location !== undefined)
|
|
64
|
+
finding.location = spec.location;
|
|
65
|
+
if (spec.data !== undefined)
|
|
66
|
+
finding.data = spec.data;
|
|
67
|
+
finding.fingerprint = spec.fingerprint ?? fingerprintFinding(finding);
|
|
68
|
+
return finding;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Stable 16-character hex fingerprint for a finding, derived from its routing
|
|
72
|
+
* fields (`kind`, `location.file`, `location.line`, `location.column`). Two
|
|
73
|
+
* findings emitted by the same tool against the same site collapse to the same
|
|
74
|
+
* fingerprint, so a downstream meta-reviewer can dedupe across runs.
|
|
75
|
+
*
|
|
76
|
+
* The fingerprint deliberately ignores `message`, `detail`, and `data` — those
|
|
77
|
+
* fields can drift across versions without changing the underlying issue.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* fingerprintFinding({
|
|
81
|
+
* tool: 'task_bound',
|
|
82
|
+
* kind: 'task_bound.out_of_scope_file',
|
|
83
|
+
* severity: 'medium',
|
|
84
|
+
* message: 'Touched file outside stated task',
|
|
85
|
+
* location: { file: 'src/index.ts', line: 42 },
|
|
86
|
+
* });
|
|
87
|
+
* // → '7e1c9b3a4d8f6e02'
|
|
88
|
+
*/
|
|
89
|
+
export function fingerprintFinding(finding) {
|
|
90
|
+
const parts = [
|
|
91
|
+
finding.kind,
|
|
92
|
+
finding.location?.file ?? '',
|
|
93
|
+
finding.location?.line ?? '',
|
|
94
|
+
finding.location?.column ?? '',
|
|
95
|
+
];
|
|
96
|
+
return createHash('sha256').update(parts.join('|')).digest('hex').slice(0, 16);
|
|
97
|
+
}
|
|
98
|
+
const FINDING_ALLOWED_KEYS = new Set([
|
|
99
|
+
'tool',
|
|
100
|
+
'kind',
|
|
101
|
+
'severity',
|
|
102
|
+
'message',
|
|
103
|
+
'detail',
|
|
104
|
+
'location',
|
|
105
|
+
'fingerprint',
|
|
106
|
+
'data',
|
|
107
|
+
]);
|
|
108
|
+
const LOCATION_ALLOWED_KEYS = new Set(['file', 'line', 'column', 'endLine', 'endColumn']);
|
|
109
|
+
/**
|
|
110
|
+
* Runtime check that a value conforms to the canonical Finding schema
|
|
111
|
+
* (`schemas/finding.schema.json`). Returns the first error per offending
|
|
112
|
+
* field rather than throwing — meta-reviewers can collect errors across a
|
|
113
|
+
* batch and report them in aggregate.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* const result = validateFinding(jsonFromDisk);
|
|
117
|
+
* if (!result.ok) console.error(result.errors.join('\n'));
|
|
118
|
+
*/
|
|
119
|
+
export function validateFinding(value) {
|
|
120
|
+
const errors = [];
|
|
121
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
122
|
+
return { ok: false, errors: ['finding must be a plain object'] };
|
|
123
|
+
}
|
|
124
|
+
const v = value;
|
|
125
|
+
if (!isToolKind(v.tool))
|
|
126
|
+
errors.push(`tool must be one of: ${TOOL_KINDS.join(', ')}`);
|
|
127
|
+
if (!isNamespacedKind(v.kind))
|
|
128
|
+
errors.push("kind must match '<tool>.<slug>' (e.g. 'scope_trail.permission_allow_widened')");
|
|
129
|
+
if (!isSeverity(v.severity))
|
|
130
|
+
errors.push(`severity must be one of: ${SEVERITIES.join(', ')}`);
|
|
131
|
+
if (typeof v.message !== 'string' || v.message.length === 0)
|
|
132
|
+
errors.push('message must be a non-empty string');
|
|
133
|
+
if (isToolKind(v.tool) && isNamespacedKind(v.kind) && !v.kind.startsWith(`${v.tool}.`)) {
|
|
134
|
+
errors.push(`kind '${v.kind}' must start with tool '${v.tool}.'`);
|
|
135
|
+
}
|
|
136
|
+
if (v.detail !== undefined && typeof v.detail !== 'string') {
|
|
137
|
+
errors.push('detail must be a string when present');
|
|
138
|
+
}
|
|
139
|
+
if (v.fingerprint !== undefined && typeof v.fingerprint !== 'string') {
|
|
140
|
+
errors.push('fingerprint must be a string when present');
|
|
141
|
+
}
|
|
142
|
+
if (v.data !== undefined && (v.data === null || typeof v.data !== 'object' || Array.isArray(v.data))) {
|
|
143
|
+
errors.push('data must be an object when present');
|
|
144
|
+
}
|
|
145
|
+
if (v.location !== undefined) {
|
|
146
|
+
errors.push(...validateLocation(v.location));
|
|
147
|
+
}
|
|
148
|
+
for (const key of Object.keys(v)) {
|
|
149
|
+
if (!FINDING_ALLOWED_KEYS.has(key))
|
|
150
|
+
errors.push(`unknown property: ${key}`);
|
|
151
|
+
}
|
|
152
|
+
return { ok: errors.length === 0, errors };
|
|
153
|
+
}
|
|
154
|
+
function validateLocation(value) {
|
|
155
|
+
const errors = [];
|
|
156
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
157
|
+
return ['location must be an object when present'];
|
|
158
|
+
}
|
|
159
|
+
const loc = value;
|
|
160
|
+
if (typeof loc.file !== 'string' || loc.file.length === 0) {
|
|
161
|
+
errors.push('location.file must be a non-empty string');
|
|
162
|
+
}
|
|
163
|
+
for (const field of ['line', 'column', 'endLine', 'endColumn']) {
|
|
164
|
+
if (loc[field] !== undefined) {
|
|
165
|
+
const n = loc[field];
|
|
166
|
+
if (typeof n !== 'number' || !Number.isInteger(n) || n < 1) {
|
|
167
|
+
errors.push(`location.${field} must be a positive integer when present`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
for (const key of Object.keys(loc)) {
|
|
172
|
+
if (!LOCATION_ALLOWED_KEYS.has(key))
|
|
173
|
+
errors.push(`unknown location property: ${key}`);
|
|
174
|
+
}
|
|
175
|
+
return errors;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=finding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding.js","sourceRoot":"","sources":["../src/finding.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type { Finding, FindingLocation, Severity, ToolKind, CreateFindingSpec, FindingValidationResult, } from './finding.js';
|
|
2
|
+
export { SEVERITIES, TOOL_KINDS, isSeverity, isToolKind, isNamespacedKind, kind, createFinding, fingerprintFinding, validateFinding, } from './finding.js';
|
|
3
|
+
export type { JsonObjectWithSource } from './jsonc.js';
|
|
4
|
+
export { readJsonObjectWithSource, stripJsonComments } from './jsonc.js';
|
|
5
|
+
export type { TomlObjectWithSource } from './toml.js';
|
|
6
|
+
export { readTomlObject, parseToml } from './toml.js';
|
|
7
|
+
export type { ByteRange } from './locators.js';
|
|
8
|
+
export { lineOfJsonKey, lineOfJsonStringValue, lineOfTomlKey, } from './locators.js';
|
|
9
|
+
export type { McpCommandSpec } from './mcp.js';
|
|
10
|
+
export { normalizeMcpCommand } from './mcp.js';
|
|
11
|
+
export { tokenizeShell, getCommandHead } from './shell.js';
|
|
12
|
+
export { rankSeverity, passesSeverityThreshold, anyAtOrAbove, emitFindingAnnotation, } from './action.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,IAAI,EACJ,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEzE,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtD,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,aAAa,GACd,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EACL,YAAY,EACZ,uBAAuB,EACvB,YAAY,EACZ,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { SEVERITIES, TOOL_KINDS, isSeverity, isToolKind, isNamespacedKind, kind, createFinding, fingerprintFinding, validateFinding, } from './finding.js';
|
|
2
|
+
export { readJsonObjectWithSource, stripJsonComments } from './jsonc.js';
|
|
3
|
+
export { readTomlObject, parseToml } from './toml.js';
|
|
4
|
+
export { lineOfJsonKey, lineOfJsonStringValue, lineOfTomlKey, } from './locators.js';
|
|
5
|
+
export { normalizeMcpCommand } from './mcp.js';
|
|
6
|
+
export { tokenizeShell, getCommandHead } from './shell.js';
|
|
7
|
+
export { rankSeverity, passesSeverityThreshold, anyAtOrAbove, emitFindingAnnotation, } from './action.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,IAAI,EACJ,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtD,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,aAAa,GACd,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EACL,YAAY,EACZ,uBAAuB,EACvB,YAAY,EACZ,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
|
package/dist/jsonc.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface JsonObjectWithSource {
|
|
2
|
+
/** Parsed JSON value, or `undefined` if parsing failed. */
|
|
3
|
+
json: unknown;
|
|
4
|
+
/** Raw file text (untouched). */
|
|
5
|
+
text: string;
|
|
6
|
+
/** Set when parsing failed. */
|
|
7
|
+
parseError?: Error;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Strip `//` line comments, `/* ... *\/` block comments, and trailing commas from JSONC,
|
|
11
|
+
* preserving byte offsets (replacement is space-filled, newlines preserved) so downstream
|
|
12
|
+
* line locators still match the original `text`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function stripJsonComments(input: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Read a JSONC file and return both the parsed value and the original text.
|
|
17
|
+
* The original text is preserved exactly so line locators can operate on it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readJsonObjectWithSource(path: string): JsonObjectWithSource;
|
|
20
|
+
//# sourceMappingURL=jsonc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonc.d.ts","sourceRoot":"","sources":["../src/jsonc.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,IAAI,EAAE,OAAO,CAAC;IACd,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,UAAU,CAAC,EAAE,KAAK,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAoEvD;AA8BD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAS3E"}
|