@node9/policy-engine 1.0.0 → 1.4.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/dist/index.d.mts +325 -3
- package/dist/index.d.ts +325 -3
- package/dist/index.js +312 -14
- package/dist/index.mjs +299 -14
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/dlp/index.ts
|
|
2
|
+
import safeRegex from "safe-regex2";
|
|
2
3
|
var ASSIGNMENT_CONTEXT_RE = /\b(?:password|passwd|secret|token|api[_-]?key|auth(?:_key|_token)?|credential|private[_-]?key|access[_-]?key|client[_-]?secret)\s*[=:]\s*/i;
|
|
3
4
|
function isAssignmentContext(text) {
|
|
4
5
|
return ASSIGNMENT_CONTEXT_RE.test(text);
|
|
@@ -482,6 +483,23 @@ function sensitivePathMatch(originalPath) {
|
|
|
482
483
|
};
|
|
483
484
|
}
|
|
484
485
|
var SENSITIVE_PATH_REGEXES = SENSITIVE_PATH_PATTERNS;
|
|
486
|
+
function assertBuiltinPatternsAreSafe() {
|
|
487
|
+
for (const p of DLP_PATTERNS) {
|
|
488
|
+
if (!safeRegex(p.regex.source)) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
`[node9 engine] Builtin DLP pattern '${p.name}' is vulnerable to ReDoS: ${p.regex.source}`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
for (const re of SENSITIVE_PATH_PATTERNS) {
|
|
495
|
+
if (!safeRegex(re.source)) {
|
|
496
|
+
throw new Error(
|
|
497
|
+
`[node9 engine] Builtin sensitive-path pattern is vulnerable to ReDoS: ${re.source}`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
assertBuiltinPatternsAreSafe();
|
|
485
503
|
function maskSecret(raw, pattern) {
|
|
486
504
|
const match = raw.match(pattern);
|
|
487
505
|
if (!match) return "****";
|
|
@@ -1073,7 +1091,7 @@ function parseAllSshHostsFromCommand(command) {
|
|
|
1073
1091
|
import pm from "picomatch";
|
|
1074
1092
|
|
|
1075
1093
|
// src/utils/regex.ts
|
|
1076
|
-
import
|
|
1094
|
+
import safeRegex2 from "safe-regex2";
|
|
1077
1095
|
var MAX_REGEX_LENGTH = 100;
|
|
1078
1096
|
var REGEX_CACHE_MAX = 500;
|
|
1079
1097
|
var regexCache = /* @__PURE__ */ new Map();
|
|
@@ -1086,7 +1104,7 @@ function validateRegex(pattern) {
|
|
|
1086
1104
|
return `Invalid regex syntax: ${e.message}`;
|
|
1087
1105
|
}
|
|
1088
1106
|
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
1089
|
-
if (!
|
|
1107
|
+
if (!safeRegex2(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
1090
1108
|
return null;
|
|
1091
1109
|
}
|
|
1092
1110
|
function getCompiledRegex(pattern, flags = "") {
|
|
@@ -1123,9 +1141,14 @@ function matchesPattern(text, patterns) {
|
|
|
1123
1141
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1124
1142
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1125
1143
|
}
|
|
1144
|
+
var FORBIDDEN_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1126
1145
|
function getNestedValue(obj, path) {
|
|
1127
1146
|
if (!obj || typeof obj !== "object") return null;
|
|
1128
|
-
|
|
1147
|
+
const segments = path.split(".");
|
|
1148
|
+
for (const seg of segments) {
|
|
1149
|
+
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
1150
|
+
}
|
|
1151
|
+
return segments.reduce((prev, curr) => prev?.[curr], obj);
|
|
1129
1152
|
}
|
|
1130
1153
|
function evaluateSmartConditions(args, rule) {
|
|
1131
1154
|
if (!rule.conditions || rule.conditions.length === 0) return true;
|
|
@@ -1411,6 +1434,9 @@ function isIgnoredTool(toolName, config) {
|
|
|
1411
1434
|
return matchesPattern(toolName, config.policy.ignoredTools);
|
|
1412
1435
|
}
|
|
1413
1436
|
|
|
1437
|
+
// src/shields/index.ts
|
|
1438
|
+
import safeRegex3 from "safe-regex2";
|
|
1439
|
+
|
|
1414
1440
|
// src/shields/builtin/aws.json
|
|
1415
1441
|
var aws_default = {
|
|
1416
1442
|
name: "aws",
|
|
@@ -1842,15 +1868,6 @@ var k8s_default = {
|
|
|
1842
1868
|
dangerousWords: []
|
|
1843
1869
|
};
|
|
1844
1870
|
|
|
1845
|
-
// src/shields/builtin/mcp-tool-gating.json
|
|
1846
|
-
var mcp_tool_gating_default = {
|
|
1847
|
-
name: "mcp-tool-gating",
|
|
1848
|
-
description: "Intercept MCP tool lists and require user approval before the agent can use any tools from a new server",
|
|
1849
|
-
aliases: ["mcp-gating", "mcp-tools"],
|
|
1850
|
-
smartRules: [],
|
|
1851
|
-
dangerousWords: []
|
|
1852
|
-
};
|
|
1853
|
-
|
|
1854
1871
|
// src/shields/builtin/mongodb.json
|
|
1855
1872
|
var mongodb_default = {
|
|
1856
1873
|
name: "mongodb",
|
|
@@ -2165,12 +2182,29 @@ var BUILTIN_SHIELDS = {
|
|
|
2165
2182
|
[filesystem_default.name]: filesystem_default,
|
|
2166
2183
|
[github_default.name]: github_default,
|
|
2167
2184
|
[k8s_default.name]: k8s_default,
|
|
2168
|
-
[mcp_tool_gating_default.name]: mcp_tool_gating_default,
|
|
2169
2185
|
[mongodb_default.name]: mongodb_default,
|
|
2170
2186
|
[postgres_default.name]: postgres_default,
|
|
2171
2187
|
[project_jail_default.name]: project_jail_default,
|
|
2172
2188
|
[redis_default.name]: redis_default
|
|
2173
2189
|
};
|
|
2190
|
+
function assertBuiltinShieldRegexesAreSafe() {
|
|
2191
|
+
for (const shield of Object.values(BUILTIN_SHIELDS)) {
|
|
2192
|
+
for (const rule of shield.smartRules) {
|
|
2193
|
+
const conditions = rule.conditions ?? [];
|
|
2194
|
+
for (const cond of conditions) {
|
|
2195
|
+
if (cond.op !== "matches" && cond.op !== "notMatches") continue;
|
|
2196
|
+
const pattern = cond.value;
|
|
2197
|
+
if (!pattern) continue;
|
|
2198
|
+
if (!safeRegex3(pattern)) {
|
|
2199
|
+
throw new Error(
|
|
2200
|
+
`[node9 engine] Shield '${shield.name}' rule '${rule.name ?? rule.tool}' has unsafe regex: ${pattern}`
|
|
2201
|
+
);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
assertBuiltinShieldRegexesAreSafe();
|
|
2174
2208
|
|
|
2175
2209
|
// src/loop/index.ts
|
|
2176
2210
|
import crypto from "crypto";
|
|
@@ -2189,19 +2223,266 @@ function evaluateLoopWindow(records, tool, args, threshold, windowMs, now) {
|
|
|
2189
2223
|
return { nextRecords, count, looping: count >= threshold };
|
|
2190
2224
|
}
|
|
2191
2225
|
|
|
2226
|
+
// src/scan/index.ts
|
|
2227
|
+
function emptySignals() {
|
|
2228
|
+
return {
|
|
2229
|
+
dlpFindings: 0,
|
|
2230
|
+
piiFindings: 0,
|
|
2231
|
+
sensitiveFileReads: 0,
|
|
2232
|
+
privilegeEscalation: 0,
|
|
2233
|
+
networkExfil: 0,
|
|
2234
|
+
pipeToShell: 0,
|
|
2235
|
+
evalOfRemote: 0,
|
|
2236
|
+
destructiveOps: 0,
|
|
2237
|
+
loops: 0,
|
|
2238
|
+
longOutputRedactions: 0
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2241
|
+
var FINDING_TO_SIGNAL = {
|
|
2242
|
+
dlp: "dlpFindings",
|
|
2243
|
+
pii: "piiFindings",
|
|
2244
|
+
"sensitive-file-read": "sensitiveFileReads",
|
|
2245
|
+
"privilege-escalation": "privilegeEscalation",
|
|
2246
|
+
"network-exfil": "networkExfil",
|
|
2247
|
+
"pipe-to-shell": "pipeToShell",
|
|
2248
|
+
"eval-of-remote": "evalOfRemote",
|
|
2249
|
+
"destructive-op": "destructiveOps",
|
|
2250
|
+
loop: "loops",
|
|
2251
|
+
"long-output-redacted": "longOutputRedactions"
|
|
2252
|
+
};
|
|
2253
|
+
var SCAN_SIGNAL_WEIGHTS = {
|
|
2254
|
+
dlpFindings: 30,
|
|
2255
|
+
piiFindings: 10,
|
|
2256
|
+
sensitiveFileReads: 20,
|
|
2257
|
+
privilegeEscalation: 15,
|
|
2258
|
+
networkExfil: 25,
|
|
2259
|
+
pipeToShell: 30,
|
|
2260
|
+
evalOfRemote: 30,
|
|
2261
|
+
destructiveOps: 15,
|
|
2262
|
+
loops: 3,
|
|
2263
|
+
longOutputRedactions: 1
|
|
2264
|
+
};
|
|
2265
|
+
function computeScanScore(signals) {
|
|
2266
|
+
let deduction = 0;
|
|
2267
|
+
for (const key of Object.keys(signals)) {
|
|
2268
|
+
deduction += signals[key] * SCAN_SIGNAL_WEIGHTS[key];
|
|
2269
|
+
}
|
|
2270
|
+
return Math.max(0, Math.min(100, 100 - deduction));
|
|
2271
|
+
}
|
|
2272
|
+
var LOOP_THRESHOLD_FOR_WASTE = 3;
|
|
2273
|
+
var COST_PER_LOOP_ITER_USD = 6e-3;
|
|
2274
|
+
function summarizeScan(findings, opts = {}) {
|
|
2275
|
+
const totalToolCalls = opts.totalToolCalls ?? 0;
|
|
2276
|
+
const topN = opts.topN ?? 10;
|
|
2277
|
+
const signals = emptySignals();
|
|
2278
|
+
const sessionIds = /* @__PURE__ */ new Set();
|
|
2279
|
+
const patternCounts = /* @__PURE__ */ new Map();
|
|
2280
|
+
for (const f of findings) {
|
|
2281
|
+
sessionIds.add(f.sessionId);
|
|
2282
|
+
const key = FINDING_TO_SIGNAL[f.type];
|
|
2283
|
+
signals[key]++;
|
|
2284
|
+
if (f.patternName) {
|
|
2285
|
+
patternCounts.set(f.patternName, (patternCounts.get(f.patternName) ?? 0) + 1);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
const topPatterns = [...patternCounts.entries()].sort((a, b) => {
|
|
2289
|
+
if (b[1] !== a[1]) return b[1] - a[1];
|
|
2290
|
+
return a[0].localeCompare(b[0]);
|
|
2291
|
+
}).slice(0, topN).map(([patternName, count]) => ({ patternName, count }));
|
|
2292
|
+
return {
|
|
2293
|
+
totalSessions: sessionIds.size,
|
|
2294
|
+
totalToolCalls,
|
|
2295
|
+
signals,
|
|
2296
|
+
topPatterns,
|
|
2297
|
+
score: computeScanScore(signals)
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// src/severity/index.ts
|
|
2302
|
+
function classifyRuleSeverity(name, verdict) {
|
|
2303
|
+
const n = name.toLowerCase();
|
|
2304
|
+
const criticalPatterns = [
|
|
2305
|
+
"rm-rf",
|
|
2306
|
+
"eval-remote",
|
|
2307
|
+
"eval-curl",
|
|
2308
|
+
"read-aws",
|
|
2309
|
+
"read-ssh",
|
|
2310
|
+
"read-gcp",
|
|
2311
|
+
"read-cred",
|
|
2312
|
+
"delete-repo",
|
|
2313
|
+
"helm-uninstall",
|
|
2314
|
+
"drop-table",
|
|
2315
|
+
"drop-database",
|
|
2316
|
+
"drop-collection",
|
|
2317
|
+
"truncate",
|
|
2318
|
+
"flushall",
|
|
2319
|
+
"flushdb",
|
|
2320
|
+
"pipe-shell"
|
|
2321
|
+
];
|
|
2322
|
+
if (criticalPatterns.some((p) => n.includes(p))) return "critical";
|
|
2323
|
+
const highPatterns = [
|
|
2324
|
+
"force-push",
|
|
2325
|
+
"force_push",
|
|
2326
|
+
"git-destructive",
|
|
2327
|
+
"reset-hard",
|
|
2328
|
+
"rebase",
|
|
2329
|
+
"delete-branch",
|
|
2330
|
+
"delete-remote"
|
|
2331
|
+
];
|
|
2332
|
+
if (highPatterns.some((p) => n.includes(p))) return "high";
|
|
2333
|
+
if (verdict === "block") return "high";
|
|
2334
|
+
return "medium";
|
|
2335
|
+
}
|
|
2336
|
+
function narrativeRuleLabel(name) {
|
|
2337
|
+
const stripped = stripRulePrefixes(name);
|
|
2338
|
+
const map = {
|
|
2339
|
+
"read-aws": "AWS credentials read",
|
|
2340
|
+
"read-ssh": "SSH private key read",
|
|
2341
|
+
"read-gcp": "GCP credentials read",
|
|
2342
|
+
"read-cred": "credential file read",
|
|
2343
|
+
"delete-repo": "GitHub repository deletion",
|
|
2344
|
+
"helm-uninstall": "helm uninstall",
|
|
2345
|
+
"rm-rf-home": "rm -rf on home directory",
|
|
2346
|
+
"rm-rf": "rm -rf",
|
|
2347
|
+
"eval-remote": "eval of remote download",
|
|
2348
|
+
"eval-curl": "eval of curl output",
|
|
2349
|
+
"pipe-shell": "curl | bash",
|
|
2350
|
+
"drop-table": "DROP TABLE",
|
|
2351
|
+
"drop-database": "DROP DATABASE",
|
|
2352
|
+
"drop-collection": "DROP COLLECTION",
|
|
2353
|
+
truncate: "TRUNCATE",
|
|
2354
|
+
flushall: "Redis FLUSHALL",
|
|
2355
|
+
flushdb: "Redis FLUSHDB",
|
|
2356
|
+
"force-push": "force pushes",
|
|
2357
|
+
force_push: "force pushes",
|
|
2358
|
+
"reset-hard": "git reset --hard",
|
|
2359
|
+
"git-destructive": "destructive git operations",
|
|
2360
|
+
"delete-branch": "branch deletion",
|
|
2361
|
+
"delete-remote": "remote deletion",
|
|
2362
|
+
rebase: "git rebase",
|
|
2363
|
+
rm: "rm calls",
|
|
2364
|
+
sudo: "sudo calls",
|
|
2365
|
+
"eval-dynamic": "dynamic eval",
|
|
2366
|
+
"config-set": "Redis CONFIG SET"
|
|
2367
|
+
};
|
|
2368
|
+
for (const [key, label] of Object.entries(map)) {
|
|
2369
|
+
if (stripped.includes(key)) return label;
|
|
2370
|
+
}
|
|
2371
|
+
return stripped;
|
|
2372
|
+
}
|
|
2373
|
+
function stripRulePrefixes(name) {
|
|
2374
|
+
let n = name.toLowerCase();
|
|
2375
|
+
if (n.startsWith("org:")) n = n.slice(4);
|
|
2376
|
+
const shieldMatch = /^shield:[^:]+:(.+)$/.exec(n);
|
|
2377
|
+
if (shieldMatch) n = shieldMatch[1];
|
|
2378
|
+
n = n.replace(/^(block|review|allow)-/, "");
|
|
2379
|
+
return n;
|
|
2380
|
+
}
|
|
2381
|
+
function classifyAuditEntry(entry) {
|
|
2382
|
+
const ruleName = entry.riskMetadata?.ruleName;
|
|
2383
|
+
if (typeof ruleName === "string" && ruleName.length > 0) {
|
|
2384
|
+
const verdict = entry.action === "AUTO_BLOCKED" || entry.action === "DENIED" ? "block" : entry.action === "APPROVED" || entry.action === "AUTO_ALLOWED" ? "allow" : "review";
|
|
2385
|
+
return classifyRuleSeverity(ruleName, verdict);
|
|
2386
|
+
}
|
|
2387
|
+
const cb = entry.checkedBy ?? "";
|
|
2388
|
+
if (cb === "dlp-block" || cb.startsWith("dlp-saas:")) return "critical";
|
|
2389
|
+
if (cb.startsWith("eval-saas") || cb === "pipe-chain-saas:critical") {
|
|
2390
|
+
return "critical";
|
|
2391
|
+
}
|
|
2392
|
+
if (cb === "loop-detected") return "medium";
|
|
2393
|
+
const isBlocked = entry.action === "AUTO_BLOCKED" || entry.action === "DENIED";
|
|
2394
|
+
if (isBlocked) return "high";
|
|
2395
|
+
return null;
|
|
2396
|
+
}
|
|
2397
|
+
function computeSecurityScore(opts) {
|
|
2398
|
+
const { critical, high, medium, total } = opts;
|
|
2399
|
+
if (total === 0) return { score: 100, tier: "good" };
|
|
2400
|
+
const criticalRate = critical / total;
|
|
2401
|
+
const highRate = high / total;
|
|
2402
|
+
const mediumRate = medium / total;
|
|
2403
|
+
const deduction = Math.min(criticalRate * 3e3, 60) + Math.min(highRate * 500, 30) + Math.min(mediumRate * 100, 15);
|
|
2404
|
+
const score = Math.max(0, Math.min(100, Math.round(100 - deduction)));
|
|
2405
|
+
const tier = score >= 80 ? "good" : score >= 50 ? "at-risk" : "critical";
|
|
2406
|
+
return { score, tier };
|
|
2407
|
+
}
|
|
2408
|
+
function classifyScanSignal(key) {
|
|
2409
|
+
const w = SCAN_SIGNAL_WEIGHTS[key];
|
|
2410
|
+
if (w >= 25) return "critical";
|
|
2411
|
+
if (w >= 11) return "high";
|
|
2412
|
+
return "medium";
|
|
2413
|
+
}
|
|
2414
|
+
function computeBlendedSecurityScore(opts) {
|
|
2415
|
+
const { audit, scan } = opts;
|
|
2416
|
+
let critical = audit.critical;
|
|
2417
|
+
let high = audit.high;
|
|
2418
|
+
let medium = audit.medium;
|
|
2419
|
+
let total = audit.total;
|
|
2420
|
+
if (scan) {
|
|
2421
|
+
let scanFindingSum = 0;
|
|
2422
|
+
for (const key of Object.keys(scan.signals)) {
|
|
2423
|
+
const count = scan.signals[key];
|
|
2424
|
+
if (count <= 0) continue;
|
|
2425
|
+
const tier = classifyScanSignal(key);
|
|
2426
|
+
if (tier === "critical") critical += count;
|
|
2427
|
+
else if (tier === "high") high += count;
|
|
2428
|
+
else medium += count;
|
|
2429
|
+
scanFindingSum += count;
|
|
2430
|
+
}
|
|
2431
|
+
const scanContribution = Math.max(scan.totalToolCalls ?? 0, scanFindingSum);
|
|
2432
|
+
total += scanContribution;
|
|
2433
|
+
}
|
|
2434
|
+
return computeSecurityScore({ critical, high, medium, total });
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// src/blast/index.ts
|
|
2438
|
+
function truncateBlastPath(full) {
|
|
2439
|
+
if (!full) return "";
|
|
2440
|
+
const cleaned = full.replace(/[/\\]+$/, "");
|
|
2441
|
+
const parts = cleaned.split(/[/\\]+/).filter((p) => p.length > 0);
|
|
2442
|
+
if (parts.length <= 2) {
|
|
2443
|
+
return cleaned.startsWith("~") && !cleaned.startsWith("~/") ? cleaned : cleaned.startsWith("~/") ? cleaned : parts.join("/");
|
|
2444
|
+
}
|
|
2445
|
+
return parts.slice(-2).join("/");
|
|
2446
|
+
}
|
|
2447
|
+
function summarizeBlast(result, opts = {}) {
|
|
2448
|
+
const topN = opts.topN ?? 5;
|
|
2449
|
+
const sorted = [...result.reachable].sort((a, b) => {
|
|
2450
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
2451
|
+
return a.label.localeCompare(b.label);
|
|
2452
|
+
});
|
|
2453
|
+
return {
|
|
2454
|
+
score: result.score,
|
|
2455
|
+
exposureCount: result.reachable.length + result.envFindings.length,
|
|
2456
|
+
envExposureCount: result.envFindings.length,
|
|
2457
|
+
worstPaths: sorted.slice(0, topN).map((f) => ({
|
|
2458
|
+
path: truncateBlastPath(f.label),
|
|
2459
|
+
score: f.score
|
|
2460
|
+
}))
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2192
2464
|
// src/index.ts
|
|
2193
|
-
var ENGINE_VERSION = "1.
|
|
2465
|
+
var ENGINE_VERSION = "1.4.0";
|
|
2194
2466
|
export {
|
|
2195
2467
|
BUILTIN_SHIELDS,
|
|
2468
|
+
COST_PER_LOOP_ITER_USD,
|
|
2196
2469
|
DLP_PATTERNS,
|
|
2197
2470
|
ENGINE_VERSION,
|
|
2198
2471
|
FLAGS_WITH_VALUES,
|
|
2199
2472
|
LOOP_MAX_RECORDS,
|
|
2473
|
+
LOOP_THRESHOLD_FOR_WASTE,
|
|
2474
|
+
SCAN_SIGNAL_WEIGHTS,
|
|
2200
2475
|
SENSITIVE_PATH_REGEXES,
|
|
2201
2476
|
analyzePipeChain,
|
|
2202
2477
|
analyzeShellCommand,
|
|
2203
2478
|
checkDangerousSql,
|
|
2479
|
+
classifyAuditEntry,
|
|
2480
|
+
classifyRuleSeverity,
|
|
2481
|
+
classifyScanSignal,
|
|
2204
2482
|
computeArgsHash,
|
|
2483
|
+
computeBlendedSecurityScore,
|
|
2484
|
+
computeScanScore,
|
|
2485
|
+
computeSecurityScore,
|
|
2205
2486
|
detectDangerousEval,
|
|
2206
2487
|
detectDangerousShellExec,
|
|
2207
2488
|
evaluateLoopWindow,
|
|
@@ -2216,12 +2497,16 @@ export {
|
|
|
2216
2497
|
isShieldVerdict,
|
|
2217
2498
|
matchSensitivePath,
|
|
2218
2499
|
matchesPattern,
|
|
2500
|
+
narrativeRuleLabel,
|
|
2219
2501
|
normalizeCommandForPolicy,
|
|
2220
2502
|
parseAllSshHostsFromCommand,
|
|
2221
2503
|
redactText,
|
|
2222
2504
|
scanArgs,
|
|
2223
2505
|
scanText,
|
|
2224
2506
|
sensitivePathMatch,
|
|
2507
|
+
summarizeBlast,
|
|
2508
|
+
summarizeScan,
|
|
2509
|
+
truncateBlastPath,
|
|
2225
2510
|
validateOverrides,
|
|
2226
2511
|
validateRegex,
|
|
2227
2512
|
validateShieldDefinition
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/policy-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Shared policy evaluation engine for node9 — DLP, smart rules, AST shell parsing, shields, loop detection. Pure functions, no I/O. Used by both node9-proxy and the node9 SaaS firewall.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://node9.ai",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/node9-ai/node9-proxy.git",
|
|
9
|
+
"url": "git+https://github.com/node9-ai/node9-proxy.git",
|
|
10
10
|
"directory": "packages/policy-engine"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|