@clear-capabilities/agentic-security-scanner 0.147.0 → 0.147.5
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 +127 -0
- package/dist/1122.index.js +79 -2
- package/dist/3180.index.js +73 -1
- package/dist/5051.index.js +77 -6
- package/dist/frontend/index.html +21 -0
- package/dist/frontend/src/app.js +176 -0
- package/dist/frontend/src/components/evidence-inspector.js +141 -0
- package/dist/frontend/src/components/filter-rail.js +119 -0
- package/dist/frontend/src/components/query-bar.js +126 -0
- package/dist/frontend/src/data/flagship-graph.js +1460 -0
- package/dist/frontend/src/export-entry.js +36 -0
- package/dist/frontend/src/lib/api-client.js +92 -0
- package/dist/frontend/src/lib/contrast.js +34 -0
- package/dist/frontend/src/lib/dom.js +24 -0
- package/dist/frontend/src/lib/escape-html.js +16 -0
- package/dist/frontend/src/lib/flow-path.js +40 -0
- package/dist/frontend/src/lib/focus-controls.js +149 -0
- package/dist/frontend/src/lib/protection-visual.js +46 -0
- package/dist/frontend/src/lib/query-language.js +240 -0
- package/dist/frontend/src/lib/row-filters.js +43 -0
- package/dist/frontend/src/lib/state.js +84 -0
- package/dist/frontend/src/main.js +83 -0
- package/dist/frontend/src/shell.js +184 -0
- package/dist/frontend/src/views/architecture-view.js +798 -0
- package/dist/frontend/src/views/inventory-view.js +292 -0
- package/dist/frontend/src/views/privacy-view.js +172 -0
- package/dist/frontend/src/views/trace-view.js +206 -0
- package/dist/frontend/styles/architecture-view.css +93 -0
- package/dist/frontend/styles/filter-rail.css +34 -0
- package/dist/frontend/styles/inspector.css +69 -0
- package/dist/frontend/styles/inventory-view.css +74 -0
- package/dist/frontend/styles/privacy-view.css +86 -0
- package/dist/frontend/styles/query-bar.css +107 -0
- package/dist/frontend/styles/shell.css +155 -0
- package/dist/frontend/styles/tokens.css +128 -0
- package/dist/frontend/styles/trace-view.css +95 -0
- package/package.json +2 -2
- package/src/server/static-assets.js +11 -6
- package/src/shared/frontend-root.js +52 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Data Flow Explorer design tokens (PRD §8.1). Dark is the reference theme.
|
|
3
|
+
* Values are copied verbatim from the PRD — do not adjust without a
|
|
4
|
+
* deliberate, separately-reviewed change.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
/* Surfaces */
|
|
9
|
+
--surface-canvas: #061625;
|
|
10
|
+
--surface-panel: #0B1E2F;
|
|
11
|
+
--surface-elevated: #102A40;
|
|
12
|
+
--border-default: #28445A;
|
|
13
|
+
|
|
14
|
+
/* Text */
|
|
15
|
+
--text-primary: #F3F7FA;
|
|
16
|
+
--text-secondary: #9FB3C5;
|
|
17
|
+
|
|
18
|
+
/* Selection / focus */
|
|
19
|
+
--accent-selection: #3DA9FF;
|
|
20
|
+
|
|
21
|
+
/* Protection verdict status */
|
|
22
|
+
--status-protected: #59D17D;
|
|
23
|
+
--status-unprotected: #FF625C;
|
|
24
|
+
--status-unknown: #F5B83D;
|
|
25
|
+
|
|
26
|
+
/* Text color for content painted directly on a status token background
|
|
27
|
+
(e.g. the coverage banner). NOT the same as --text-primary, which is
|
|
28
|
+
sized against --surface-canvas/--surface-panel, not against a status
|
|
29
|
+
color. Verified with lib/contrast.js's contrastRatio(): #061625 on
|
|
30
|
+
dark --status-unknown (#F5B83D) is 10.27:1. See the light-theme block
|
|
31
|
+
below for the light-theme value and its own verified ratio. */
|
|
32
|
+
--status-banner-text: #061625;
|
|
33
|
+
|
|
34
|
+
/* AI processing context */
|
|
35
|
+
--context-ai: #B47AFF;
|
|
36
|
+
|
|
37
|
+
/* Data class overlays */
|
|
38
|
+
--class-pii: #28D1C5;
|
|
39
|
+
--class-phi: #A97BFF;
|
|
40
|
+
--class-pci: #4CA7FF;
|
|
41
|
+
|
|
42
|
+
/* Spacing (8px grid, PRD §7.7) */
|
|
43
|
+
--space-1: 8px;
|
|
44
|
+
--space-2: 16px;
|
|
45
|
+
--space-3: 24px;
|
|
46
|
+
--space-4: 32px;
|
|
47
|
+
|
|
48
|
+
/* Radius / border (PRD §8.2) */
|
|
49
|
+
--radius-default: 8px;
|
|
50
|
+
--border-width: 1px;
|
|
51
|
+
|
|
52
|
+
/* Typography (PRD §8.2) — bundled system stack; never a remote/CDN font */
|
|
53
|
+
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
54
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
55
|
+
--font-size-view-title: 23px;
|
|
56
|
+
--font-size-panel-title: 15px;
|
|
57
|
+
--font-size-node-title: 14px;
|
|
58
|
+
--font-size-body: 13px;
|
|
59
|
+
--font-size-code: 12.5px;
|
|
60
|
+
|
|
61
|
+
/* Motion (PRD §8.2 — focus/topology change only, no ambient animation) */
|
|
62
|
+
--motion-duration: 200ms;
|
|
63
|
+
--motion-duration-reduced: 0ms;
|
|
64
|
+
|
|
65
|
+
/* Shell region sizing (PRD §7.7) */
|
|
66
|
+
--header-height: 56px;
|
|
67
|
+
--view-tabs-height: 44px;
|
|
68
|
+
--left-rail-width: 248px;
|
|
69
|
+
--left-rail-collapsed-width: 56px;
|
|
70
|
+
--right-inspector-width: 360px;
|
|
71
|
+
--context-rail-height: 72px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Light theme — same semantic tokens, WCAG-AA-equivalent contrast (PRD §8.1/§23) */
|
|
75
|
+
:root[data-theme="light"] {
|
|
76
|
+
--surface-canvas: #FFFFFF;
|
|
77
|
+
--surface-panel: #F4F7FA;
|
|
78
|
+
--surface-elevated: #E7EEF3;
|
|
79
|
+
--border-default: #C7D3DC;
|
|
80
|
+
|
|
81
|
+
--text-primary: #0B1E2F;
|
|
82
|
+
--text-secondary: #3E5568;
|
|
83
|
+
|
|
84
|
+
--accent-selection: #1B6FC2;
|
|
85
|
+
|
|
86
|
+
/* status-protected and status-unknown darkened slightly from their
|
|
87
|
+
original values (#1E8A4C, #9A6B00) — Milestone 3, sub-project A11y,
|
|
88
|
+
found via test/tokens-contrast.test.js that both originals failed
|
|
89
|
+
WCAG AA (4.5:1) against --surface-panel (#F4F7FA): 4.07:1 and 4.36:1
|
|
90
|
+
respectively. Verified with lib/contrast.js's contrastRatio() against
|
|
91
|
+
BOTH backgrounds a status color plausibly renders on: --status-
|
|
92
|
+
protected (#1C8047) is 4.97:1 on --surface-canvas / 4.62:1 on
|
|
93
|
+
--surface-panel; --status-unknown (#956700) is 4.98:1 / 4.63:1. Both
|
|
94
|
+
now clear AA with margin (>=4.6) rather than sitting at the exact
|
|
95
|
+
4.50 boundary, which is fragile against hex-rounding. --status-
|
|
96
|
+
unprotected was already compliant (5.26:1 / 4.89:1), unchanged. */
|
|
97
|
+
--status-protected: #1C8047;
|
|
98
|
+
--status-unprotected: #C7362D;
|
|
99
|
+
--status-unknown: #956700;
|
|
100
|
+
|
|
101
|
+
/* Verified with lib/contrast.js's contrastRatio(): #FFFFFF on light
|
|
102
|
+
--status-unknown (#956700) is 4.98:1 (>= 4.5:1 AA). The dark-block
|
|
103
|
+
value (#061625, --surface-canvas's own dark value) is 3.90:1 here --
|
|
104
|
+
below AA -- which is the bug this token pair fixes: shell.css must
|
|
105
|
+
never hardcode dark theme's value and expect it to also work in
|
|
106
|
+
light theme. */
|
|
107
|
+
--status-banner-text: #FFFFFF;
|
|
108
|
+
|
|
109
|
+
--context-ai: #6E38B8;
|
|
110
|
+
|
|
111
|
+
--class-pii: #0E7C73;
|
|
112
|
+
--class-phi: #6E44B8;
|
|
113
|
+
--class-pci: #1C63A8;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@media (prefers-reduced-motion: reduce) {
|
|
117
|
+
:root {
|
|
118
|
+
--motion-duration: var(--motion-duration-reduced);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
body {
|
|
123
|
+
background: var(--surface-canvas);
|
|
124
|
+
color: var(--text-primary);
|
|
125
|
+
font-family: var(--font-family);
|
|
126
|
+
font-size: var(--font-size-body);
|
|
127
|
+
margin: 0;
|
|
128
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
.trace-view {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--space-2);
|
|
5
|
+
max-width: 640px;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.trace-step {
|
|
9
|
+
display: flex;
|
|
10
|
+
gap: var(--space-2);
|
|
11
|
+
padding: var(--space-2);
|
|
12
|
+
background: var(--surface-panel);
|
|
13
|
+
border: var(--border-width) solid var(--border-default);
|
|
14
|
+
border-radius: var(--radius-default);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.trace-step-number {
|
|
18
|
+
flex: 0 0 auto;
|
|
19
|
+
width: 24px;
|
|
20
|
+
height: 24px;
|
|
21
|
+
border-radius: 50%;
|
|
22
|
+
background: var(--surface-elevated);
|
|
23
|
+
color: var(--text-primary);
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
font-size: var(--font-size-code);
|
|
28
|
+
font-weight: 600;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.trace-step-body {
|
|
32
|
+
flex: 1 1 auto;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.trace-step-kind {
|
|
36
|
+
color: var(--text-secondary);
|
|
37
|
+
font-size: var(--font-size-code);
|
|
38
|
+
text-transform: uppercase;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.trace-step-mapping {
|
|
42
|
+
font-family: var(--font-mono);
|
|
43
|
+
font-size: var(--font-size-code);
|
|
44
|
+
color: var(--text-primary);
|
|
45
|
+
margin: 4px 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.trace-step-node {
|
|
49
|
+
color: var(--text-secondary);
|
|
50
|
+
font-size: var(--font-size-body);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.trace-step-boundary {
|
|
54
|
+
display: inline-block;
|
|
55
|
+
border: 1px solid var(--status-unprotected);
|
|
56
|
+
color: var(--status-unprotected);
|
|
57
|
+
border-radius: var(--radius-default);
|
|
58
|
+
padding: 1px 6px;
|
|
59
|
+
font-size: var(--font-size-code);
|
|
60
|
+
margin-top: 4px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.trace-branch-group {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
gap: var(--space-2);
|
|
67
|
+
padding-left: var(--space-2);
|
|
68
|
+
border-left: 2px solid var(--accent-selection);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.trace-branch-label {
|
|
72
|
+
color: var(--text-secondary);
|
|
73
|
+
font-size: var(--font-size-code);
|
|
74
|
+
text-transform: uppercase;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.trace-alternates {
|
|
78
|
+
margin-top: var(--space-2);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.trace-alternate-item {
|
|
82
|
+
padding: var(--space-1) 0;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
color: var(--text-secondary);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.trace-alternate-item:hover {
|
|
88
|
+
color: var(--text-primary);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.trace-empty {
|
|
92
|
+
color: var(--text-secondary);
|
|
93
|
+
font-style: italic;
|
|
94
|
+
padding: var(--space-2);
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clear-capabilities/agentic-security-scanner",
|
|
3
|
-
"version": "0.147.
|
|
3
|
+
"version": "0.147.5",
|
|
4
4
|
"description": "Scanner engine for the agentic-security Claude Code plugin — SAST, SCA (function-level reachability + CISA KEV), secrets, IaC, prompt-injection, MCP/agent-tool audit, auth/authZ deep analysis, attack chains, PoC generation, business logic, toxic-combinations scoring, SBOM, pipeline integrity, compliance attestation, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"lodash-es": "^4.18.1"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
|
-
"build": "ncc build bin/agentic-security.js -o dist --minify -e web-tree-sitter -e tree-sitter-wasms && rm -rf dist/compliance-frameworks && mkdir -p dist/compliance-frameworks && cp src/posture/compliance-frameworks/*.json dist/compliance-frameworks/ && mv dist/index.js dist/agentic-security.mjs && rm -f dist/package.json && chmod +x dist/agentic-security.mjs && node -e \"const fs=require('fs');const p='dist/agentic-security.mjs';const c=fs.readFileSync(p,'utf8');if(!c.startsWith('#!'))fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c);\" && node -e \"const fs=require('fs');const c=require('crypto');const h=c.createHash('sha256').update(fs.readFileSync('dist/agentic-security.mjs')).digest('hex');fs.writeFileSync('dist/agentic-security.mjs.sha256',h+' agentic-security.mjs\\n');\"",
|
|
59
|
+
"build": "ncc build bin/agentic-security.js -o dist --minify -e web-tree-sitter -e tree-sitter-wasms && rm -rf dist/compliance-frameworks && mkdir -p dist/compliance-frameworks && cp src/posture/compliance-frameworks/*.json dist/compliance-frameworks/ && node scripts/copy-frontend.mjs && mv dist/index.js dist/agentic-security.mjs && rm -f dist/package.json && chmod +x dist/agentic-security.mjs && node -e \"const fs=require('fs');const p='dist/agentic-security.mjs';const c=fs.readFileSync(p,'utf8');if(!c.startsWith('#!'))fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c);\" && node -e \"const fs=require('fs');const c=require('crypto');const h=c.createHash('sha256').update(fs.readFileSync('dist/agentic-security.mjs')).digest('hex');fs.writeFileSync('dist/agentic-security.mjs.sha256',h+' agentic-security.mjs\\n');\"",
|
|
60
60
|
"prepare": "node ../scripts/pre-push-gate.mjs --install-hook",
|
|
61
61
|
"prepublishOnly": "npm run build && node ../scripts/sync-scanner-changelog.mjs && node ../scripts/release-check.mjs",
|
|
62
62
|
"test": "node ../scripts/run-unit-tests.mjs && AGENTIC_SECURITY_CPP_DATAFLOW=1 node --test test/cpp-dataflow.test.js && node --experimental-test-module-mocks --test test/fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection-k8s-admission.test.js && node --experimental-test-module-mocks --test test/lineage-fault-injection.test.js && npm run test:python",
|
|
@@ -14,14 +14,19 @@
|
|
|
14
14
|
|
|
15
15
|
import * as path from 'node:path';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { resolveFrontendRoot } from '../shared/frontend-root.js';
|
|
17
18
|
|
|
18
|
-
// Located
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
19
|
+
// Located relative to this module's own directory (path.dirname(
|
|
20
|
+
// fileURLToPath(import.meta.url)), the same pattern scanner/src/mcp/server.js
|
|
21
|
+
// uses), but via resolveFrontendRoot's search-upward strategy rather than a
|
|
22
|
+
// single hardcoded relative depth — a fixed `../../../frontend` resolved
|
|
23
|
+
// correctly for this file's unbundled dev location but broke (silently, at
|
|
24
|
+
// runtime, for every real npx/npm user) once `npm run build` split this
|
|
25
|
+
// module into its own dist/ chunk, whose import.meta.url reflects dist/'s
|
|
26
|
+
// own shallower location. See src/shared/frontend-root.js for the full story
|
|
27
|
+
// and scripts/copy-frontend.mjs for the build-time copy this now finds.
|
|
23
28
|
const _here = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
-
export const FRONTEND_ROOT =
|
|
29
|
+
export const FRONTEND_ROOT = resolveFrontendRoot(_here);
|
|
25
30
|
|
|
26
31
|
export const CONTENT_TYPE_MAP = Object.freeze({
|
|
27
32
|
'.html': 'text/html; charset=utf-8',
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// frontend-root.js — locates the Data Flow Explorer's `frontend/` assets
|
|
2
|
+
// (index.html/src/styles) regardless of whether this code is running:
|
|
3
|
+
// - unbundled, straight out of scanner/src/ or scanner/scripts/ (dev/test —
|
|
4
|
+
// frontend/ is a monorepo sibling of scanner/, 2-3 levels up), or
|
|
5
|
+
// - bundled by `npm run build` (ncc splits a dynamic import() into its own
|
|
6
|
+
// chunk file physically written into scanner/dist/, so import.meta.url
|
|
7
|
+
// inside that chunk reflects dist/'s own location, not the original
|
|
8
|
+
// source file's — a fixed relative-levels-up path that was correct for
|
|
9
|
+
// one depth breaks silently at the other), or
|
|
10
|
+
// - installed from the published npm package, where the build copies
|
|
11
|
+
// frontend/'s servable files into scanner/dist/frontend/ (a sibling of
|
|
12
|
+
// the chunk file itself, i.e. 0 levels up) — see scripts/copy-frontend.mjs.
|
|
13
|
+
//
|
|
14
|
+
// Rather than hardcode one of those depths (the bug this file fixes: every
|
|
15
|
+
// consumer used to hardcode the dev-only depth), search upward from the
|
|
16
|
+
// caller's own directory and take the first candidate that actually has a
|
|
17
|
+
// frontend/index.html on disk. Never guessed silently past that — a caller
|
|
18
|
+
// with no match anywhere gets a clear, actionable error instead of a
|
|
19
|
+
// downstream ENOENT/404 with no indication why.
|
|
20
|
+
|
|
21
|
+
import * as fs from 'node:fs';
|
|
22
|
+
import * as path from 'node:path';
|
|
23
|
+
|
|
24
|
+
const MAX_LEVELS_UP = 4;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} startDir - `path.dirname(fileURLToPath(import.meta.url))`
|
|
28
|
+
* of the CALLING module (not this file) — each caller's own bundled/
|
|
29
|
+
* unbundled location determines which candidate depth resolves.
|
|
30
|
+
* @returns {string} absolute path to a real `frontend/` directory containing
|
|
31
|
+
* `index.html`.
|
|
32
|
+
* @throws if no candidate directory up to MAX_LEVELS_UP contains one.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveFrontendRoot(startDir) {
|
|
35
|
+
const tried = [];
|
|
36
|
+
for (let up = 0; up <= MAX_LEVELS_UP; up++) {
|
|
37
|
+
const candidate = path.resolve(startDir, ...Array(up).fill('..'), 'frontend');
|
|
38
|
+
tried.push(candidate);
|
|
39
|
+
// Every caller invokes resolveFrontendRoot() once, at module-load time,
|
|
40
|
+
// to compute a top-level FRONTEND_ROOT const (see static-assets.js /
|
|
41
|
+
// generate-html-report.mjs) — never per-request inside the explore
|
|
42
|
+
// server's request handler. At most MAX_LEVELS_UP+1 (5) sync stat calls
|
|
43
|
+
// at process startup is not a request-path DoS surface.
|
|
44
|
+
if (fs.existsSync(path.join(candidate, 'index.html'))) return candidate; // agentic-security-ignore: dos-sync-io
|
|
45
|
+
}
|
|
46
|
+
throw new Error(
|
|
47
|
+
`resolveFrontendRoot: no frontend/index.html found searching up from ${startDir}. ` +
|
|
48
|
+
`Tried: ${tried.join(', ')}. If you're running from a source checkout, run \`npm run build\` ` +
|
|
49
|
+
`first (it copies frontend/ into scanner/dist/frontend/); if you're running the published ` +
|
|
50
|
+
`package, this indicates a packaging defect — report it.`
|
|
51
|
+
);
|
|
52
|
+
}
|