@node9/proxy 1.7.1 → 1.8.3
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 +50 -657
- package/dist/cli.js +404 -333
- package/dist/cli.mjs +396 -325
- package/dist/index.js +90 -260
- package/dist/index.mjs +90 -260
- package/dist/shields/builtin/aws.json +59 -0
- package/dist/shields/builtin/bash-safe.json +78 -0
- package/dist/shields/builtin/docker.json +120 -0
- package/dist/shields/builtin/filesystem.json +30 -0
- package/dist/shields/builtin/github.json +26 -0
- package/dist/shields/builtin/k8s.json +92 -0
- package/dist/shields/builtin/mongodb.json +78 -0
- package/dist/shields/builtin/postgres.json +42 -0
- package/dist/shields/builtin/redis.json +78 -0
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -250,251 +250,73 @@ ${lines.join("\n")}`
|
|
|
250
250
|
import fs2 from "fs";
|
|
251
251
|
import path2 from "path";
|
|
252
252
|
import os2 from "os";
|
|
253
|
-
var
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
name: "shield:postgres:block-drop-table",
|
|
261
|
-
tool: "*",
|
|
262
|
-
conditions: [{ field: "sql", op: "matches", value: "DROP\\s+TABLE", flags: "i" }],
|
|
263
|
-
verdict: "block",
|
|
264
|
-
reason: "DROP TABLE is irreversible \u2014 blocked by Postgres shield"
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
name: "shield:postgres:block-truncate",
|
|
268
|
-
tool: "*",
|
|
269
|
-
conditions: [{ field: "sql", op: "matches", value: "TRUNCATE\\s+TABLE", flags: "i" }],
|
|
270
|
-
verdict: "block",
|
|
271
|
-
reason: "TRUNCATE is irreversible \u2014 blocked by Postgres shield"
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
name: "shield:postgres:block-drop-column",
|
|
275
|
-
tool: "*",
|
|
276
|
-
conditions: [
|
|
277
|
-
{ field: "sql", op: "matches", value: "ALTER\\s+TABLE.*DROP\\s+COLUMN", flags: "i" }
|
|
278
|
-
],
|
|
279
|
-
verdict: "block",
|
|
280
|
-
reason: "DROP COLUMN is irreversible \u2014 blocked by Postgres shield"
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
name: "shield:postgres:review-grant-revoke",
|
|
284
|
-
tool: "*",
|
|
285
|
-
conditions: [{ field: "sql", op: "matches", value: "\\b(GRANT|REVOKE)\\b", flags: "i" }],
|
|
286
|
-
verdict: "review",
|
|
287
|
-
reason: "Permission changes require human approval (Postgres shield)"
|
|
288
|
-
}
|
|
289
|
-
],
|
|
290
|
-
dangerousWords: ["dropdb", "pg_dropcluster"]
|
|
291
|
-
},
|
|
292
|
-
github: {
|
|
293
|
-
name: "github",
|
|
294
|
-
description: "Protects GitHub repositories from destructive AI operations",
|
|
295
|
-
aliases: ["git"],
|
|
296
|
-
smartRules: [
|
|
297
|
-
{
|
|
298
|
-
// Note: git branch -d/-D is already caught by the built-in review-git-destructive rule.
|
|
299
|
-
// This rule adds coverage for `git push --delete` which the built-in does not match.
|
|
300
|
-
name: "shield:github:review-delete-branch-remote",
|
|
301
|
-
tool: "bash",
|
|
302
|
-
conditions: [
|
|
303
|
-
{
|
|
304
|
-
field: "command",
|
|
305
|
-
op: "matches",
|
|
306
|
-
value: "git\\s+push\\s+.*--delete",
|
|
307
|
-
flags: "i"
|
|
308
|
-
}
|
|
309
|
-
],
|
|
310
|
-
verdict: "review",
|
|
311
|
-
reason: "Remote branch deletion requires human approval (GitHub shield)"
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
name: "shield:github:block-delete-repo",
|
|
315
|
-
tool: "*",
|
|
316
|
-
conditions: [
|
|
317
|
-
{ field: "command", op: "matches", value: "gh\\s+repo\\s+delete", flags: "i" }
|
|
318
|
-
],
|
|
319
|
-
verdict: "block",
|
|
320
|
-
reason: "Repository deletion is irreversible \u2014 blocked by GitHub shield"
|
|
321
|
-
}
|
|
322
|
-
],
|
|
323
|
-
dangerousWords: []
|
|
324
|
-
},
|
|
325
|
-
aws: {
|
|
326
|
-
name: "aws",
|
|
327
|
-
description: "Protects AWS infrastructure from destructive AI operations",
|
|
328
|
-
aliases: ["amazon"],
|
|
329
|
-
smartRules: [
|
|
330
|
-
{
|
|
331
|
-
name: "shield:aws:block-delete-s3-bucket",
|
|
332
|
-
tool: "*",
|
|
333
|
-
conditions: [
|
|
334
|
-
{
|
|
335
|
-
field: "command",
|
|
336
|
-
op: "matches",
|
|
337
|
-
value: "aws\\s+s3.*rb\\s|aws\\s+s3api\\s+delete-bucket",
|
|
338
|
-
flags: "i"
|
|
339
|
-
}
|
|
340
|
-
],
|
|
341
|
-
verdict: "block",
|
|
342
|
-
reason: "S3 bucket deletion is irreversible \u2014 blocked by AWS shield"
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
name: "shield:aws:review-iam-changes",
|
|
346
|
-
tool: "*",
|
|
347
|
-
conditions: [
|
|
348
|
-
{
|
|
349
|
-
field: "command",
|
|
350
|
-
op: "matches",
|
|
351
|
-
value: "aws\\s+iam\\s+(create|delete|attach|detach|put|remove)",
|
|
352
|
-
flags: "i"
|
|
353
|
-
}
|
|
354
|
-
],
|
|
355
|
-
verdict: "review",
|
|
356
|
-
reason: "IAM changes require human approval (AWS shield)"
|
|
357
|
-
},
|
|
358
|
-
{
|
|
359
|
-
name: "shield:aws:block-ec2-terminate",
|
|
360
|
-
tool: "*",
|
|
361
|
-
conditions: [
|
|
362
|
-
{
|
|
363
|
-
field: "command",
|
|
364
|
-
op: "matches",
|
|
365
|
-
value: "aws\\s+ec2\\s+terminate-instances",
|
|
366
|
-
flags: "i"
|
|
367
|
-
}
|
|
368
|
-
],
|
|
369
|
-
verdict: "block",
|
|
370
|
-
reason: "EC2 instance termination is irreversible \u2014 blocked by AWS shield"
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
name: "shield:aws:review-rds-delete",
|
|
374
|
-
tool: "*",
|
|
375
|
-
conditions: [
|
|
376
|
-
{ field: "command", op: "matches", value: "aws\\s+rds\\s+delete-", flags: "i" }
|
|
377
|
-
],
|
|
378
|
-
verdict: "review",
|
|
379
|
-
reason: "RDS deletion requires human approval (AWS shield)"
|
|
380
|
-
}
|
|
381
|
-
],
|
|
382
|
-
dangerousWords: []
|
|
383
|
-
},
|
|
384
|
-
"bash-safe": {
|
|
385
|
-
name: "bash-safe",
|
|
386
|
-
description: "Blocks high-risk bash patterns: pipe-to-shell, rm -rf /, disk overwrites, eval",
|
|
387
|
-
aliases: ["bash", "shell"],
|
|
388
|
-
smartRules: [
|
|
389
|
-
{
|
|
390
|
-
name: "shield:bash-safe:block-pipe-to-shell",
|
|
391
|
-
tool: "bash",
|
|
392
|
-
conditions: [
|
|
393
|
-
{
|
|
394
|
-
field: "command",
|
|
395
|
-
op: "matches",
|
|
396
|
-
value: "(curl|wget)\\s+[^|]*\\|\\s*(bash|sh|zsh|fish|python3?|ruby|perl|node)",
|
|
397
|
-
flags: "i"
|
|
398
|
-
}
|
|
399
|
-
],
|
|
400
|
-
verdict: "block",
|
|
401
|
-
reason: "Pipe-to-shell is a common supply-chain attack vector \u2014 blocked by bash-safe shield"
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
name: "shield:bash-safe:block-obfuscated-exec",
|
|
405
|
-
tool: "bash",
|
|
406
|
-
conditions: [
|
|
407
|
-
{
|
|
408
|
-
field: "command",
|
|
409
|
-
op: "matches",
|
|
410
|
-
value: "base64\\s+(-d|--decode).*\\|\\s*(bash|sh|zsh)",
|
|
411
|
-
flags: "i"
|
|
412
|
-
}
|
|
413
|
-
],
|
|
414
|
-
verdict: "block",
|
|
415
|
-
reason: "Obfuscated execution via base64 decode \u2014 blocked by bash-safe shield"
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
name: "shield:bash-safe:block-rm-root",
|
|
419
|
-
tool: "bash",
|
|
420
|
-
conditions: [
|
|
421
|
-
{
|
|
422
|
-
field: "command",
|
|
423
|
-
op: "matches",
|
|
424
|
-
value: "rm\\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)[a-zA-Z]*\\s+(\\/|~|\\$HOME|\\$\\{HOME\\})\\s*$",
|
|
425
|
-
flags: "i"
|
|
426
|
-
}
|
|
427
|
-
],
|
|
428
|
-
verdict: "block",
|
|
429
|
-
reason: "rm -rf of root or home directory is catastrophic \u2014 blocked by bash-safe shield"
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
name: "shield:bash-safe:block-disk-overwrite",
|
|
433
|
-
tool: "bash",
|
|
434
|
-
conditions: [
|
|
435
|
-
{
|
|
436
|
-
field: "command",
|
|
437
|
-
op: "matches",
|
|
438
|
-
value: "dd\\s+.*of=\\/dev\\/(sd|nvme|hd|vd|xvd)",
|
|
439
|
-
flags: "i"
|
|
440
|
-
}
|
|
441
|
-
],
|
|
442
|
-
verdict: "block",
|
|
443
|
-
reason: "Writing directly to a block device is irreversible \u2014 blocked by bash-safe shield"
|
|
444
|
-
},
|
|
445
|
-
{
|
|
446
|
-
name: "shield:bash-safe:review-eval",
|
|
447
|
-
tool: "bash",
|
|
448
|
-
conditions: [
|
|
449
|
-
{
|
|
450
|
-
field: "command",
|
|
451
|
-
op: "matches",
|
|
452
|
-
value: '\\beval\\s+[\\$`("]',
|
|
453
|
-
flags: "i"
|
|
454
|
-
}
|
|
455
|
-
],
|
|
456
|
-
verdict: "review",
|
|
457
|
-
reason: "eval of dynamic content requires human approval (bash-safe shield)"
|
|
458
|
-
}
|
|
459
|
-
],
|
|
460
|
-
dangerousWords: []
|
|
461
|
-
},
|
|
462
|
-
filesystem: {
|
|
463
|
-
name: "filesystem",
|
|
464
|
-
description: "Protects the local filesystem from dangerous AI operations",
|
|
465
|
-
aliases: ["fs"],
|
|
466
|
-
smartRules: [
|
|
467
|
-
{
|
|
468
|
-
name: "shield:filesystem:review-chmod-777",
|
|
469
|
-
tool: "bash",
|
|
470
|
-
conditions: [
|
|
471
|
-
{ field: "command", op: "matches", value: "chmod\\s+(777|a\\+rwx)", flags: "i" }
|
|
472
|
-
],
|
|
473
|
-
verdict: "review",
|
|
474
|
-
reason: "chmod 777 requires human approval (filesystem shield)"
|
|
475
|
-
},
|
|
476
|
-
{
|
|
477
|
-
name: "shield:filesystem:review-write-etc",
|
|
478
|
-
tool: "bash",
|
|
479
|
-
conditions: [
|
|
480
|
-
{
|
|
481
|
-
field: "command",
|
|
482
|
-
// Narrow to write-indicative operations to avoid approval fatigue on reads.
|
|
483
|
-
// Matches: tee /etc/*, cp .../etc/*, mv .../etc/*, > /etc/*, install .../etc/*
|
|
484
|
-
op: "matches",
|
|
485
|
-
value: "(tee|\\bcp\\b|\\bmv\\b|install|>+)\\s+.*\\/etc\\/"
|
|
486
|
-
}
|
|
487
|
-
],
|
|
488
|
-
verdict: "review",
|
|
489
|
-
reason: "Writing to /etc requires human approval (filesystem shield)"
|
|
490
|
-
}
|
|
491
|
-
],
|
|
492
|
-
// dd removed: too common as a legitimate tool (disk imaging, file ops).
|
|
493
|
-
// mkfs removed: already in the built-in DANGEROUS_WORDS baseline.
|
|
494
|
-
// wipefs retained: rarely legitimate in an agent context and not in built-ins.
|
|
495
|
-
dangerousWords: ["wipefs"]
|
|
253
|
+
var BUILTIN_DIR = path2.join(__dirname, "shields", "builtin");
|
|
254
|
+
var USER_SHIELDS_DIR = path2.join(os2.homedir(), ".node9", "shields");
|
|
255
|
+
function validateShieldDefinition(raw, filePath) {
|
|
256
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
257
|
+
process.stderr.write(`[node9] Shield file is not an object: ${filePath}
|
|
258
|
+
`);
|
|
259
|
+
return null;
|
|
496
260
|
}
|
|
497
|
-
|
|
261
|
+
const r = raw;
|
|
262
|
+
if (typeof r.name !== "string" || !r.name) {
|
|
263
|
+
process.stderr.write(`[node9] Shield file missing 'name': ${filePath}
|
|
264
|
+
`);
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
if (typeof r.description !== "string") {
|
|
268
|
+
process.stderr.write(`[node9] Shield file missing 'description': ${filePath}
|
|
269
|
+
`);
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
if (!Array.isArray(r.aliases)) {
|
|
273
|
+
process.stderr.write(`[node9] Shield file missing 'aliases' array: ${filePath}
|
|
274
|
+
`);
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
if (!Array.isArray(r.smartRules)) {
|
|
278
|
+
process.stderr.write(`[node9] Shield file missing 'smartRules' array: ${filePath}
|
|
279
|
+
`);
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
if (!Array.isArray(r.dangerousWords)) {
|
|
283
|
+
process.stderr.write(`[node9] Shield file missing 'dangerousWords' array: ${filePath}
|
|
284
|
+
`);
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
return r;
|
|
288
|
+
}
|
|
289
|
+
function loadShieldsFromDir(dir, label) {
|
|
290
|
+
const result = {};
|
|
291
|
+
let entries;
|
|
292
|
+
try {
|
|
293
|
+
entries = fs2.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
294
|
+
} catch (err) {
|
|
295
|
+
if (err.code !== "ENOENT") {
|
|
296
|
+
process.stderr.write(`[node9] Could not read ${label} shields dir ${dir}: ${String(err)}
|
|
297
|
+
`);
|
|
298
|
+
}
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
for (const file of entries) {
|
|
302
|
+
const filePath = path2.join(dir, file);
|
|
303
|
+
try {
|
|
304
|
+
const raw = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
305
|
+
const shield = validateShieldDefinition(raw, filePath);
|
|
306
|
+
if (shield) result[shield.name] = shield;
|
|
307
|
+
} catch (err) {
|
|
308
|
+
process.stderr.write(`[node9] Failed to load ${label} shield ${file}: ${String(err)}
|
|
309
|
+
`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
function buildSHIELDS() {
|
|
315
|
+
const builtins = loadShieldsFromDir(BUILTIN_DIR, "builtin");
|
|
316
|
+
const userShields = loadShieldsFromDir(USER_SHIELDS_DIR, "user");
|
|
317
|
+
return { ...builtins, ...userShields };
|
|
318
|
+
}
|
|
319
|
+
var SHIELDS = buildSHIELDS();
|
|
498
320
|
function resolveShieldName(input) {
|
|
499
321
|
const lower = input.toLowerCase();
|
|
500
322
|
if (SHIELDS[lower]) return lower;
|
|
@@ -2118,7 +1940,7 @@ function isDaemonRunning() {
|
|
|
2118
1940
|
return false;
|
|
2119
1941
|
}
|
|
2120
1942
|
}
|
|
2121
|
-
async function registerDaemonEntry(toolName, args, meta, riskMetadata, activityId, cwd, recoveryCommand, skipBackgroundAuth, viewOnly) {
|
|
1943
|
+
async function registerDaemonEntry(toolName, args, meta, riskMetadata, activityId, cwd, recoveryCommand, skipBackgroundAuth, viewOnly, localSmartRuleMatched) {
|
|
2122
1944
|
const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
2123
1945
|
const ctrl = new AbortController();
|
|
2124
1946
|
const timer = setTimeout(() => ctrl.abort(), 5e3);
|
|
@@ -2139,7 +1961,8 @@ async function registerDaemonEntry(toolName, args, meta, riskMetadata, activityI
|
|
|
2139
1961
|
...cwd && { cwd },
|
|
2140
1962
|
...recoveryCommand && { recoveryCommand },
|
|
2141
1963
|
...skipBackgroundAuth && { skipBackgroundAuth: true },
|
|
2142
|
-
...viewOnly && { viewOnly: true }
|
|
1964
|
+
...viewOnly && { viewOnly: true },
|
|
1965
|
+
...localSmartRuleMatched && { localSmartRuleMatched: true }
|
|
2143
1966
|
}),
|
|
2144
1967
|
signal: ctrl.signal
|
|
2145
1968
|
});
|
|
@@ -2529,9 +2352,7 @@ end run`;
|
|
|
2529
2352
|
"--text",
|
|
2530
2353
|
pangoMessage,
|
|
2531
2354
|
"--ok-label",
|
|
2532
|
-
locked ? "Waiting..." : "Allow \u21B5"
|
|
2533
|
-
"--timeout",
|
|
2534
|
-
"300"
|
|
2355
|
+
locked ? "Waiting..." : "Allow \u21B5"
|
|
2535
2356
|
];
|
|
2536
2357
|
if (!locked) {
|
|
2537
2358
|
argsList.push("--cancel-label", "Block \u238B");
|
|
@@ -2807,6 +2628,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2807
2628
|
let policyMatchedWord;
|
|
2808
2629
|
let riskMetadata;
|
|
2809
2630
|
let statefulRecoveryCommand;
|
|
2631
|
+
let localSmartRuleMatched = false;
|
|
2810
2632
|
let taintWarning = null;
|
|
2811
2633
|
if (isNetworkTool(toolName, args)) {
|
|
2812
2634
|
const filePaths = extractFilePaths(toolName, args);
|
|
@@ -2950,6 +2772,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2950
2772
|
explainableLabel = policyResult.blockedByLabel || "Local Config";
|
|
2951
2773
|
policyMatchedField = policyResult.matchedField;
|
|
2952
2774
|
policyMatchedWord = policyResult.matchedWord;
|
|
2775
|
+
if (policyResult.ruleName) localSmartRuleMatched = true;
|
|
2953
2776
|
riskMetadata = computeRiskMetadata(
|
|
2954
2777
|
args,
|
|
2955
2778
|
policyResult.tier ?? 6,
|
|
@@ -2992,22 +2815,26 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2992
2815
|
}
|
|
2993
2816
|
let cloudRequestId = null;
|
|
2994
2817
|
const cloudEnforced = approvers.cloud && !!creds?.apiKey;
|
|
2995
|
-
if (cloudEnforced) {
|
|
2818
|
+
if (cloudEnforced && !localSmartRuleMatched && !options?.localSmartRuleMatched) {
|
|
2996
2819
|
try {
|
|
2997
2820
|
const initResult = await initNode9SaaS(toolName, args, creds, meta, riskMetadata);
|
|
2998
2821
|
if (!initResult.pending) {
|
|
2999
2822
|
if (initResult.shadowMode) {
|
|
3000
2823
|
return { approved: true, checkedBy: "cloud" };
|
|
3001
2824
|
}
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
2825
|
+
if (!localSmartRuleMatched && !options?.localSmartRuleMatched) {
|
|
2826
|
+
return {
|
|
2827
|
+
approved: !!initResult.approved,
|
|
2828
|
+
reason: initResult.reason || (initResult.approved ? void 0 : "Action rejected by organization policy."),
|
|
2829
|
+
checkedBy: initResult.approved ? "cloud" : void 0,
|
|
2830
|
+
blockedBy: initResult.approved ? void 0 : "team-policy",
|
|
2831
|
+
blockedByLabel: "Organization Policy (SaaS)"
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
if (!localSmartRuleMatched && !options?.localSmartRuleMatched) {
|
|
2836
|
+
cloudRequestId = initResult.requestId || null;
|
|
3009
2837
|
}
|
|
3010
|
-
cloudRequestId = initResult.requestId || null;
|
|
3011
2838
|
if (!taintWarning) explainableLabel = "Organization Policy (SaaS)";
|
|
3012
2839
|
} catch {
|
|
3013
2840
|
}
|
|
@@ -3053,7 +2880,10 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3053
2880
|
riskMetadata,
|
|
3054
2881
|
options?.activityId,
|
|
3055
2882
|
options?.cwd,
|
|
3056
|
-
statefulRecoveryCommand
|
|
2883
|
+
statefulRecoveryCommand,
|
|
2884
|
+
void 0,
|
|
2885
|
+
void 0,
|
|
2886
|
+
localSmartRuleMatched || options?.localSmartRuleMatched
|
|
3057
2887
|
);
|
|
3058
2888
|
daemonEntryId = entry.id;
|
|
3059
2889
|
daemonAllowCount = entry.allowCount;
|
|
@@ -3061,7 +2891,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3061
2891
|
}
|
|
3062
2892
|
}
|
|
3063
2893
|
}
|
|
3064
|
-
if (cloudEnforced && cloudRequestId) {
|
|
2894
|
+
if (cloudEnforced && cloudRequestId && !localSmartRuleMatched && !options?.localSmartRuleMatched) {
|
|
3065
2895
|
racePromises.push(
|
|
3066
2896
|
(async () => {
|
|
3067
2897
|
try {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aws",
|
|
3
|
+
"description": "Protects AWS infrastructure from destructive AI operations",
|
|
4
|
+
"aliases": ["amazon"],
|
|
5
|
+
"smartRules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "shield:aws:block-delete-s3-bucket",
|
|
8
|
+
"tool": "*",
|
|
9
|
+
"conditions": [
|
|
10
|
+
{
|
|
11
|
+
"field": "command",
|
|
12
|
+
"op": "matches",
|
|
13
|
+
"value": "aws\\s+s3.*rb\\s|aws\\s+s3api\\s+delete-bucket",
|
|
14
|
+
"flags": "i"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"verdict": "block",
|
|
18
|
+
"reason": "S3 bucket deletion is irreversible — blocked by AWS shield"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "shield:aws:review-iam-changes",
|
|
22
|
+
"tool": "*",
|
|
23
|
+
"conditions": [
|
|
24
|
+
{
|
|
25
|
+
"field": "command",
|
|
26
|
+
"op": "matches",
|
|
27
|
+
"value": "aws\\s+iam\\s+(create|delete|attach|detach|put|remove)",
|
|
28
|
+
"flags": "i"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"verdict": "review",
|
|
32
|
+
"reason": "IAM changes require human approval (AWS shield)"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "shield:aws:block-ec2-terminate",
|
|
36
|
+
"tool": "*",
|
|
37
|
+
"conditions": [
|
|
38
|
+
{
|
|
39
|
+
"field": "command",
|
|
40
|
+
"op": "matches",
|
|
41
|
+
"value": "aws\\s+ec2\\s+terminate-instances",
|
|
42
|
+
"flags": "i"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"verdict": "block",
|
|
46
|
+
"reason": "EC2 instance termination is irreversible — blocked by AWS shield"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "shield:aws:review-rds-delete",
|
|
50
|
+
"tool": "*",
|
|
51
|
+
"conditions": [
|
|
52
|
+
{ "field": "command", "op": "matches", "value": "aws\\s+rds\\s+delete-", "flags": "i" }
|
|
53
|
+
],
|
|
54
|
+
"verdict": "review",
|
|
55
|
+
"reason": "RDS deletion requires human approval (AWS shield)"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"dangerousWords": []
|
|
59
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bash-safe",
|
|
3
|
+
"description": "Blocks high-risk bash patterns: pipe-to-shell, rm -rf /, disk overwrites, eval",
|
|
4
|
+
"aliases": ["bash", "shell"],
|
|
5
|
+
"smartRules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "shield:bash-safe:block-pipe-to-shell",
|
|
8
|
+
"tool": "bash",
|
|
9
|
+
"conditions": [
|
|
10
|
+
{
|
|
11
|
+
"field": "command",
|
|
12
|
+
"op": "matches",
|
|
13
|
+
"value": "(curl|wget)\\s+[^|]*\\|\\s*(bash|sh|zsh|fish|python3?|ruby|perl|node)",
|
|
14
|
+
"flags": "i"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"verdict": "block",
|
|
18
|
+
"reason": "Pipe-to-shell is a common supply-chain attack vector — blocked by bash-safe shield"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "shield:bash-safe:block-obfuscated-exec",
|
|
22
|
+
"tool": "bash",
|
|
23
|
+
"conditions": [
|
|
24
|
+
{
|
|
25
|
+
"field": "command",
|
|
26
|
+
"op": "matches",
|
|
27
|
+
"value": "base64\\s+(-d|--decode).*\\|\\s*(bash|sh|zsh)",
|
|
28
|
+
"flags": "i"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"verdict": "block",
|
|
32
|
+
"reason": "Obfuscated execution via base64 decode — blocked by bash-safe shield"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "shield:bash-safe:block-rm-root",
|
|
36
|
+
"tool": "bash",
|
|
37
|
+
"conditions": [
|
|
38
|
+
{
|
|
39
|
+
"field": "command",
|
|
40
|
+
"op": "matches",
|
|
41
|
+
"value": "rm\\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)[a-zA-Z]*\\s+(\\/|~|\\$HOME|\\$\\{HOME\\})\\s*$",
|
|
42
|
+
"flags": "i"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"verdict": "block",
|
|
46
|
+
"reason": "rm -rf of root or home directory is catastrophic — blocked by bash-safe shield"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "shield:bash-safe:block-disk-overwrite",
|
|
50
|
+
"tool": "bash",
|
|
51
|
+
"conditions": [
|
|
52
|
+
{
|
|
53
|
+
"field": "command",
|
|
54
|
+
"op": "matches",
|
|
55
|
+
"value": "dd\\s+.*of=\\/dev\\/(sd|nvme|hd|vd|xvd)",
|
|
56
|
+
"flags": "i"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"verdict": "block",
|
|
60
|
+
"reason": "Writing directly to a block device is irreversible — blocked by bash-safe shield"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "shield:bash-safe:review-eval",
|
|
64
|
+
"tool": "bash",
|
|
65
|
+
"conditions": [
|
|
66
|
+
{
|
|
67
|
+
"field": "command",
|
|
68
|
+
"op": "matches",
|
|
69
|
+
"value": "\\beval\\s+[\\$`(\"]",
|
|
70
|
+
"flags": "i"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"verdict": "review",
|
|
74
|
+
"reason": "eval of dynamic content requires human approval (bash-safe shield)"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"dangerousWords": []
|
|
78
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "docker",
|
|
3
|
+
"description": "Protects Docker environments from destructive AI operations",
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"smartRules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "shield:docker:block-system-prune",
|
|
8
|
+
"tool": "*",
|
|
9
|
+
"conditions": [
|
|
10
|
+
{
|
|
11
|
+
"field": "command",
|
|
12
|
+
"op": "matches",
|
|
13
|
+
"value": "docker\\s+system\\s+prune",
|
|
14
|
+
"flags": "i"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"verdict": "block",
|
|
18
|
+
"reason": "docker system prune removes all unused containers, images, and volumes — blocked by Docker shield"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "shield:docker:block-volume-prune",
|
|
22
|
+
"tool": "*",
|
|
23
|
+
"conditions": [
|
|
24
|
+
{
|
|
25
|
+
"field": "command",
|
|
26
|
+
"op": "matches",
|
|
27
|
+
"value": "docker\\s+volume\\s+prune",
|
|
28
|
+
"flags": "i"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"verdict": "block",
|
|
32
|
+
"reason": "docker volume prune destroys all unused volumes and their data — blocked by Docker shield"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "shield:docker:block-rm-force",
|
|
36
|
+
"tool": "*",
|
|
37
|
+
"conditionMode": "all",
|
|
38
|
+
"conditions": [
|
|
39
|
+
{
|
|
40
|
+
"field": "command",
|
|
41
|
+
"op": "matches",
|
|
42
|
+
"value": "docker\\s+rm\\b",
|
|
43
|
+
"flags": "i"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"field": "command",
|
|
47
|
+
"op": "matches",
|
|
48
|
+
"value": "(^|\\s)(-f|--force)(\\s|$)",
|
|
49
|
+
"flags": "i"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"verdict": "block",
|
|
53
|
+
"reason": "Force-removing running containers is destructive — blocked by Docker shield"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "shield:docker:review-volume-rm",
|
|
57
|
+
"tool": "*",
|
|
58
|
+
"conditions": [
|
|
59
|
+
{
|
|
60
|
+
"field": "command",
|
|
61
|
+
"op": "matches",
|
|
62
|
+
"value": "docker\\s+volume\\s+rm\\s+",
|
|
63
|
+
"flags": "i"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"verdict": "review",
|
|
67
|
+
"reason": "Volume removal deletes persistent data and requires human approval (Docker shield)"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "shield:docker:review-stop-kill",
|
|
71
|
+
"tool": "*",
|
|
72
|
+
"conditions": [
|
|
73
|
+
{
|
|
74
|
+
"field": "command",
|
|
75
|
+
"op": "matches",
|
|
76
|
+
"value": "docker\\s+(stop|kill)\\s+",
|
|
77
|
+
"flags": "i"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"verdict": "review",
|
|
81
|
+
"reason": "Stopping or killing containers requires human approval (Docker shield)"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "shield:docker:review-image-rm",
|
|
85
|
+
"tool": "*",
|
|
86
|
+
"conditions": [
|
|
87
|
+
{
|
|
88
|
+
"field": "command",
|
|
89
|
+
"op": "matches",
|
|
90
|
+
"value": "docker\\s+image\\s+rm\\b",
|
|
91
|
+
"flags": "i"
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"verdict": "review",
|
|
95
|
+
"reason": "Image removal requires human approval (Docker shield)"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "shield:docker:review-rmi-force",
|
|
99
|
+
"tool": "*",
|
|
100
|
+
"conditionMode": "all",
|
|
101
|
+
"conditions": [
|
|
102
|
+
{
|
|
103
|
+
"field": "command",
|
|
104
|
+
"op": "matches",
|
|
105
|
+
"value": "docker\\s+rmi\\b",
|
|
106
|
+
"flags": "i"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"field": "command",
|
|
110
|
+
"op": "matches",
|
|
111
|
+
"value": "(^|\\s)(-f|--force)(\\s|$)",
|
|
112
|
+
"flags": "i"
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"verdict": "review",
|
|
116
|
+
"reason": "Force image removal requires human approval (Docker shield)"
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
"dangerousWords": []
|
|
120
|
+
}
|