fullcourtdefense-cli 1.14.5 → 1.14.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.
|
@@ -41,16 +41,12 @@ const path = __importStar(require("path"));
|
|
|
41
41
|
const MAX_FILES = 150;
|
|
42
42
|
const MAX_FILE_BYTES = 512 * 1024;
|
|
43
43
|
const EXCERPT_LEN = 180;
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
{ re: /\b(?:curl|wget|webhook|exfiltrate|post to slack|send email|http request)\b/i, tag: 'network_exfil' },
|
|
51
|
-
{ re: /\b(?:send email|post to slack|transfer funds|delete all|publish package|npm publish|git push)\b/i, tag: 'destructive_actions' },
|
|
52
|
-
{ re: /\b(?:api[_-]?key|secret|password|token)\s*[:=]/i, tag: 'embedded_secret_ref' },
|
|
53
|
-
];
|
|
44
|
+
const NEGATED_RISK_LINE = /\b(?:do not|does not|don't|not an? (?:instruction|permission)|not permission|never|avoid|forbid|block|wrong|bad|dangerous|must not)\b/i;
|
|
45
|
+
const ACTIVE_AGENT_SUBJECT = /\b(?:agent|assistant|model|tool|mcp|skill|it)\b/i;
|
|
46
|
+
const EMBEDDED_SECRET_VALUE = /\b(?:sk-(?:proj-)?[A-Za-z0-9_-]{24,}|ghp_[A-Za-z0-9_]{24,}|github_pat_[A-Za-z0-9_]{24,}|glpat-[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{24,}|hf_[A-Za-z0-9]{24,}|gsk_[A-Za-z0-9]{24,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{28,}|xox[baprs]-[A-Za-z0-9-]{20,})\b/;
|
|
47
|
+
function stripCodeBlocks(content) {
|
|
48
|
+
return content.replace(/```[\s\S]*?```/g, '');
|
|
49
|
+
}
|
|
54
50
|
function stableId(parts) {
|
|
55
51
|
return crypto.createHash('sha256').update(parts.join('|')).digest('hex').slice(0, 16);
|
|
56
52
|
}
|
|
@@ -66,11 +62,49 @@ function readAgentFile(filePath) {
|
|
|
66
62
|
return null;
|
|
67
63
|
}
|
|
68
64
|
}
|
|
65
|
+
function lineHasRiskIntent(line, action, target) {
|
|
66
|
+
if (NEGATED_RISK_LINE.test(line))
|
|
67
|
+
return false;
|
|
68
|
+
if (!action.test(line))
|
|
69
|
+
return false;
|
|
70
|
+
return target ? target.test(line) : true;
|
|
71
|
+
}
|
|
72
|
+
function lineHasOrderedRiskIntent(line, pattern) {
|
|
73
|
+
return !NEGATED_RISK_LINE.test(line) && pattern.test(line);
|
|
74
|
+
}
|
|
75
|
+
function lineHasActiveAgentRisk(line, pattern) {
|
|
76
|
+
return !NEGATED_RISK_LINE.test(line) && ACTIVE_AGENT_SUBJECT.test(line) && pattern.test(line);
|
|
77
|
+
}
|
|
69
78
|
function analyzeContent(content) {
|
|
70
79
|
const tags = new Set();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
const text = stripCodeBlocks(content);
|
|
81
|
+
const lines = text.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
|
82
|
+
const whole = lines.join('\n');
|
|
83
|
+
if (/\b(?:api[_-]?key|secret|password|token)\s*[:=]\s*/i.test(whole) && EMBEDDED_SECRET_VALUE.test(whole)) {
|
|
84
|
+
tags.add('embedded_secret_ref');
|
|
85
|
+
}
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
if (lineHasActiveAgentRisk(line, /\b(?:allow|can|may|must|should|use|execute|invoke|enable|grant)\b.{0,100}\b(?:shell|bash|exec|subprocess|run_command|command runner|terminal)\b/i)) {
|
|
88
|
+
tags.add('allows_shell');
|
|
89
|
+
}
|
|
90
|
+
if (lineHasActiveAgentRisk(line, /\b(?:allow|can|may|must|should|read|write|access|trust|grant)\b.{0,100}\b(?:read any file|full filesystem|entire filesystem|trusted workspace|workspace trust|\/etc\/passwd|sudo)\b/i)) {
|
|
91
|
+
tags.add('broad_filesystem');
|
|
92
|
+
}
|
|
93
|
+
if (lineHasOrderedRiskIntent(line, /\b(?:ignore (?:all )?(?:previous )?instructions|bypass safety|bypass guardrails|DAN mode)\b/i)) {
|
|
94
|
+
tags.add('jailbreak_hint');
|
|
95
|
+
}
|
|
96
|
+
if (lineHasOrderedRiskIntent(line, /\b(?:always allow|disable security|skip validation|bypass validation)\b/i)) {
|
|
97
|
+
tags.add('weak_guardrails');
|
|
98
|
+
}
|
|
99
|
+
if (lineHasRiskIntent(line, /\b(?:auto[- ]?approve|without asking|unattended|non[- ]?interactive|yolo mode)\b/i)) {
|
|
100
|
+
tags.add('auto_approval');
|
|
101
|
+
}
|
|
102
|
+
if (lineHasOrderedRiskIntent(line, /\b(?:exfiltrate\b|(?:send|post|upload|curl|wget)\b.{0,120}\b(?:secret|token|credential|api[_-]?key|env|customer|database|private|confidential)\b.{0,120}\b(?:webhook|https?:|external|slack|email|url)|(?:send|post|upload|curl|wget)\b.{0,120}\b(?:webhook|https?:|external|slack|email|url)\b.{0,120}\b(?:secret|token|credential|api[_-]?key|env|customer|database|private|confidential))\b/i)) {
|
|
103
|
+
tags.add('network_exfil');
|
|
104
|
+
}
|
|
105
|
+
if (lineHasOrderedRiskIntent(line, /\b(?:allow|can|may|must|should|run|execute|auto[- ]?approve)\b.{0,120}\b(?:transfer funds|delete all|publish package|npm publish|git push|drop table|remove directory)\b/i)) {
|
|
106
|
+
tags.add('destructive_actions');
|
|
107
|
+
}
|
|
74
108
|
}
|
|
75
109
|
return [...tags];
|
|
76
110
|
}
|
|
@@ -127,8 +127,8 @@ function computeBlastRadius(input) {
|
|
|
127
127
|
combos.push({
|
|
128
128
|
id: 'permissive_rules_plus_secrets',
|
|
129
129
|
severity: 'high',
|
|
130
|
-
title: '
|
|
131
|
-
detail:
|
|
130
|
+
title: 'Agent can reach local credentials',
|
|
131
|
+
detail: `${criticalSecrets.length} high-risk credential(s) are on this machine, and agent instructions allow broad actions such as shell, filesystem, auto-approval, or network sends. If the agent is hijacked, those credentials could be read or used.`,
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
if (packagePublisherSecrets.length > 0 && riskyAgentFiles.some(f => f.riskTags.includes('destructive_actions')
|
|
@@ -137,8 +137,8 @@ function computeBlastRadius(input) {
|
|
|
137
137
|
combos.push({
|
|
138
138
|
id: 'supply_chain_token_plus_agent_actions',
|
|
139
139
|
severity: 'high',
|
|
140
|
-
title: '
|
|
141
|
-
detail: `${packagePublisherSecrets.length} package/repository
|
|
140
|
+
title: 'Publishing token exposed to agent actions',
|
|
141
|
+
detail: `${packagePublisherSecrets.length} package/repository token(s) are on this machine while agent rules allow command execution, auto-approval, or publishing actions. A hijacked agent could publish packages or push code with those credentials.`,
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
if (agentFiles.length >= 5 && !clientCoverage.some(c => c.cursorHooksInstalled)) {
|
package/dist/version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullcourtdefense-cli",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.7",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"test:cmd-guard": "npm run build && node scripts/test-cmd-guard.js",
|
|
22
22
|
"test:posix-guard": "npm run build && node scripts/test-posix-guard.js",
|
|
23
23
|
"test:secret-posture": "npm run build && node scripts/test-secret-posture-fixtures.js",
|
|
24
|
+
"test:agent-file-posture": "npm run build && node scripts/test-agent-file-posture.js",
|
|
25
|
+
"test:realworld-posture": "npm run build && node scripts/test-realworld-posture-fixtures.js",
|
|
24
26
|
"test:posture-blast": "npm run build && node scripts/test-posture-blast-radius.js",
|
|
25
27
|
"test:ping-e2e": "npm run build && node scripts/test-ping-e2e.js",
|
|
26
28
|
"prepublishOnly": "npm run build"
|