@clear-capabilities/agentic-security-scanner 0.158.0 → 0.159.0
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 +29 -0
- package/dist/1919.index.js +5 -2
- package/dist/2862.index.js +29 -9
- package/dist/3531.index.js +1 -1
- package/dist/4171.index.js +4 -4
- package/dist/{8551.index.js → 7033.index.js} +415 -22
- package/dist/9560.index.js +3 -1
- package/dist/agentic-security.mjs +15 -15
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +6 -4
- package/src/engine.js +6 -1
- package/src/language/CLAUDE.md +48 -1
- package/src/language/assurance.js +21 -4
- package/src/language/context.js +2 -0
- package/src/language/cvss.js +91 -0
- package/src/language/fix-lifecycle.js +114 -21
- package/src/language/haskell-adapter.js +4 -3
- package/src/language/haskell-advisory-feed.js +15 -0
- package/src/language/haskell-boundaries.js +220 -0
- package/src/language/haskell-cpp.js +460 -0
- package/src/language/haskell-ir.js +6 -3
- package/src/language/haskell-models.js +4 -3
- package/src/language/haskell-parser.js +122 -38
- package/src/language/haskell-sca.js +10 -6
- package/src/language/haskell-supply.js +42 -8
- package/src/language/haskell-web.js +128 -23
- package/src/language/nix-advisory-feed.js +275 -0
- package/src/language/nix-fix.js +162 -24
- package/src/language/nix-ir.js +16 -12
- package/src/language/nix-sca.js +71 -12
- package/src/language/nixos-eval-forms.js +162 -0
- package/src/language/nixos-module-resolver.js +346 -51
- package/src/language/resolved-pass.js +57 -3
- package/src/lsp/server.js +5 -2
- package/src/mcp/tools.js +3 -1
- package/src/posture/fix-history.js +30 -3
- package/src/report/index.js +4 -1
- package/dist/1310.index.js +0 -3161
- package/dist/2376.index.js +0 -3151
- package/dist/3526.index.js +0 -1019
- package/dist/7310.index.js +0 -520
- package/dist/8520.index.js +0 -13
- package/dist/9091.index.js +0 -3135
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Pure pieces of the bounded NixOS option evaluator (NIX-002): value classes, equality, and the
|
|
2
|
+
// closed table of library functions the evaluator is willing to run.
|
|
3
|
+
//
|
|
4
|
+
// The evaluator (nixos-module-resolver.js) is a SOUNDNESS-first subset: a value is either fully known
|
|
5
|
+
// or unknown, and any unknown part makes the whole value unknown. This file is the allow-list. A
|
|
6
|
+
// function that is not in these tables is never run, so `import`, `fetch*`, `readFile`, `toFile`,
|
|
7
|
+
// `currentSystem`, `getEnv`, derivations and every other effectful or environment-dependent primitive
|
|
8
|
+
// are unknown by construction (there is no deny-list to forget an entry in).
|
|
9
|
+
|
|
10
|
+
/** Thrown by a table function (and caught by the resolver) when its arguments are not statically usable. */
|
|
11
|
+
export const NOT_STATIC = Object.freeze({ notStatic: true });
|
|
12
|
+
|
|
13
|
+
/** A lambda captured with its definition environment. Never a final option value. */
|
|
14
|
+
export class Closure {
|
|
15
|
+
constructor(param, body, env) { this.param = param; this.body = body; this.env = env; }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A table function, possibly partially applied. Never a final option value. */
|
|
19
|
+
export class Builtin {
|
|
20
|
+
constructor(name, def, args = []) { this.name = name; this.def = def; this.args = args; }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** A namespace value (`lib`, `builtins`, `lib.strings`, `lib.lists`). Never a final option value. */
|
|
24
|
+
export class Ns {
|
|
25
|
+
constructor(kind) { this.kind = kind; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const isAttrs = (v) => v !== null && typeof v === 'object' && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
|
|
29
|
+
export const hasOwn = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
30
|
+
const isInt = (v) => typeof v === 'number' && Number.isSafeInteger(v);
|
|
31
|
+
|
|
32
|
+
/** Own-property assignment that is safe for the attribute name `__proto__`. */
|
|
33
|
+
export function setAttr(obj, key, value) {
|
|
34
|
+
Object.defineProperty(obj, key, { value, enumerable: true, writable: true, configurable: true });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** True when a value contains only data (no closure, partial application or namespace). Bounded. */
|
|
38
|
+
export function isPlain(v, budget = { n: 20_000 }, depth = 0) {
|
|
39
|
+
if (--budget.n < 0 || depth > 48) return false;
|
|
40
|
+
if (v === null || typeof v === 'string' || typeof v === 'boolean' || typeof v === 'number') return true;
|
|
41
|
+
if (Array.isArray(v)) return v.every((x) => isPlain(x, budget, depth + 1));
|
|
42
|
+
if (isAttrs(v)) return Object.keys(v).every((k) => isPlain(v[k], budget, depth + 1));
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Nix `==`: true, false, or null when it cannot be decided statically (functions are never compared). */
|
|
47
|
+
export function deepEq(a, b, depth = 0) {
|
|
48
|
+
if (depth > 48) return null;
|
|
49
|
+
const fnA = a instanceof Closure || a instanceof Builtin || a instanceof Ns;
|
|
50
|
+
const fnB = b instanceof Closure || b instanceof Builtin || b instanceof Ns;
|
|
51
|
+
if (fnA || fnB) return null;
|
|
52
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
53
|
+
if (!Array.isArray(a) || !Array.isArray(b)) return false;
|
|
54
|
+
if (a.length !== b.length) return false;
|
|
55
|
+
let unknown = false;
|
|
56
|
+
for (let i = 0; i < a.length; i++) { const r = deepEq(a[i], b[i], depth + 1); if (r === false) return false; if (r === null) unknown = true; }
|
|
57
|
+
return unknown ? null : true;
|
|
58
|
+
}
|
|
59
|
+
if (isAttrs(a) || isAttrs(b)) {
|
|
60
|
+
if (!isAttrs(a) || !isAttrs(b)) return false;
|
|
61
|
+
const ka = Object.keys(a); const kb = Object.keys(b);
|
|
62
|
+
if (ka.length !== kb.length || !ka.every((k) => hasOwn(b, k))) return false;
|
|
63
|
+
let unknown = false;
|
|
64
|
+
for (const k of ka) { const r = deepEq(a[k], b[k], depth + 1); if (r === false) return false; if (r === null) unknown = true; }
|
|
65
|
+
return unknown ? null : true;
|
|
66
|
+
}
|
|
67
|
+
return a === b;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const need = (cond) => { if (!cond) throw NOT_STATIC; };
|
|
71
|
+
const MAX_STRING = 65_536;
|
|
72
|
+
|
|
73
|
+
function toStr(v) {
|
|
74
|
+
if (typeof v === 'string') return v;
|
|
75
|
+
if (v === null || v === false) return '';
|
|
76
|
+
if (v === true) return '1';
|
|
77
|
+
if (isInt(v)) return String(v);
|
|
78
|
+
throw NOT_STATIC;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Each entry: arity and a function over the evaluated arguments. `c.apply(f, x)` runs a closure or table
|
|
82
|
+
// function and throws NOT_STATIC if it is not statically known; `c.size(n)` enforces the size cap.
|
|
83
|
+
const DEFS = {
|
|
84
|
+
map: { arity: 2, call: ([f, xs], c) => { need(Array.isArray(xs)); c.size(xs.length); return xs.map((x) => c.apply(f, x)); } },
|
|
85
|
+
filter: {
|
|
86
|
+
arity: 2,
|
|
87
|
+
call: ([f, xs], c) => { need(Array.isArray(xs)); c.size(xs.length); return xs.filter((x) => { const r = c.apply(f, x); need(typeof r === 'boolean'); return r; }); },
|
|
88
|
+
},
|
|
89
|
+
elem: {
|
|
90
|
+
arity: 2,
|
|
91
|
+
call: ([x, xs]) => {
|
|
92
|
+
need(Array.isArray(xs));
|
|
93
|
+
let unknown = false;
|
|
94
|
+
for (const y of xs) { const r = deepEq(x, y); if (r === true) return true; if (r === null) unknown = true; }
|
|
95
|
+
need(!unknown);
|
|
96
|
+
return false;
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
length: { arity: 1, call: ([xs]) => { need(Array.isArray(xs)); return xs.length; } },
|
|
100
|
+
concatLists: {
|
|
101
|
+
arity: 1,
|
|
102
|
+
call: ([xss], c) => { need(Array.isArray(xss) && xss.every(Array.isArray)); const out = [].concat(...xss); c.size(out.length); return out; },
|
|
103
|
+
},
|
|
104
|
+
concatMap: {
|
|
105
|
+
arity: 2,
|
|
106
|
+
call: ([f, xs], c) => {
|
|
107
|
+
need(Array.isArray(xs)); c.size(xs.length);
|
|
108
|
+
const parts = xs.map((x) => { const r = c.apply(f, x); need(Array.isArray(r)); return r; });
|
|
109
|
+
const out = [].concat(...parts); c.size(out.length); return out;
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
hasAttr: { arity: 2, call: ([name, set]) => { need(typeof name === 'string' && isAttrs(set)); return hasOwn(set, name); } },
|
|
113
|
+
toString: { arity: 1, call: ([v]) => toStr(v) },
|
|
114
|
+
optionals: { arity: 2, call: ([cond, xs]) => { need(typeof cond === 'boolean' && Array.isArray(xs)); return cond ? xs : []; } },
|
|
115
|
+
optional: { arity: 2, call: ([cond, x]) => { need(typeof cond === 'boolean'); return cond ? [x] : []; } },
|
|
116
|
+
optionalAttrs: { arity: 2, call: ([cond, set]) => { need(typeof cond === 'boolean' && isAttrs(set)); return cond ? set : {}; } },
|
|
117
|
+
optionalString: { arity: 2, call: ([cond, s]) => { need(typeof cond === 'boolean' && typeof s === 'string'); return cond ? s : ''; } },
|
|
118
|
+
concatStringsSep: {
|
|
119
|
+
arity: 2,
|
|
120
|
+
call: ([sep, xs], c) => {
|
|
121
|
+
need(typeof sep === 'string' && Array.isArray(xs) && xs.every((x) => typeof x === 'string')); c.size(xs.length);
|
|
122
|
+
const out = xs.join(sep); need(out.length <= MAX_STRING); return out;
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
concatMapStringsSep: {
|
|
126
|
+
arity: 3,
|
|
127
|
+
call: ([sep, f, xs], c) => {
|
|
128
|
+
need(typeof sep === 'string' && Array.isArray(xs)); c.size(xs.length);
|
|
129
|
+
const parts = xs.map((x) => { const r = c.apply(f, x); need(typeof r === 'string'); return r; });
|
|
130
|
+
const out = parts.join(sep); need(out.length <= MAX_STRING); return out;
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
hasPrefix: { arity: 2, call: ([p, s]) => { need(typeof p === 'string' && typeof s === 'string'); return s.startsWith(p); } },
|
|
134
|
+
hasSuffix: { arity: 2, call: ([p, s]) => { need(typeof p === 'string' && typeof s === 'string'); return s.endsWith(p); } },
|
|
135
|
+
boolToString: { arity: 1, call: ([b]) => { need(typeof b === 'boolean'); return b ? 'true' : 'false'; } },
|
|
136
|
+
// Only a merge of plain lists is a plain list; any other element (an attribute set, a priority or condition
|
|
137
|
+
// wrapper, an unknown) cannot be merged without the option's type, so it is not static.
|
|
138
|
+
mkMerge: { arity: 1, call: ([xss], c) => { need(Array.isArray(xss) && xss.every(Array.isArray)); const out = [].concat(...xss); c.size(out.length); return out; } },
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const LIB_NAMES = new Set(['map', 'filter', 'elem', 'length', 'concatLists', 'concatMap', 'optionals', 'optional', 'optionalAttrs', 'optionalString',
|
|
142
|
+
'concatStringsSep', 'concatMapStringsSep', 'hasPrefix', 'hasSuffix', 'boolToString', 'mkMerge']);
|
|
143
|
+
const LIB_STRINGS = new Set(['concatStringsSep', 'concatMapStringsSep', 'hasPrefix', 'hasSuffix', 'optionalString']);
|
|
144
|
+
const LIB_LISTS = new Set(['map', 'filter', 'elem', 'length', 'concatLists', 'concatMap', 'optionals', 'optional']);
|
|
145
|
+
const BUILTIN_NAMES = new Set(['map', 'filter', 'elem', 'length', 'concatLists', 'concatMap', 'hasAttr', 'toString']);
|
|
146
|
+
/** Names a Nix program has without any import (the ones we model). */
|
|
147
|
+
const GLOBAL_NAMES = new Set(['map', 'toString']);
|
|
148
|
+
|
|
149
|
+
const SETS = { lib: LIB_NAMES, builtins: BUILTIN_NAMES, 'lib.strings': LIB_STRINGS, 'lib.lists': LIB_LISTS };
|
|
150
|
+
|
|
151
|
+
/** Resolves `<ns>.<name>`: a table function, a sub-namespace, or undefined (not modelled). */
|
|
152
|
+
export function nsLookup(kind, name) {
|
|
153
|
+
if (kind === 'lib' && name === 'strings') return new Ns('lib.strings');
|
|
154
|
+
if (kind === 'lib' && name === 'lists') return new Ns('lib.lists');
|
|
155
|
+
const set = SETS[kind];
|
|
156
|
+
if (!set || !set.has(name)) return undefined;
|
|
157
|
+
return new Builtin(name, DEFS[name]);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const isGlobalName = (name) => GLOBAL_NAMES.has(name);
|
|
161
|
+
export const globalValue = (name) => (name === 'builtins' ? new Ns('builtins') : nsLookup('builtins', name));
|
|
162
|
+
export const MODELLED = Object.freeze({ lib: [...LIB_NAMES].sort(), builtins: [...BUILTIN_NAMES].sort() });
|