@aegis-scan/core 0.16.6 → 0.17.7
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/README.md +37 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/manipulation-resistance/ai-io-boundary.d.ts +84 -0
- package/dist/manipulation-resistance/ai-io-boundary.d.ts.map +1 -0
- package/dist/manipulation-resistance/ai-io-boundary.js +216 -0
- package/dist/manipulation-resistance/ai-io-boundary.js.map +1 -0
- package/dist/manipulation-resistance/config-integrity.d.ts +28 -0
- package/dist/manipulation-resistance/config-integrity.d.ts.map +1 -0
- package/dist/manipulation-resistance/config-integrity.js +53 -0
- package/dist/manipulation-resistance/config-integrity.js.map +1 -0
- package/dist/manipulation-resistance/index.d.ts +16 -0
- package/dist/manipulation-resistance/index.d.ts.map +1 -0
- package/dist/manipulation-resistance/index.js +16 -0
- package/dist/manipulation-resistance/index.js.map +1 -0
- package/dist/manipulation-resistance/instruction-boundary.d.ts +50 -0
- package/dist/manipulation-resistance/instruction-boundary.d.ts.map +1 -0
- package/dist/manipulation-resistance/instruction-boundary.js +114 -0
- package/dist/manipulation-resistance/instruction-boundary.js.map +1 -0
- package/dist/manipulation-resistance/oob-blocker.d.ts +58 -0
- package/dist/manipulation-resistance/oob-blocker.d.ts.map +1 -0
- package/dist/manipulation-resistance/oob-blocker.js +55 -0
- package/dist/manipulation-resistance/oob-blocker.js.map +1 -0
- package/dist/manipulation-resistance/redirect-policy.d.ts +43 -0
- package/dist/manipulation-resistance/redirect-policy.d.ts.map +1 -0
- package/dist/manipulation-resistance/redirect-policy.js +197 -0
- package/dist/manipulation-resistance/redirect-policy.js.map +1 -0
- package/dist/manipulation-resistance/response-validator.d.ts +33 -0
- package/dist/manipulation-resistance/response-validator.d.ts.map +1 -0
- package/dist/manipulation-resistance/response-validator.js +186 -0
- package/dist/manipulation-resistance/response-validator.js.map +1 -0
- package/dist/manipulation-resistance/scope-expansion-detector.d.ts +33 -0
- package/dist/manipulation-resistance/scope-expansion-detector.d.ts.map +1 -0
- package/dist/manipulation-resistance/scope-expansion-detector.js +68 -0
- package/dist/manipulation-resistance/scope-expansion-detector.js.map +1 -0
- package/dist/oversight/approval-gates.d.ts +77 -0
- package/dist/oversight/approval-gates.d.ts.map +1 -0
- package/dist/oversight/approval-gates.js +133 -0
- package/dist/oversight/approval-gates.js.map +1 -0
- package/dist/oversight/authority-matrix.d.ts +39 -0
- package/dist/oversight/authority-matrix.d.ts.map +1 -0
- package/dist/oversight/authority-matrix.js +75 -0
- package/dist/oversight/authority-matrix.js.map +1 -0
- package/dist/oversight/cia-scoring.d.ts +56 -0
- package/dist/oversight/cia-scoring.d.ts.map +1 -0
- package/dist/oversight/cia-scoring.js +98 -0
- package/dist/oversight/cia-scoring.js.map +1 -0
- package/dist/oversight/escalation.d.ts +58 -0
- package/dist/oversight/escalation.d.ts.map +1 -0
- package/dist/oversight/escalation.js +97 -0
- package/dist/oversight/escalation.js.map +1 -0
- package/dist/oversight/index.d.ts +15 -0
- package/dist/oversight/index.d.ts.map +1 -0
- package/dist/oversight/index.js +15 -0
- package/dist/oversight/index.js.map +1 -0
- package/dist/roe/index.d.ts +3 -0
- package/dist/roe/index.d.ts.map +1 -0
- package/dist/roe/index.js +3 -0
- package/dist/roe/index.js.map +1 -0
- package/dist/roe/loader.d.ts +15 -0
- package/dist/roe/loader.d.ts.map +1 -0
- package/dist/roe/loader.js +56 -0
- package/dist/roe/loader.js.map +1 -0
- package/dist/roe/types.d.ts +738 -0
- package/dist/roe/types.d.ts.map +1 -0
- package/dist/roe/types.js +525 -0
- package/dist/roe/types.js.map +1 -0
- package/dist/runtime/chain.d.ts +60 -0
- package/dist/runtime/chain.d.ts.map +1 -0
- package/dist/runtime/chain.js +156 -0
- package/dist/runtime/chain.js.map +1 -0
- package/dist/runtime/events.d.ts +104 -0
- package/dist/runtime/events.d.ts.map +1 -0
- package/dist/runtime/events.js +68 -0
- package/dist/runtime/events.js.map +1 -0
- package/dist/runtime/hash.d.ts +16 -0
- package/dist/runtime/hash.d.ts.map +1 -0
- package/dist/runtime/hash.js +70 -0
- package/dist/runtime/hash.js.map +1 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/notifications.d.ts +24 -0
- package/dist/runtime/notifications.d.ts.map +1 -0
- package/dist/runtime/notifications.js +41 -0
- package/dist/runtime/notifications.js.map +1 -0
- package/dist/runtime/signals.d.ts +56 -0
- package/dist/runtime/signals.d.ts.map +1 -0
- package/dist/runtime/signals.js +72 -0
- package/dist/runtime/signals.js.map +1 -0
- package/dist/runtime/state.d.ts +88 -0
- package/dist/runtime/state.d.ts.map +1 -0
- package/dist/runtime/state.js +172 -0
- package/dist/runtime/state.js.map +1 -0
- package/dist/safety-controls/boundary-monitor.d.ts +45 -0
- package/dist/safety-controls/boundary-monitor.d.ts.map +1 -0
- package/dist/safety-controls/boundary-monitor.js +77 -0
- package/dist/safety-controls/boundary-monitor.js.map +1 -0
- package/dist/safety-controls/decision-timeout.d.ts +56 -0
- package/dist/safety-controls/decision-timeout.d.ts.map +1 -0
- package/dist/safety-controls/decision-timeout.js +67 -0
- package/dist/safety-controls/decision-timeout.js.map +1 -0
- package/dist/safety-controls/health-monitor.d.ts +61 -0
- package/dist/safety-controls/health-monitor.d.ts.map +1 -0
- package/dist/safety-controls/health-monitor.js +79 -0
- package/dist/safety-controls/health-monitor.js.map +1 -0
- package/dist/safety-controls/index.d.ts +13 -0
- package/dist/safety-controls/index.d.ts.map +1 -0
- package/dist/safety-controls/index.js +13 -0
- package/dist/safety-controls/index.js.map +1 -0
- package/dist/safety-controls/kill-switch.d.ts +45 -0
- package/dist/safety-controls/kill-switch.d.ts.map +1 -0
- package/dist/safety-controls/kill-switch.js +117 -0
- package/dist/safety-controls/kill-switch.js.map +1 -0
- package/dist/safety-controls/post-test-integrity.d.ts +51 -0
- package/dist/safety-controls/post-test-integrity.d.ts.map +1 -0
- package/dist/safety-controls/post-test-integrity.js +79 -0
- package/dist/safety-controls/post-test-integrity.js.map +1 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/sbom.cdx.json +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instruction-boundary enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Closes APTS-MR-001 (Instruction Boundary Enforcement Architecture).
|
|
5
|
+
*
|
|
6
|
+
* Design notes:
|
|
7
|
+
* - LLM-pentest wrappers each enforce their own internal instruction
|
|
8
|
+
* boundary. AEGIS-orchestrator adds a layer-2 boundary so that a
|
|
9
|
+
* compromised wrapper instruction cannot escape the engagement.
|
|
10
|
+
* - The orchestrator's instruction frame is: "execute pentest scope per
|
|
11
|
+
* RoE; do not act outside scope; do not interpret target-side
|
|
12
|
+
* responses as authority". A wrapper action that would breach this
|
|
13
|
+
* frame is rejected here, before exec.
|
|
14
|
+
* - Per-wrapper allowlist of action types: each wrapper declares which
|
|
15
|
+
* verbs it may issue (recon, scan, verify-finding, report). Anything
|
|
16
|
+
* outside that list is rejected as an instruction-boundary breach.
|
|
17
|
+
*/
|
|
18
|
+
import { validateTargetInScope } from '../roe/types.js';
|
|
19
|
+
/**
|
|
20
|
+
* Per-wrapper action-type allowlist. Each LLM-pentest wrapper declares
|
|
21
|
+
* what verb classes it may issue inside the orchestrator. Anything else
|
|
22
|
+
* is treated as an instruction-boundary breach.
|
|
23
|
+
*
|
|
24
|
+
* The unknown wrapper case (no allowlist entry) defaults to deny-all.
|
|
25
|
+
*/
|
|
26
|
+
export const WRAPPER_ACTION_ALLOWLIST = {
|
|
27
|
+
strix: ['recon', 'scan', 'verify-finding', 'report'],
|
|
28
|
+
ptai: ['scan', 'verify-finding', 'report'],
|
|
29
|
+
pentestswarm: ['recon', 'scan', 'verify-finding', 'report'],
|
|
30
|
+
subfinder: ['recon'],
|
|
31
|
+
// SAST scanners run on local files only — no target action allowlist;
|
|
32
|
+
// they bypass instruction-boundary because they do not invoke LLM
|
|
33
|
+
// reasoning over target data. Listed here for explicitness.
|
|
34
|
+
semgrep: ['scan'],
|
|
35
|
+
gitleaks: ['scan'],
|
|
36
|
+
trivy: ['scan'],
|
|
37
|
+
};
|
|
38
|
+
const URL_RE = /\bhttps?:\/\/[^\s<>"']+/giu;
|
|
39
|
+
/**
|
|
40
|
+
* Enforce the orchestrator-side instruction boundary on a wrapper's
|
|
41
|
+
* proposed action. Returns a ValidationDecision that the caller logs into
|
|
42
|
+
* the audit channel and gates the action on.
|
|
43
|
+
*
|
|
44
|
+
* Checks (in order):
|
|
45
|
+
* 1. Action type is in the per-wrapper allowlist (unknown wrapper → deny-all).
|
|
46
|
+
* 2. Action target is in RoE in_scope and not in out_of_scope.
|
|
47
|
+
* 3. Any URL embedded in payload is in RoE scope.
|
|
48
|
+
*/
|
|
49
|
+
export function enforceInstructionBoundary(wrapperName, action, roe) {
|
|
50
|
+
const allowedTypes = WRAPPER_ACTION_ALLOWLIST[wrapperName];
|
|
51
|
+
if (!allowedTypes) {
|
|
52
|
+
return {
|
|
53
|
+
allowed: false,
|
|
54
|
+
reason: `wrapper "${wrapperName}" has no action allowlist registered — instruction-boundary deny-all by default`,
|
|
55
|
+
apts_refs: ['APTS-MR-001'],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (!allowedTypes.includes(action.type)) {
|
|
59
|
+
return {
|
|
60
|
+
allowed: false,
|
|
61
|
+
reason: `wrapper "${wrapperName}" attempted action type "${action.type}" outside its allowlist (${allowedTypes.join(', ')})`,
|
|
62
|
+
apts_refs: ['APTS-MR-001'],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const targetCheck = validateTargetInScope(action.target, roe);
|
|
66
|
+
if (!targetCheck.allowed) {
|
|
67
|
+
return {
|
|
68
|
+
allowed: false,
|
|
69
|
+
reason: `wrapper "${wrapperName}" instruction-boundary breach: ${targetCheck.reason}`,
|
|
70
|
+
apts_refs: ['APTS-MR-001', ...(targetCheck.apts_refs ?? [])],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const payloadUrls = extractUrls(action.payload);
|
|
74
|
+
for (const url of payloadUrls) {
|
|
75
|
+
const urlCheck = validateTargetInScope(url, roe);
|
|
76
|
+
if (!urlCheck.allowed) {
|
|
77
|
+
return {
|
|
78
|
+
allowed: false,
|
|
79
|
+
reason: `wrapper "${wrapperName}" payload includes out-of-scope URL "${url}": ${urlCheck.reason}`,
|
|
80
|
+
apts_refs: ['APTS-MR-001', ...(urlCheck.apts_refs ?? [])],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
allowed: true,
|
|
86
|
+
reason: `wrapper "${wrapperName}" action "${action.type}" on ${action.target} within instruction boundary`,
|
|
87
|
+
apts_refs: ['APTS-MR-001'],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Recursively walk a JSON-shaped value and pull every http(s) URL out of
|
|
92
|
+
* any string leaf. Used to scope-validate URLs embedded in wrapper
|
|
93
|
+
* payloads (e.g., a callback URL inside a structured action payload).
|
|
94
|
+
*/
|
|
95
|
+
function extractUrls(value, acc = []) {
|
|
96
|
+
if (typeof value === 'string') {
|
|
97
|
+
const matches = value.match(URL_RE);
|
|
98
|
+
if (matches)
|
|
99
|
+
acc.push(...matches);
|
|
100
|
+
return acc;
|
|
101
|
+
}
|
|
102
|
+
if (Array.isArray(value)) {
|
|
103
|
+
for (const v of value)
|
|
104
|
+
extractUrls(v, acc);
|
|
105
|
+
return acc;
|
|
106
|
+
}
|
|
107
|
+
if (value !== null && typeof value === 'object') {
|
|
108
|
+
for (const v of Object.values(value)) {
|
|
109
|
+
extractUrls(v, acc);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return acc;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=instruction-boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instruction-boundary.js","sourceRoot":"","sources":["../../src/manipulation-resistance/instruction-boundary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Out-of-Band communication egress allowlist composition.
|
|
3
|
+
*
|
|
4
|
+
* Closes APTS-MR-011 (Out-of-Band Communication Prevention).
|
|
5
|
+
*
|
|
6
|
+
* Design notes:
|
|
7
|
+
* - LLM-pentest wrappers spawn as child processes with full network
|
|
8
|
+
* access by default. AEGIS composes a per-engagement egress
|
|
9
|
+
* allowlist from RoE in_scope hosts plus a fixed orchestrator
|
|
10
|
+
* essentials list (LLM provider APIs, wrapper update endpoints).
|
|
11
|
+
* - Allowlist is propagated to wrappers via the AEGIS_EGRESS_ALLOWLIST
|
|
12
|
+
* environment variable. Wrappers running in `--sandbox-mode docker`
|
|
13
|
+
* have it hard-enforced via `docker run --network=<name>`; in
|
|
14
|
+
* `--sandbox-mode none` the allowlist is a soft signal that
|
|
15
|
+
* cooperative wrappers consume.
|
|
16
|
+
* - The fixed orchestrator essentials list is intentionally short —
|
|
17
|
+
* anything not strictly required for the wrapper to function is
|
|
18
|
+
* omitted. Operators extend via RoE in_scope.
|
|
19
|
+
*/
|
|
20
|
+
import type { RoE } from '../roe/types.js';
|
|
21
|
+
/**
|
|
22
|
+
* Fixed orchestrator essentials — endpoints that any LLM-pentest
|
|
23
|
+
* wrapper must reach to function (LLM provider APIs). Operators
|
|
24
|
+
* cannot remove these via RoE; they can extend via in_scope. If a
|
|
25
|
+
* wrapper does not need LLM access (subfinder, semgrep), it should
|
|
26
|
+
* be invoked without these in its env.
|
|
27
|
+
*/
|
|
28
|
+
export declare const ORCHESTRATOR_ESSENTIALS: readonly string[];
|
|
29
|
+
export interface EgressAllowlist {
|
|
30
|
+
/** Hostnames + IP-literals + bare-domain entries (RoE in_scope.domains, ip_ranges, plus essentials). */
|
|
31
|
+
hosts: string[];
|
|
32
|
+
/** Comma-joined form for AEGIS_EGRESS_ALLOWLIST env var. */
|
|
33
|
+
envValue: string;
|
|
34
|
+
/** Whether LLM-provider essentials are included. False for non-LLM wrappers. */
|
|
35
|
+
includes_llm_essentials: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface ComposeEgressAllowlistOptions {
|
|
38
|
+
/** Whether the wrapper needs LLM provider access. False for subfinder/SAST. */
|
|
39
|
+
includeLlmEssentials?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Operator-defined extra entries — additional CDN / package mirror /
|
|
42
|
+
* artifact registry hosts the wrapper needs that are not in RoE
|
|
43
|
+
* in_scope (RoE in_scope is for *targets*, not infrastructure).
|
|
44
|
+
*/
|
|
45
|
+
extras?: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Compose the per-engagement egress allowlist from RoE in_scope plus
|
|
49
|
+
* orchestrator essentials. Returns the structured list and the
|
|
50
|
+
* AEGIS_EGRESS_ALLOWLIST env value.
|
|
51
|
+
*/
|
|
52
|
+
export declare function composeEgressAllowlist(roe: RoE, opts?: ComposeEgressAllowlistOptions): EgressAllowlist;
|
|
53
|
+
/**
|
|
54
|
+
* Helper to merge the AEGIS_EGRESS_ALLOWLIST env var into a wrapper's
|
|
55
|
+
* spawn env without clobbering the operator's own env. Returns a copy.
|
|
56
|
+
*/
|
|
57
|
+
export declare function withEgressEnv(baseEnv: NodeJS.ProcessEnv, allowlist: EgressAllowlist): NodeJS.ProcessEnv;
|
|
58
|
+
//# sourceMappingURL=oob-blocker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oob-blocker.d.ts","sourceRoot":"","sources":["../../src/manipulation-resistance/oob-blocker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAMnD,CAAC;AAEH,MAAM,WAAW,eAAe;IAC9B,wGAAwG;IACxG,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,6BAA6B;IAC5C,+EAA+E;IAC/E,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,GAAG,EACR,IAAI,GAAE,6BAAkC,GACvC,eAAe,CAwBjB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,SAAS,EAAE,eAAe,GACzB,MAAM,CAAC,UAAU,CAKnB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed orchestrator essentials — endpoints that any LLM-pentest
|
|
3
|
+
* wrapper must reach to function (LLM provider APIs). Operators
|
|
4
|
+
* cannot remove these via RoE; they can extend via in_scope. If a
|
|
5
|
+
* wrapper does not need LLM access (subfinder, semgrep), it should
|
|
6
|
+
* be invoked without these in its env.
|
|
7
|
+
*/
|
|
8
|
+
export const ORCHESTRATOR_ESSENTIALS = Object.freeze([
|
|
9
|
+
'api.openai.com',
|
|
10
|
+
'api.anthropic.com',
|
|
11
|
+
'generativelanguage.googleapis.com',
|
|
12
|
+
'api.cohere.ai',
|
|
13
|
+
'api.mistral.ai',
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* Compose the per-engagement egress allowlist from RoE in_scope plus
|
|
17
|
+
* orchestrator essentials. Returns the structured list and the
|
|
18
|
+
* AEGIS_EGRESS_ALLOWLIST env value.
|
|
19
|
+
*/
|
|
20
|
+
export function composeEgressAllowlist(roe, opts = {}) {
|
|
21
|
+
const { includeLlmEssentials = true, extras = [] } = opts;
|
|
22
|
+
const hosts = new Set();
|
|
23
|
+
for (const dom of roe.in_scope.domains) {
|
|
24
|
+
hosts.add(dom.pattern.toLowerCase());
|
|
25
|
+
if (dom.includeSubdomains && !dom.pattern.startsWith('*.')) {
|
|
26
|
+
hosts.add(`*.${dom.pattern.toLowerCase()}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const cidr of roe.in_scope.ip_ranges) {
|
|
30
|
+
hosts.add(cidr);
|
|
31
|
+
}
|
|
32
|
+
if (includeLlmEssentials) {
|
|
33
|
+
for (const e of ORCHESTRATOR_ESSENTIALS)
|
|
34
|
+
hosts.add(e);
|
|
35
|
+
}
|
|
36
|
+
for (const e of extras)
|
|
37
|
+
hosts.add(e.toLowerCase());
|
|
38
|
+
const sorted = [...hosts].sort();
|
|
39
|
+
return {
|
|
40
|
+
hosts: sorted,
|
|
41
|
+
envValue: sorted.join(','),
|
|
42
|
+
includes_llm_essentials: includeLlmEssentials,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Helper to merge the AEGIS_EGRESS_ALLOWLIST env var into a wrapper's
|
|
47
|
+
* spawn env without clobbering the operator's own env. Returns a copy.
|
|
48
|
+
*/
|
|
49
|
+
export function withEgressEnv(baseEnv, allowlist) {
|
|
50
|
+
return {
|
|
51
|
+
...baseEnv,
|
|
52
|
+
AEGIS_EGRESS_ALLOWLIST: allowlist.envValue,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=oob-blocker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oob-blocker.js","sourceRoot":"","sources":["../../src/manipulation-resistance/oob-blocker.ts"],"names":[],"mappings":"AAqBA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACtE,gBAAgB;IAChB,mBAAmB;IACnB,mCAAmC;IACnC,eAAe;IACf,gBAAgB;CACjB,CAAC,CAAC;AAsBH;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAAQ,EACR,OAAsC,EAAE;IAExC,MAAM,EAAE,oBAAoB,GAAG,IAAI,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhC,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACvC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QACrC,IAAI,GAAG,CAAC,iBAAiB,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3D,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC1C,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,oBAAoB,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,uBAAuB;YAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM;QAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,OAAO;QACL,KAAK,EAAE,MAAM;QACb,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QAC1B,uBAAuB,EAAE,oBAAoB;KAC9C,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,OAA0B,EAC1B,SAA0B;IAE1B,OAAO;QACL,GAAG,OAAO;QACV,sBAAsB,EAAE,SAAS,CAAC,QAAQ;KAC3C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface SafeFetchOptions extends Omit<RequestInit, 'redirect'> {
|
|
2
|
+
/**
|
|
3
|
+
* Maximum redirect chain length. After this many manual hops, the
|
|
4
|
+
* request is rejected even if every intermediate URL was in policy.
|
|
5
|
+
* Defaults to 5.
|
|
6
|
+
*/
|
|
7
|
+
maxRedirects?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Operator opt-in for testing against loopback targets (127.x.x.x, ::1).
|
|
10
|
+
* Default false. When true, loopback resolution is reclassified as
|
|
11
|
+
* 'public' so the fetch succeeds. Used by `aegis siege --allow-loopback`
|
|
12
|
+
* for legitimate local-pentest workflows. Always emits a warning.
|
|
13
|
+
* Does NOT bypass the other rejections (private-ip, link-local, cloud-metadata).
|
|
14
|
+
*/
|
|
15
|
+
allowLoopback?: boolean;
|
|
16
|
+
/** Override the DNS lookup for tests. Resolves a hostname to an IPv4. */
|
|
17
|
+
dnsLookup?: (hostname: string) => Promise<string>;
|
|
18
|
+
/** Override the underlying fetch — for tests. */
|
|
19
|
+
fetchImpl?: typeof fetch;
|
|
20
|
+
}
|
|
21
|
+
export interface SafeFetchRejection extends Error {
|
|
22
|
+
reason: SafeFetchRejectReason;
|
|
23
|
+
apts_refs: string[];
|
|
24
|
+
url?: string;
|
|
25
|
+
}
|
|
26
|
+
export type SafeFetchRejectReason = 'non-http-protocol' | 'private-ip' | 'loopback-ip' | 'link-local-ip' | 'cloud-metadata-ip' | 'dns-resolution-failed' | 'redirect-chain-too-long' | 'redirect-target-rejected';
|
|
27
|
+
/**
|
|
28
|
+
* SSRF-hardened HTTP client. Throws SafeFetchRejection on policy
|
|
29
|
+
* violation; otherwise delegates to the underlying fetch with manual
|
|
30
|
+
* redirect handling so each Location is re-validated.
|
|
31
|
+
*/
|
|
32
|
+
export declare function safeFetch(url: string, init?: SafeFetchOptions): Promise<Response>;
|
|
33
|
+
type IpClass = 'public' | 'private' | 'loopback' | 'link-local' | 'cloud-metadata';
|
|
34
|
+
/**
|
|
35
|
+
* Classify an IPv4 or IPv6 address. Conservative: anything not
|
|
36
|
+
* recognized as public (RFC 1918, link-local, loopback, multicast,
|
|
37
|
+
* cloud-metadata) is rejected.
|
|
38
|
+
*/
|
|
39
|
+
export declare function classifyIp(ip: string): IpClass;
|
|
40
|
+
/** Type guard for use by callers that want to differentiate policy rejections. */
|
|
41
|
+
export declare function isSafeFetchRejection(err: unknown): err is SafeFetchRejection;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=redirect-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redirect-policy.d.ts","sourceRoot":"","sources":["../../src/manipulation-resistance/redirect-policy.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC;IACrE;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yEAAyE;IACzE,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAmB,SAAQ,KAAK;IAC/C,MAAM,EAAE,qBAAqB,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,qBAAqB,GAC7B,mBAAmB,GACnB,YAAY,GACZ,aAAa,GACb,eAAe,GACf,mBAAmB,GACnB,uBAAuB,GACvB,yBAAyB,GACzB,0BAA0B,CAAC;AAE/B;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CAgCnB;AA6DD,KAAK,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,gBAAgB,CAAC;AAEnF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAI9C;AAoED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,kBAAkB,CAE5E"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe-fetch — orchestrator-side HTTP client with redirect, DNS-rebind,
|
|
3
|
+
* and SSRF defenses.
|
|
4
|
+
*
|
|
5
|
+
* Closes APTS-MR-007 (Redirect Following Vulnerability Testing) +
|
|
6
|
+
* APTS-MR-008 (DNS Rebinding Attack Resistance) + APTS-MR-009 (SSRF
|
|
7
|
+
* Vulnerability Testing in AI Pentest Framework).
|
|
8
|
+
*
|
|
9
|
+
* Design notes:
|
|
10
|
+
* - safeFetch is the orchestrator's HTTP egress surface for recon +
|
|
11
|
+
* finding-verification. It never follows redirects automatically;
|
|
12
|
+
* each Location header is re-validated against the same policy.
|
|
13
|
+
* - DNS rebinding defense: the resolved IP is pinned at first lookup;
|
|
14
|
+
* subsequent connections to the same host go via the pinned IP. We
|
|
15
|
+
* resolve the host once, validate the IP class, and short-circuit
|
|
16
|
+
* to a private/link-local/cloud-metadata reject if needed.
|
|
17
|
+
* - SSRF defense: requests against private (RFC 1918), link-local
|
|
18
|
+
* (169.254/16), loopback (127/8 + ::1), and cloud-metadata
|
|
19
|
+
* (169.254.169.254 + fd00:ec2::254) are rejected outright.
|
|
20
|
+
* - Non-HTTP(S) protocols are rejected — file://, gopher://,
|
|
21
|
+
* dict://, ftp:// have all been used in SSRF chains.
|
|
22
|
+
* - This is the orchestrator's own HTTP client. SAST-side
|
|
23
|
+
* ssrf-checker scans target source for these same patterns; the
|
|
24
|
+
* two defenses are independent.
|
|
25
|
+
*/
|
|
26
|
+
import { lookup } from 'node:dns/promises';
|
|
27
|
+
/**
|
|
28
|
+
* SSRF-hardened HTTP client. Throws SafeFetchRejection on policy
|
|
29
|
+
* violation; otherwise delegates to the underlying fetch with manual
|
|
30
|
+
* redirect handling so each Location is re-validated.
|
|
31
|
+
*/
|
|
32
|
+
export async function safeFetch(url, init = {}) {
|
|
33
|
+
const maxRedirects = init.maxRedirects ?? 5;
|
|
34
|
+
const dnsLookup = init.dnsLookup ?? defaultDnsLookup;
|
|
35
|
+
const fetchImpl = init.fetchImpl ?? fetch;
|
|
36
|
+
const allowLoopback = init.allowLoopback === true;
|
|
37
|
+
let currentUrl = url;
|
|
38
|
+
for (let hop = 0; hop <= maxRedirects; hop++) {
|
|
39
|
+
const policyCheck = await urlPolicyCheck(currentUrl, dnsLookup, allowLoopback);
|
|
40
|
+
if (!policyCheck.ok) {
|
|
41
|
+
throw makeRejection(policyCheck.reason, currentUrl);
|
|
42
|
+
}
|
|
43
|
+
const { dnsLookup: _omitDns, fetchImpl: _omitFetch, maxRedirects: _omitMax, ...rest } = init;
|
|
44
|
+
const response = await fetchImpl(currentUrl, { ...rest, redirect: 'manual' });
|
|
45
|
+
if (![301, 302, 303, 307, 308].includes(response.status)) {
|
|
46
|
+
return response;
|
|
47
|
+
}
|
|
48
|
+
const location = response.headers.get('location');
|
|
49
|
+
if (!location) {
|
|
50
|
+
// 3xx without Location header — treat as a final response so the
|
|
51
|
+
// caller observes the unusual shape.
|
|
52
|
+
return response;
|
|
53
|
+
}
|
|
54
|
+
if (hop === maxRedirects) {
|
|
55
|
+
throw makeRejection('redirect-chain-too-long', currentUrl);
|
|
56
|
+
}
|
|
57
|
+
currentUrl = new URL(location, currentUrl).toString();
|
|
58
|
+
}
|
|
59
|
+
// Unreachable — the loop either returns a Response or throws.
|
|
60
|
+
throw makeRejection('redirect-chain-too-long', currentUrl);
|
|
61
|
+
}
|
|
62
|
+
async function urlPolicyCheck(rawUrl, dnsLookup, allowLoopback) {
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = new URL(rawUrl);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return { ok: false, reason: 'non-http-protocol' };
|
|
69
|
+
}
|
|
70
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
71
|
+
return { ok: false, reason: 'non-http-protocol' };
|
|
72
|
+
}
|
|
73
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
74
|
+
// Bracketed IPv6 → strip brackets for classification
|
|
75
|
+
const hostKey = hostname.startsWith('[') && hostname.endsWith(']')
|
|
76
|
+
? hostname.slice(1, -1)
|
|
77
|
+
: hostname;
|
|
78
|
+
// Direct IP literal → classify without DNS lookup
|
|
79
|
+
if (isIpLiteral(hostKey)) {
|
|
80
|
+
const ipClass = classifyIp(hostKey);
|
|
81
|
+
if (ipClass === 'loopback' && allowLoopback) {
|
|
82
|
+
return { ok: true, resolvedIp: hostKey };
|
|
83
|
+
}
|
|
84
|
+
if (ipClass !== 'public')
|
|
85
|
+
return { ok: false, reason: ipReasonOf(ipClass) };
|
|
86
|
+
return { ok: true, resolvedIp: hostKey };
|
|
87
|
+
}
|
|
88
|
+
let resolvedIp;
|
|
89
|
+
try {
|
|
90
|
+
resolvedIp = await dnsLookup(hostKey);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return { ok: false, reason: 'dns-resolution-failed' };
|
|
94
|
+
}
|
|
95
|
+
const ipClass = classifyIp(resolvedIp);
|
|
96
|
+
if (ipClass === 'loopback' && allowLoopback) {
|
|
97
|
+
return { ok: true, resolvedIp };
|
|
98
|
+
}
|
|
99
|
+
if (ipClass !== 'public')
|
|
100
|
+
return { ok: false, reason: ipReasonOf(ipClass) };
|
|
101
|
+
return { ok: true, resolvedIp };
|
|
102
|
+
}
|
|
103
|
+
async function defaultDnsLookup(hostname) {
|
|
104
|
+
const result = await lookup(hostname, { family: 0 });
|
|
105
|
+
return result.address;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Classify an IPv4 or IPv6 address. Conservative: anything not
|
|
109
|
+
* recognized as public (RFC 1918, link-local, loopback, multicast,
|
|
110
|
+
* cloud-metadata) is rejected.
|
|
111
|
+
*/
|
|
112
|
+
export function classifyIp(ip) {
|
|
113
|
+
if (isIpv4(ip))
|
|
114
|
+
return classifyIpv4(ip);
|
|
115
|
+
if (isIpv6(ip))
|
|
116
|
+
return classifyIpv6(ip);
|
|
117
|
+
return 'private'; // unparseable → treat as non-public
|
|
118
|
+
}
|
|
119
|
+
function isIpLiteral(s) {
|
|
120
|
+
return isIpv4(s) || isIpv6(s);
|
|
121
|
+
}
|
|
122
|
+
function isIpv4(s) {
|
|
123
|
+
return /^(?:\d{1,3}\.){3}\d{1,3}$/.test(s);
|
|
124
|
+
}
|
|
125
|
+
function isIpv6(s) {
|
|
126
|
+
// Loose match — handles compressed forms; full RFC validation is
|
|
127
|
+
// not required since classifyIpv6 short-circuits to safe defaults
|
|
128
|
+
// on unrecognized shapes.
|
|
129
|
+
return /^[0-9a-fA-F:]+$/.test(s) && s.includes(':');
|
|
130
|
+
}
|
|
131
|
+
function classifyIpv4(ip) {
|
|
132
|
+
if (ip === '169.254.169.254')
|
|
133
|
+
return 'cloud-metadata';
|
|
134
|
+
const parts = ip.split('.').map((p) => Number.parseInt(p, 10));
|
|
135
|
+
if (parts.some((n) => !Number.isFinite(n) || n < 0 || n > 255))
|
|
136
|
+
return 'private';
|
|
137
|
+
const [a, b] = parts;
|
|
138
|
+
if (a === 127)
|
|
139
|
+
return 'loopback';
|
|
140
|
+
if (a === 10)
|
|
141
|
+
return 'private';
|
|
142
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
143
|
+
return 'private';
|
|
144
|
+
if (a === 192 && b === 168)
|
|
145
|
+
return 'private';
|
|
146
|
+
if (a === 169 && b === 254)
|
|
147
|
+
return 'link-local';
|
|
148
|
+
if (a === 0)
|
|
149
|
+
return 'private';
|
|
150
|
+
if (a >= 224)
|
|
151
|
+
return 'private'; // multicast/reserved
|
|
152
|
+
return 'public';
|
|
153
|
+
}
|
|
154
|
+
function classifyIpv6(raw) {
|
|
155
|
+
const ip = raw.toLowerCase();
|
|
156
|
+
if (ip === '::1')
|
|
157
|
+
return 'loopback';
|
|
158
|
+
if (ip === '::' || ip === '::0')
|
|
159
|
+
return 'private';
|
|
160
|
+
if (ip.startsWith('fe80:'))
|
|
161
|
+
return 'link-local';
|
|
162
|
+
// Unique-local (fc00::/7)
|
|
163
|
+
if (/^f[cd][0-9a-f]{2}:/.test(ip))
|
|
164
|
+
return 'private';
|
|
165
|
+
// Cloud metadata (AWS IMDS over IPv6)
|
|
166
|
+
if (ip === 'fd00:ec2::254')
|
|
167
|
+
return 'cloud-metadata';
|
|
168
|
+
if (ip.startsWith('ff'))
|
|
169
|
+
return 'private'; // multicast
|
|
170
|
+
return 'public';
|
|
171
|
+
}
|
|
172
|
+
function ipReasonOf(c) {
|
|
173
|
+
switch (c) {
|
|
174
|
+
case 'private':
|
|
175
|
+
return 'private-ip';
|
|
176
|
+
case 'loopback':
|
|
177
|
+
return 'loopback-ip';
|
|
178
|
+
case 'link-local':
|
|
179
|
+
return 'link-local-ip';
|
|
180
|
+
case 'cloud-metadata':
|
|
181
|
+
return 'cloud-metadata-ip';
|
|
182
|
+
default:
|
|
183
|
+
return 'private-ip';
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function makeRejection(reason, url) {
|
|
187
|
+
const err = new Error(`safeFetch rejected: ${reason} (${url})`);
|
|
188
|
+
err.reason = reason;
|
|
189
|
+
err.url = url;
|
|
190
|
+
err.apts_refs = ['APTS-MR-007', 'APTS-MR-008', 'APTS-MR-009'];
|
|
191
|
+
return err;
|
|
192
|
+
}
|
|
193
|
+
/** Type guard for use by callers that want to differentiate policy rejections. */
|
|
194
|
+
export function isSafeFetchRejection(err) {
|
|
195
|
+
return err instanceof Error && typeof err.reason === 'string';
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=redirect-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redirect-policy.js","sourceRoot":"","sources":["../../src/manipulation-resistance/redirect-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface ResponseValidation {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
cleaned?: unknown;
|
|
4
|
+
reason?: string;
|
|
5
|
+
apts_refs?: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Validate + sanitize a wrapper's structured output before propagating
|
|
9
|
+
* to Findings. Returns { ok: true, cleaned } on success or
|
|
10
|
+
* { ok: false, reason } with an APTS reference on rejection.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateWrapperResponse(wrapperName: string, raw: unknown): ResponseValidation;
|
|
13
|
+
export type AuthorityClaim = 'none' | 'admin' | 'root' | 'rce' | 'reverse-shell';
|
|
14
|
+
export interface AuthorityClaimResult {
|
|
15
|
+
claim: AuthorityClaim;
|
|
16
|
+
rationale: string;
|
|
17
|
+
matched_phrase?: string;
|
|
18
|
+
suggested_action: 'pass' | 'verify' | 'reject';
|
|
19
|
+
apts_refs: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Detect authority claims in finding text. Returns the strongest match
|
|
23
|
+
* (RCE > root > admin) or `claim: 'none'` if no pattern fires.
|
|
24
|
+
*
|
|
25
|
+
* Suggested-action policy:
|
|
26
|
+
* - 'reject' for RCE / reverse-shell — these are high-impact assertions
|
|
27
|
+
* that AEGIS must not propagate without operator confirmation.
|
|
28
|
+
* - 'verify' for root/admin — pause for operator confirmation via
|
|
29
|
+
* SIGUSR1 resume, then proceed if confirmed.
|
|
30
|
+
* - 'pass' when no claim detected.
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectAuthorityClaim(findingText: string): AuthorityClaimResult;
|
|
33
|
+
//# sourceMappingURL=response-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-validator.d.ts","sourceRoot":"","sources":["../../src/manipulation-resistance/response-validator.ts"],"names":[],"mappings":"AA0FA,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,OAAO,GACX,kBAAkB,CAyBpB;AA2BD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,eAAe,CAAC;AAEjF,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC/C,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AA4BD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,oBAAoB,CAmB9E"}
|