@integsec/agentic-pentest-proxy 0.1.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.
Files changed (89) hide show
  1. package/README.md +731 -0
  2. package/dist/bin/integsec-agentic-pentest-proxy.d.ts +3 -0
  3. package/dist/bin/integsec-agentic-pentest-proxy.d.ts.map +1 -0
  4. package/dist/bin/integsec-agentic-pentest-proxy.js +51 -0
  5. package/dist/bin/integsec-agentic-pentest-proxy.js.map +1 -0
  6. package/dist/src/audit/azure-monitor.d.ts +22 -0
  7. package/dist/src/audit/azure-monitor.d.ts.map +1 -0
  8. package/dist/src/audit/azure-monitor.js +62 -0
  9. package/dist/src/audit/azure-monitor.js.map +1 -0
  10. package/dist/src/audit/cloudwatch.d.ts +20 -0
  11. package/dist/src/audit/cloudwatch.d.ts.map +1 -0
  12. package/dist/src/audit/cloudwatch.js +89 -0
  13. package/dist/src/audit/cloudwatch.js.map +1 -0
  14. package/dist/src/audit/gcp-logging.d.ts +23 -0
  15. package/dist/src/audit/gcp-logging.d.ts.map +1 -0
  16. package/dist/src/audit/gcp-logging.js +70 -0
  17. package/dist/src/audit/gcp-logging.js.map +1 -0
  18. package/dist/src/audit/index.d.ts +49 -0
  19. package/dist/src/audit/index.d.ts.map +1 -0
  20. package/dist/src/audit/index.js +79 -0
  21. package/dist/src/audit/index.js.map +1 -0
  22. package/dist/src/audit/local.d.ts +25 -0
  23. package/dist/src/audit/local.d.ts.map +1 -0
  24. package/dist/src/audit/local.js +51 -0
  25. package/dist/src/audit/local.js.map +1 -0
  26. package/dist/src/config.d.ts +25 -0
  27. package/dist/src/config.d.ts.map +1 -0
  28. package/dist/src/config.js +26 -0
  29. package/dist/src/config.js.map +1 -0
  30. package/dist/src/dns-resolver.d.ts +21 -0
  31. package/dist/src/dns-resolver.d.ts.map +1 -0
  32. package/dist/src/dns-resolver.js +68 -0
  33. package/dist/src/dns-resolver.js.map +1 -0
  34. package/dist/src/domain-matcher.d.ts +35 -0
  35. package/dist/src/domain-matcher.d.ts.map +1 -0
  36. package/dist/src/domain-matcher.js +97 -0
  37. package/dist/src/domain-matcher.js.map +1 -0
  38. package/dist/src/extractor.d.ts +30 -0
  39. package/dist/src/extractor.d.ts.map +1 -0
  40. package/dist/src/extractor.js +176 -0
  41. package/dist/src/extractor.js.map +1 -0
  42. package/dist/src/index.d.ts +8 -0
  43. package/dist/src/index.d.ts.map +1 -0
  44. package/dist/src/index.js +7 -0
  45. package/dist/src/index.js.map +1 -0
  46. package/dist/src/ip-matcher.d.ts +38 -0
  47. package/dist/src/ip-matcher.d.ts.map +1 -0
  48. package/dist/src/ip-matcher.js +128 -0
  49. package/dist/src/ip-matcher.js.map +1 -0
  50. package/dist/src/manifest-schema.d.ts +77 -0
  51. package/dist/src/manifest-schema.d.ts.map +1 -0
  52. package/dist/src/manifest-schema.js +34 -0
  53. package/dist/src/manifest-schema.js.map +1 -0
  54. package/dist/src/manifest.d.ts +3 -0
  55. package/dist/src/manifest.d.ts.map +1 -0
  56. package/dist/src/manifest.js +115 -0
  57. package/dist/src/manifest.js.map +1 -0
  58. package/dist/src/proxy.d.ts +16 -0
  59. package/dist/src/proxy.d.ts.map +1 -0
  60. package/dist/src/proxy.js +72 -0
  61. package/dist/src/proxy.js.map +1 -0
  62. package/dist/src/sanitizer.d.ts +19 -0
  63. package/dist/src/sanitizer.d.ts.map +1 -0
  64. package/dist/src/sanitizer.js +68 -0
  65. package/dist/src/sanitizer.js.map +1 -0
  66. package/dist/src/technique-checker.d.ts +50 -0
  67. package/dist/src/technique-checker.d.ts.map +1 -0
  68. package/dist/src/technique-checker.js +110 -0
  69. package/dist/src/technique-checker.js.map +1 -0
  70. package/dist/src/transports/http.d.ts +3 -0
  71. package/dist/src/transports/http.d.ts.map +1 -0
  72. package/dist/src/transports/http.js +67 -0
  73. package/dist/src/transports/http.js.map +1 -0
  74. package/dist/src/transports/stdio.d.ts +3 -0
  75. package/dist/src/transports/stdio.d.ts.map +1 -0
  76. package/dist/src/transports/stdio.js +50 -0
  77. package/dist/src/transports/stdio.js.map +1 -0
  78. package/dist/src/types.d.ts +43 -0
  79. package/dist/src/types.d.ts.map +1 -0
  80. package/dist/src/types.js +2 -0
  81. package/dist/src/types.js.map +1 -0
  82. package/dist/src/validator.d.ts +54 -0
  83. package/dist/src/validator.d.ts.map +1 -0
  84. package/dist/src/validator.js +200 -0
  85. package/dist/src/validator.js.map +1 -0
  86. package/examples/claude-desktop-config.json +15 -0
  87. package/examples/scope-manifest.json +18 -0
  88. package/examples/technique-map.json +10 -0
  89. package/package.json +58 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * ip-matcher.ts
3
+ *
4
+ * IP / CIDR matching utilities for scope enforcement.
5
+ * Used to determine whether a target IP is in-scope, private, or a cloud
6
+ * metadata endpoint. This is security-critical code: every edge case matters.
7
+ */
8
+ import IPCIDR from "ip-cidr";
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ // Helpers
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+ /**
13
+ * Return true if `address` looks like an IPv6 string (contains a colon).
14
+ * This is intentionally lightweight — full validation is delegated to ip-cidr.
15
+ */
16
+ function looksLikeIPv6(address) {
17
+ return address.includes(":");
18
+ }
19
+ /**
20
+ * Normalise a range string so it always contains a `/`.
21
+ *
22
+ * Rules:
23
+ * - If it already contains a `/` it is returned as-is.
24
+ * - If it looks like an IPv6 bare address, append `/128`.
25
+ * - Otherwise append `/32` (treat as an IPv4 exact-host CIDR).
26
+ *
27
+ * We intentionally do NOT strip whitespace here — a range with leading or
28
+ * trailing whitespace is invalid and should propagate to ip-cidr which will
29
+ * throw and be caught.
30
+ */
31
+ function normaliseCidr(range) {
32
+ if (range.includes("/")) {
33
+ return range;
34
+ }
35
+ return looksLikeIPv6(range) ? `${range}/128` : `${range}/32`;
36
+ }
37
+ // ─────────────────────────────────────────────────────────────────────────────
38
+ // Public API
39
+ // ─────────────────────────────────────────────────────────────────────────────
40
+ /**
41
+ * Check whether `ip` falls within the CIDR `cidr`.
42
+ *
43
+ * - Bare IPs (no slash) are treated as an exact-host match (/32 or /128).
44
+ * - Returns `false` for any invalid input rather than throwing.
45
+ * - An IPv4 address will never match an IPv6 range and vice-versa.
46
+ */
47
+ export function isIpInCidr(ip, cidr) {
48
+ if (!ip || !cidr) {
49
+ return false;
50
+ }
51
+ try {
52
+ const normalisedCidr = normaliseCidr(cidr);
53
+ if (!IPCIDR.isValidCIDR(normalisedCidr)) {
54
+ return false;
55
+ }
56
+ const range = new IPCIDR(normalisedCidr);
57
+ return range.contains(ip);
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ /**
64
+ * Check whether `ip` falls within any of the provided CIDR/IP `ranges`.
65
+ *
66
+ * - An empty `ranges` array always returns `false`.
67
+ * - Invalid individual ranges are silently skipped.
68
+ * - Returns `false` for any invalid `ip`.
69
+ */
70
+ export function isIpInAnyRange(ip, ranges) {
71
+ for (const range of ranges) {
72
+ if (isIpInCidr(ip, range)) {
73
+ return true;
74
+ }
75
+ }
76
+ return false;
77
+ }
78
+ /**
79
+ * RFC 1918 / link-local private address ranges:
80
+ * 10.0.0.0/8 — Class A private
81
+ * 172.16.0.0/12 — Class B private
82
+ * 192.168.0.0/16 — Class C private
83
+ * 169.254.0.0/16 — Link-local (APIPA)
84
+ */
85
+ const RFC1918_RANGES = [
86
+ "10.0.0.0/8",
87
+ "172.16.0.0/12",
88
+ "192.168.0.0/16",
89
+ "169.254.0.0/16",
90
+ ];
91
+ /**
92
+ * Return true if `ip` is a private/link-local IPv4 address as defined by
93
+ * RFC 1918 and RFC 3927 (169.254.0.0/16).
94
+ *
95
+ * Returns `false` for invalid inputs and for IPv6 addresses.
96
+ */
97
+ export function isRfc1918(ip) {
98
+ if (!ip) {
99
+ return false;
100
+ }
101
+ // Quick guard: IPv6 addresses are never RFC 1918
102
+ if (looksLikeIPv6(ip)) {
103
+ return false;
104
+ }
105
+ return isIpInAnyRange(ip, RFC1918_RANGES);
106
+ }
107
+ /**
108
+ * Well-known cloud instance metadata endpoints.
109
+ * 169.254.169.254 — AWS, Azure, GCP (IPv4)
110
+ * fd00:ec2::254 — AWS (IPv6)
111
+ */
112
+ const CLOUD_METADATA_IPS = [
113
+ "169.254.169.254",
114
+ "fd00:ec2::254",
115
+ ];
116
+ /**
117
+ * Return true if `ip` is a known cloud metadata endpoint.
118
+ *
119
+ * Performs exact-host matching only — no subnet expansion.
120
+ * Returns `false` for invalid inputs.
121
+ */
122
+ export function isCloudMetadata(ip) {
123
+ if (!ip) {
124
+ return false;
125
+ }
126
+ return isIpInAnyRange(ip, CLOUD_METADATA_IPS);
127
+ }
128
+ //# sourceMappingURL=ip-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip-matcher.js","sourceRoot":"","sources":["../../src/ip-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF;;;GAGG;AACH,SAAS,aAAa,CAAC,OAAe;IACpC,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC;AAC/D,CAAC;AAED,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,EAAU,EAAE,IAAY;IACjD,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAE3C,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC;YACxC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,cAAc,CAAC,CAAC;QACzC,OAAO,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,MAAgB;IACzD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,cAAc,GAAa;IAC/B,YAAY;IACZ,eAAe;IACf,gBAAgB;IAChB,gBAAgB;CACjB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO,KAAK,CAAC;IACf,CAAC;IACD,iDAAiD;IACjD,IAAI,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,cAAc,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,kBAAkB,GAAa;IACnC,iBAAiB;IACjB,eAAe;CAChB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,cAAc,CAAC,EAAE,EAAE,kBAAkB,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+ export declare const ScopeManifestSchema: z.ZodObject<{
3
+ engagement_id: z.ZodString;
4
+ client: z.ZodString;
5
+ operator: z.ZodString;
6
+ authorized_targets: z.ZodObject<{
7
+ ip_ranges: z.ZodArray<z.ZodString, "many">;
8
+ domains: z.ZodArray<z.ZodString, "many">;
9
+ urls: z.ZodArray<z.ZodString, "many">;
10
+ cloud_accounts: z.ZodArray<z.ZodString, "many">;
11
+ }, "strip", z.ZodTypeAny, {
12
+ ip_ranges: string[];
13
+ domains: string[];
14
+ urls: string[];
15
+ cloud_accounts: string[];
16
+ }, {
17
+ ip_ranges: string[];
18
+ domains: string[];
19
+ urls: string[];
20
+ cloud_accounts: string[];
21
+ }>;
22
+ excluded_targets: z.ZodArray<z.ZodString, "many">;
23
+ authorized_techniques: z.ZodArray<z.ZodString, "many">;
24
+ excluded_techniques: z.ZodArray<z.ZodString, "many">;
25
+ engagement_window: z.ZodObject<{
26
+ start: z.ZodEffects<z.ZodString, string, string>;
27
+ end: z.ZodEffects<z.ZodString, string, string>;
28
+ }, "strip", z.ZodTypeAny, {
29
+ start: string;
30
+ end: string;
31
+ }, {
32
+ start: string;
33
+ end: string;
34
+ }>;
35
+ }, "strip", z.ZodTypeAny, {
36
+ engagement_id: string;
37
+ client: string;
38
+ operator: string;
39
+ authorized_targets: {
40
+ ip_ranges: string[];
41
+ domains: string[];
42
+ urls: string[];
43
+ cloud_accounts: string[];
44
+ };
45
+ excluded_targets: string[];
46
+ authorized_techniques: string[];
47
+ excluded_techniques: string[];
48
+ engagement_window: {
49
+ start: string;
50
+ end: string;
51
+ };
52
+ }, {
53
+ engagement_id: string;
54
+ client: string;
55
+ operator: string;
56
+ authorized_targets: {
57
+ ip_ranges: string[];
58
+ domains: string[];
59
+ urls: string[];
60
+ cloud_accounts: string[];
61
+ };
62
+ excluded_targets: string[];
63
+ authorized_techniques: string[];
64
+ excluded_techniques: string[];
65
+ engagement_window: {
66
+ start: string;
67
+ end: string;
68
+ };
69
+ }>;
70
+ /**
71
+ * Inferred TypeScript type from the Zod schema.
72
+ * Use this instead of the hand-written `ScopeManifest` interface from
73
+ * types.ts wherever you want the compiler to track schema changes
74
+ * automatically.
75
+ */
76
+ export type ScopeManifest = z.infer<typeof ScopeManifestSchema>;
77
+ //# sourceMappingURL=manifest-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-schema.d.ts","sourceRoot":"","sources":["../../src/manifest-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiBxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiB9B,CAAC;AAEH;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Validates that a string is a parseable ISO 8601 date/datetime.
4
+ *
5
+ * Date.parse("") returns NaN in all major JS engines, so empty strings are
6
+ * rejected. Strings like "not-a-date" also produce NaN and are rejected.
7
+ * Valid variants such as "2026-03-26", "2026-03-26T08:00:00Z", and
8
+ * "2026-03-26T08:00:00+05:30" all parse to finite numbers and pass.
9
+ */
10
+ const isoDateString = z
11
+ .string()
12
+ .min(1, { message: "Date string must not be empty" })
13
+ .refine((val) => !isNaN(Date.parse(val)), {
14
+ message: "Must be a valid ISO 8601 date string",
15
+ });
16
+ export const ScopeManifestSchema = z.object({
17
+ engagement_id: z.string().min(1, { message: "engagement_id must not be empty" }),
18
+ client: z.string().min(1, { message: "client must not be empty" }),
19
+ operator: z.string().min(1, { message: "operator must not be empty" }),
20
+ authorized_targets: z.object({
21
+ ip_ranges: z.array(z.string()),
22
+ domains: z.array(z.string()),
23
+ urls: z.array(z.string()),
24
+ cloud_accounts: z.array(z.string()),
25
+ }),
26
+ excluded_targets: z.array(z.string()),
27
+ authorized_techniques: z.array(z.string()),
28
+ excluded_techniques: z.array(z.string()),
29
+ engagement_window: z.object({
30
+ start: isoDateString,
31
+ end: isoDateString,
32
+ }),
33
+ });
34
+ //# sourceMappingURL=manifest-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-schema.js","sourceRoot":"","sources":["../../src/manifest-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC;KACpD,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE;IACxC,OAAO,EAAE,sCAAsC;CAChD,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;IAChF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC;IAClE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC;IACtE,kBAAkB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC3B,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC5B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACzB,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACpC,CAAC;IACF,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACrC,qBAAqB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC1C,mBAAmB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACxC,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1B,KAAK,EAAE,aAAa;QACpB,GAAG,EAAE,aAAa;KACnB,CAAC;CACH,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ScopeManifest } from "./manifest-schema.js";
2
+ export declare function loadManifest(): Promise<ScopeManifest>;
3
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/manifest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAqB1D,wBAAsB,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC,CA2G3D"}
@@ -0,0 +1,115 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { ScopeManifestSchema } from "./manifest-schema.js";
3
+ function parseAndValidate(json, source) {
4
+ let parsed;
5
+ try {
6
+ parsed = JSON.parse(json);
7
+ }
8
+ catch (err) {
9
+ throw new Error(`Failed to parse manifest JSON from ${source}: ${err.message}`);
10
+ }
11
+ const result = ScopeManifestSchema.safeParse(parsed);
12
+ if (!result.success) {
13
+ const issues = result.error.issues
14
+ .map((i) => `${i.path.join(".")}: ${i.message}`)
15
+ .join("; ");
16
+ throw new Error(`Manifest from ${source} failed schema validation: ${issues}`);
17
+ }
18
+ return result.data;
19
+ }
20
+ export async function loadManifest() {
21
+ // 1. Inline JSON env var — highest priority
22
+ const inlineJson = process.env.SCOPE_MANIFEST_JSON;
23
+ if (inlineJson) {
24
+ return parseAndValidate(inlineJson, "SCOPE_MANIFEST_JSON");
25
+ }
26
+ // 2. File path env var
27
+ const filePath = process.env.SCOPE_MANIFEST_PATH;
28
+ if (filePath) {
29
+ let fileContents;
30
+ try {
31
+ fileContents = readFileSync(filePath, "utf-8");
32
+ }
33
+ catch (err) {
34
+ throw new Error(`Failed to read manifest file at "${filePath}": ${err.message}`);
35
+ }
36
+ return parseAndValidate(fileContents, `file:${filePath}`);
37
+ }
38
+ // 3. AWS Secrets Manager
39
+ const secretArn = process.env.SCOPE_MANIFEST_SECRET_ARN;
40
+ if (secretArn) {
41
+ try {
42
+ const awsMod = "@aws-sdk/client-secrets-manager";
43
+ const { SecretsManagerClient, GetSecretValueCommand } = await import(
44
+ /* @vite-ignore */ awsMod);
45
+ const client = new SecretsManagerClient({});
46
+ const response = await client.send(new GetSecretValueCommand({ SecretId: secretArn }));
47
+ const secretString = response.SecretString;
48
+ if (!secretString) {
49
+ throw new Error("Secret value is empty or binary (binary secrets are not supported)");
50
+ }
51
+ return parseAndValidate(secretString, `AWS Secrets Manager ARN: ${secretArn}`);
52
+ }
53
+ catch (err) {
54
+ if (err.code === "ERR_MODULE_NOT_FOUND") {
55
+ throw new Error("SCOPE_MANIFEST_SECRET_ARN is set but @aws-sdk/client-secrets-manager is not installed. " +
56
+ "Install it with: npm install @aws-sdk/client-secrets-manager");
57
+ }
58
+ throw err;
59
+ }
60
+ }
61
+ // 4. Azure Key Vault
62
+ const keyvaultUri = process.env.SCOPE_MANIFEST_KEYVAULT_URI;
63
+ if (keyvaultUri) {
64
+ try {
65
+ const kvMod = "@azure/keyvault-secrets";
66
+ const idMod = "@azure/identity";
67
+ const { SecretClient } = await import(/* @vite-ignore */ kvMod);
68
+ const { DefaultAzureCredential } = await import(/* @vite-ignore */ idMod);
69
+ const credential = new DefaultAzureCredential();
70
+ const client = new SecretClient(keyvaultUri, credential);
71
+ // The URI may be in the form https://vault.azure.net/secrets/secretName
72
+ // or we use a convention: treat the env var as the vault base URI
73
+ // and look for secret named "scope-manifest"
74
+ const secretName = process.env.SCOPE_MANIFEST_KEYVAULT_SECRET_NAME || "scope-manifest";
75
+ const secret = await client.getSecret(secretName);
76
+ if (!secret.value) {
77
+ throw new Error(`Secret "${secretName}" in Key Vault has no value`);
78
+ }
79
+ return parseAndValidate(secret.value, `Azure Key Vault: ${keyvaultUri}/${secretName}`);
80
+ }
81
+ catch (err) {
82
+ if (err.code === "ERR_MODULE_NOT_FOUND") {
83
+ throw new Error("SCOPE_MANIFEST_KEYVAULT_URI is set but @azure/keyvault-secrets or @azure/identity is not installed. " +
84
+ "Install them with: npm install @azure/keyvault-secrets @azure/identity");
85
+ }
86
+ throw err;
87
+ }
88
+ }
89
+ // 5. GCP Secret Manager
90
+ const gcpSecret = process.env.SCOPE_MANIFEST_GCP_SECRET;
91
+ if (gcpSecret) {
92
+ try {
93
+ const gcpMod = "@google-cloud/secret-manager";
94
+ const { SecretManagerServiceClient } = await import(/* @vite-ignore */ gcpMod);
95
+ const client = new SecretManagerServiceClient();
96
+ const [version] = await client.accessSecretVersion({ name: gcpSecret });
97
+ const payload = version.payload?.data;
98
+ if (!payload) {
99
+ throw new Error(`GCP secret "${gcpSecret}" has no payload data`);
100
+ }
101
+ const secretString = typeof payload === "string" ? payload : Buffer.from(payload).toString("utf-8");
102
+ return parseAndValidate(secretString, `GCP Secret Manager: ${gcpSecret}`);
103
+ }
104
+ catch (err) {
105
+ if (err.code === "ERR_MODULE_NOT_FOUND") {
106
+ throw new Error("SCOPE_MANIFEST_GCP_SECRET is set but @google-cloud/secret-manager is not installed. " +
107
+ "Install it with: npm install @google-cloud/secret-manager");
108
+ }
109
+ throw err;
110
+ }
111
+ }
112
+ throw new Error("No scope manifest source configured. Set one of: SCOPE_MANIFEST_JSON, SCOPE_MANIFEST_PATH, " +
113
+ "SCOPE_MANIFEST_SECRET_ARN, SCOPE_MANIFEST_KEYVAULT_URI, or SCOPE_MANIFEST_GCP_SECRET");
114
+ }
115
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { ScopeManifest } from "./types.js";
2
+ import { AuditLogger } from "./audit/index.js";
3
+ import type { ProxyConfig } from "./config.js";
4
+ export declare class ScopeEnforcementProxy {
5
+ private validator;
6
+ private auditLogger;
7
+ private manifest;
8
+ private config;
9
+ constructor(manifest: ScopeManifest, config: ProxyConfig);
10
+ getAuditLogger(): AuditLogger;
11
+ handleMessage(message: unknown): Promise<{
12
+ forward: boolean;
13
+ response?: unknown;
14
+ }>;
15
+ }
16
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,YAAY,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAI/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,qBAAa,qBAAqB;IAChC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,MAAM,CAAc;gBAEhB,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW;IASxD,cAAc,IAAI,WAAW;IAEvB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAmDzF"}
@@ -0,0 +1,72 @@
1
+ import { ScopeValidator } from "./validator.js";
2
+ import { AuditLogger } from "./audit/index.js";
3
+ import { sanitizeParams } from "./sanitizer.js";
4
+ import { DnsResolver } from "./dns-resolver.js";
5
+ import { TechniqueChecker } from "./technique-checker.js";
6
+ export class ScopeEnforcementProxy {
7
+ validator;
8
+ auditLogger;
9
+ manifest;
10
+ config;
11
+ constructor(manifest, config) {
12
+ this.manifest = manifest;
13
+ this.config = config;
14
+ const dnsResolver = new DnsResolver(config.dnsCacheTtl);
15
+ const techniqueChecker = new TechniqueChecker();
16
+ this.validator = new ScopeValidator(manifest, dnsResolver, techniqueChecker);
17
+ this.auditLogger = new AuditLogger(config.auditLogPath);
18
+ }
19
+ getAuditLogger() { return this.auditLogger; }
20
+ async handleMessage(message) {
21
+ // Only intercept tools/call JSON-RPC requests
22
+ if (!isToolCallRequest(message)) {
23
+ return { forward: true };
24
+ }
25
+ const { id, params } = message;
26
+ const toolName = params?.name ?? "unknown";
27
+ const toolParams = params?.arguments ?? {};
28
+ const result = await this.validator.validate(toolName, toolParams);
29
+ // Build audit entry
30
+ const entry = {
31
+ timestamp: new Date().toISOString(),
32
+ engagement_id: this.manifest.engagement_id,
33
+ client: this.manifest.client,
34
+ operator: this.manifest.operator,
35
+ tool_name: toolName,
36
+ tool_parameters: sanitizeParams(toolParams),
37
+ extracted_target: result.extractedTarget ?? null,
38
+ resolved_ips: result.resolvedIps ?? [],
39
+ decision: result.decision,
40
+ decision_reason: result.reason,
41
+ matched_scope_item: result.matchedScopeItem ?? null,
42
+ duration_ms: result.durationMs,
43
+ proxy_version: this.config.proxyVersion,
44
+ };
45
+ // Log asynchronously — never block on logging failure
46
+ this.auditLogger.log(entry).catch((err) => {
47
+ console.error(`[integsec-agentic-pentest-proxy] Failed to log audit entry: ${err.message}`);
48
+ });
49
+ // If blocked, return error response
50
+ if (result.decision.startsWith("BLOCKED")) {
51
+ return {
52
+ forward: false,
53
+ response: {
54
+ jsonrpc: "2.0",
55
+ id,
56
+ error: {
57
+ code: -32001,
58
+ message: `SCOPE_VIOLATION: ${result.reason}. Decision: ${result.decision}.`,
59
+ },
60
+ },
61
+ };
62
+ }
63
+ return { forward: true };
64
+ }
65
+ }
66
+ function isToolCallRequest(msg) {
67
+ if (typeof msg !== "object" || msg === null)
68
+ return false;
69
+ const obj = msg;
70
+ return obj.method === "tools/call" && obj.jsonrpc === "2.0";
71
+ }
72
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Parameter sanitizer — redacts sensitive values from tool parameter objects
3
+ * before they are logged or forwarded.
4
+ *
5
+ * Sensitive key names are matched case-insensitively against an exact list.
6
+ * Nested objects and arrays are recursed into so that deeply nested credentials
7
+ * are also redacted. The original object is never mutated; a new copy is
8
+ * always returned.
9
+ */
10
+ /**
11
+ * Return a deep copy of `params` with any values whose key matches the
12
+ * sensitive-key list replaced by `"[REDACTED]"`.
13
+ *
14
+ * Matching is case-insensitive and exact (no prefix/suffix matching).
15
+ * Nested objects and arrays of objects are recursed into.
16
+ * The original `params` object is never mutated.
17
+ */
18
+ export declare function sanitizeParams(params: Record<string, unknown>): Record<string, unknown>;
19
+ //# sourceMappingURL=sanitizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../../src/sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0DH;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Parameter sanitizer — redacts sensitive values from tool parameter objects
3
+ * before they are logged or forwarded.
4
+ *
5
+ * Sensitive key names are matched case-insensitively against an exact list.
6
+ * Nested objects and arrays are recursed into so that deeply nested credentials
7
+ * are also redacted. The original object is never mutated; a new copy is
8
+ * always returned.
9
+ */
10
+ const REDACTED = "[REDACTED]";
11
+ /**
12
+ * Regex that matches a key name that is *exactly* one of the sensitive words
13
+ * (case-insensitive, full-string match).
14
+ */
15
+ const SENSITIVE_KEY_RE = /^(password|passwd|token|secret|key|credential|auth|apikey|api_key|authorization)$/i;
16
+ /** Returns true if the given key should have its value redacted. */
17
+ function isSensitive(key) {
18
+ return SENSITIVE_KEY_RE.test(key);
19
+ }
20
+ /**
21
+ * Recursively sanitize an unknown value that may be a plain object, an array,
22
+ * or a primitive. When called from the top-level sanitizeParams the `key`
23
+ * parameter is always provided so we can decide whether to redact.
24
+ *
25
+ * @param value - the value to sanitize
26
+ * @param key - the object key under which this value sits (undefined for the
27
+ * top-level call where there is no parent key)
28
+ */
29
+ function sanitizeValue(value, key) {
30
+ // If this value sits under a sensitive key, redact it immediately —
31
+ // regardless of the value's own type (string, number, null, object, …).
32
+ if (key !== undefined && isSensitive(key)) {
33
+ return REDACTED;
34
+ }
35
+ if (Array.isArray(value)) {
36
+ return value.map((item) => {
37
+ // Array items do not inherit a key name, so we only recurse into objects.
38
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
39
+ return sanitizeObject(item);
40
+ }
41
+ return item;
42
+ });
43
+ }
44
+ if (value !== null && typeof value === "object") {
45
+ return sanitizeObject(value);
46
+ }
47
+ return value;
48
+ }
49
+ /** Shallow-copy an object and recursively sanitize every property. */
50
+ function sanitizeObject(obj) {
51
+ const result = {};
52
+ for (const [k, v] of Object.entries(obj)) {
53
+ result[k] = sanitizeValue(v, k);
54
+ }
55
+ return result;
56
+ }
57
+ /**
58
+ * Return a deep copy of `params` with any values whose key matches the
59
+ * sensitive-key list replaced by `"[REDACTED]"`.
60
+ *
61
+ * Matching is case-insensitive and exact (no prefix/suffix matching).
62
+ * Nested objects and arrays of objects are recursed into.
63
+ * The original `params` object is never mutated.
64
+ */
65
+ export function sanitizeParams(params) {
66
+ return sanitizeObject(params);
67
+ }
68
+ //# sourceMappingURL=sanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizer.js","sourceRoot":"","sources":["../../src/sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B;;;GAGG;AACH,MAAM,gBAAgB,GACpB,oFAAoF,CAAC;AAEvF,oEAAoE;AACpE,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,KAAc,EAAE,GAAY;IACjD,oEAAoE;IACpE,wEAAwE;IACxE,IAAI,GAAG,KAAK,SAAS,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACxB,0EAA0E;YAC1E,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtE,OAAO,cAAc,CAAC,IAA+B,CAAC,CAAC;YACzD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,cAAc,CAAC,KAAgC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,sEAAsE;AACtE,SAAS,cAAc,CAAC,GAA4B;IAClD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,MAA+B;IAE/B,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * TechniqueChecker
3
+ *
4
+ * Maps tool names to technique categories using exact-match lists and
5
+ * substring patterns. All comparisons are case-insensitive.
6
+ *
7
+ * Default categories:
8
+ * dos — denial-of-service flooding tools
9
+ * destructive — data-wiping / destructive tools
10
+ * social_engineering — phishing / credential-harvesting frameworks
11
+ *
12
+ * Custom mappings passed to the constructor are merged with the defaults
13
+ * (they do not replace them). Both exact lists and patterns are extended
14
+ * per-category.
15
+ */
16
+ export interface TechniqueMapping {
17
+ /** Tool names that must match exactly (case-insensitive). */
18
+ exact: string[];
19
+ /** Substrings; tool name is matched if it contains any of these (case-insensitive). */
20
+ patterns: string[];
21
+ }
22
+ export type TechniqueMappings = Record<string, TechniqueMapping>;
23
+ export declare class TechniqueChecker {
24
+ private readonly mappings;
25
+ /**
26
+ * @param customMappings Optional extra mappings to merge on top of the
27
+ * defaults. Keys that already exist in the defaults
28
+ * have their `exact` and `patterns` arrays extended;
29
+ * new keys add entirely new categories.
30
+ */
31
+ constructor(customMappings?: TechniqueMappings);
32
+ /**
33
+ * Returns the technique category that `toolName` belongs to, or `null` if
34
+ * no category matches.
35
+ */
36
+ getCategory(toolName: string): string | null;
37
+ /**
38
+ * Returns `true` when `toolName` belongs to a category that appears in
39
+ * `excludedTechniques`, `false` otherwise (including when the tool is not
40
+ * categorised).
41
+ */
42
+ isBlocked(toolName: string, excludedTechniques: string[]): boolean;
43
+ /**
44
+ * Merges `custom` mappings into `base`, extending per-category arrays rather
45
+ * than replacing them. Returns a deep-copied result so the originals are
46
+ * never mutated.
47
+ */
48
+ private static mergeMappings;
49
+ }
50
+ //# sourceMappingURL=technique-checker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"technique-checker.d.ts","sourceRoot":"","sources":["../../src/technique-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAyBjE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAE7C;;;;;OAKG;gBACS,cAAc,CAAC,EAAE,iBAAiB;IAQ9C;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAoB5C;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,OAAO;IAalE;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;CAsB7B"}