fullcourtdefense-cli 1.14.1 → 1.14.2

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.
@@ -38,29 +38,60 @@ const crypto = __importStar(require("crypto"));
38
38
  const fs = __importStar(require("fs"));
39
39
  const os = __importStar(require("os"));
40
40
  const path = __importStar(require("path"));
41
- /** Patterns inspired by GhostHunt + secrets-audit-mcp (local-only, no network). */
41
+ /**
42
+ * Patterns aligned with Gitleaks / TruffleHog / GitHub push-protection defaults
43
+ * (local-only, no network). ORDER MATTERS: specific prefixes (sk-proj-, sk-ant-,
44
+ * sk-or-) must come before the generic OpenAI sk- pattern — matches are deduped
45
+ * by the matched string, first pattern wins.
46
+ */
42
47
  const SECRET_PATTERNS = [
43
- { re: /\bsk-[a-zA-Z0-9_-]{20,}\b/, provider: 'openai', severity: 'critical', title: 'OpenAI API key' },
44
- { re: /\bsk-proj-[a-zA-Z0-9_-]{20,}\b/, provider: 'openai', severity: 'critical', title: 'OpenAI project key' },
45
- { re: /\bsk-ant-[a-zA-Z0-9_-]{20,}\b/, provider: 'anthropic', severity: 'critical', title: 'Anthropic API key' },
46
- { re: /\bghp_[a-zA-Z0-9]{36,}\b/, provider: 'github', severity: 'critical', title: 'GitHub personal access token' },
47
- { re: /\bgho_[a-zA-Z0-9]{36,}\b/, provider: 'github', severity: 'critical', title: 'GitHub OAuth token' },
48
- { re: /\bgithub_pat_[a-zA-Z0-9_]{20,}\b/, provider: 'github', severity: 'critical', title: 'GitHub fine-grained PAT' },
49
- { re: /\bAKIA[0-9A-Z]{16}\b/, provider: 'aws', severity: 'critical', title: 'AWS access key ID' },
50
- { re: /\bAIza[0-9A-Za-z_-]{35}\b/, provider: 'google', severity: 'critical', title: 'Google API key' },
51
- { re: /\bxox[baprs]-[a-zA-Z0-9-]{10,}\b/, provider: 'slack', severity: 'high', title: 'Slack token' },
52
- { re: /\bsk_live_[0-9a-zA-Z]{24,}\b/, provider: 'stripe', severity: 'critical', title: 'Stripe live secret key' },
53
- { re: /\brk_live_[0-9a-zA-Z]{24,}\b/, provider: 'stripe', severity: 'critical', title: 'Stripe restricted key' },
54
- { re: /\bSG\.[a-zA-Z0-9_-]{20,}\.[a-zA-Z0-9_-]{20,}\b/, provider: 'sendgrid', severity: 'high', title: 'SendGrid API key' },
55
- { re: /\bsk-[0-9a-fA-F]{32}\b/, provider: 'twilio', severity: 'high', title: 'Twilio API key' },
56
- { re: /\bhf_[a-zA-Z0-9]{30,}\b/, provider: 'huggingface', severity: 'high', title: 'HuggingFace token' },
57
- { re: /\bag_org_[a-zA-Z0-9_-]{20,}\b/, provider: 'fullcourtdefense', severity: 'high', title: 'FullCourtDefense org API key' },
58
- { re: /\bshsk_[a-zA-Z0-9]{20,}\b/, provider: 'fullcourtdefense', severity: 'high', title: 'Shield API key' },
59
- { re: /\beyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/, provider: 'jwt', severity: 'medium', title: 'JWT token' },
60
- { re: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/, provider: 'private_key', severity: 'critical', title: 'Private key material' },
61
- { re: /postgres(?:ql)?:\/\/[^\s'"]+:[^\s'"]+@/i, provider: 'database', severity: 'critical', title: 'Database connection string' },
62
- { re: /mongodb(?:\+srv)?:\/\/[^\s'"]+:[^\s'"]+@/i, provider: 'database', severity: 'critical', title: 'MongoDB connection string' },
48
+ { re: /\bsk-proj-[a-zA-Z0-9_-]{20,}\b/, provider: 'openai', severity: 'critical', title: 'OpenAI project key', impact: 'full OpenAI API usage billed to your account' },
49
+ { re: /\bsk-ant-[a-zA-Z0-9_-]{20,}\b/, provider: 'anthropic', severity: 'critical', title: 'Anthropic API key', impact: 'full Anthropic API usage billed to your account' },
50
+ { re: /\bsk-or-[a-zA-Z0-9_-]{20,}\b/, provider: 'openrouter', severity: 'high', title: 'OpenRouter API key', impact: 'LLM usage across providers billed to your account' },
51
+ { re: /\bsk-[a-zA-Z0-9_-]{20,}\b/, provider: 'openai', severity: 'critical', title: 'OpenAI API key', impact: 'full OpenAI API usage billed to your account' },
52
+ { re: /\bgsk_[a-zA-Z0-9]{20,}\b/, provider: 'groq', severity: 'high', title: 'Groq API key', impact: 'LLM inference billed to your account' },
53
+ { re: /\bxai-[a-zA-Z0-9]{20,}\b/, provider: 'xai', severity: 'high', title: 'xAI API key', impact: 'LLM inference billed to your account' },
54
+ { re: /\bghp_[a-zA-Z0-9]{36,}\b/, provider: 'github', severity: 'critical', title: 'GitHub personal access token', impact: 'read/write access to your repositories' },
55
+ { re: /\bgho_[a-zA-Z0-9]{36,}\b/, provider: 'github', severity: 'critical', title: 'GitHub OAuth token', impact: 'read/write access to your repositories' },
56
+ { re: /\bghs_[a-zA-Z0-9]{36,}\b/, provider: 'github', severity: 'critical', title: 'GitHub app server token', impact: 'app-scoped repository access' },
57
+ { re: /\bgithub_pat_[a-zA-Z0-9_]{20,}\b/, provider: 'github', severity: 'critical', title: 'GitHub fine-grained PAT', impact: 'scoped access to your repositories' },
58
+ { re: /\bglpat-[a-zA-Z0-9_-]{20,}\b/, provider: 'gitlab', severity: 'critical', title: 'GitLab personal access token', impact: 'read/write access to your GitLab projects' },
59
+ { re: /\bAKIA[0-9A-Z]{16}\b/, provider: 'aws', severity: 'critical', title: 'AWS access key ID', impact: 'AWS API access (paired with its secret key)' },
60
+ { re: /\baws_secret_access_key\s*[=:]\s*['"]?[A-Za-z0-9/+=]{40}\b/i, provider: 'aws', severity: 'critical', title: 'AWS secret access key', impact: 'full AWS API access for the paired key ID' },
61
+ { re: /\bAIza[0-9A-Za-z_-]{35}\b/, provider: 'google', severity: 'critical', title: 'Google API key', impact: 'Google Cloud API usage billed to your project' },
62
+ { re: /\bxox[baprs]-[a-zA-Z0-9-]{10,}\b/, provider: 'slack', severity: 'high', title: 'Slack token', impact: 'read/post messages as your Slack app or user' },
63
+ { re: /\bhooks\.slack\.com\/services\/T[a-zA-Z0-9]+\/B[a-zA-Z0-9]+\/[a-zA-Z0-9]+\b/, provider: 'slack', severity: 'high', title: 'Slack incoming webhook URL', impact: 'post arbitrary messages into your Slack channel' },
64
+ { re: /\bsk_live_[0-9a-zA-Z]{24,}\b/, provider: 'stripe', severity: 'critical', title: 'Stripe live secret key', impact: 'charges, refunds, and customer data in live mode' },
65
+ { re: /\brk_live_[0-9a-zA-Z]{24,}\b/, provider: 'stripe', severity: 'critical', title: 'Stripe restricted key', impact: 'scoped live-mode payment operations' },
66
+ { re: /\bSG\.[a-zA-Z0-9_-]{20,}\.[a-zA-Z0-9_-]{20,}\b/, provider: 'sendgrid', severity: 'high', title: 'SendGrid API key', impact: 'send email from your verified domains (phishing risk)' },
67
+ { re: /\bSK[0-9a-fA-F]{32}\b/, provider: 'twilio', severity: 'high', title: 'Twilio API key SID', impact: 'send SMS/calls billed to your account' },
68
+ { re: /\bhf_[a-zA-Z0-9]{30,}\b/, provider: 'huggingface', severity: 'high', title: 'HuggingFace token', impact: 'access to your private models and datasets' },
69
+ { re: /\bnpm_[a-zA-Z0-9]{36,}\b/, provider: 'npm', severity: 'high', title: 'npm access token', impact: 'publish packages as you (supply-chain risk)' },
70
+ { re: /\bhvs\.[a-zA-Z0-9_-]{20,}\b/, provider: 'vault', severity: 'critical', title: 'HashiCorp Vault token', impact: 'access to every secret the token policy allows' },
71
+ { re: /\bag_org_[a-zA-Z0-9_-]{20,}\b/, provider: 'fullcourtdefense', severity: 'high', title: 'FullCourtDefense org API key', impact: 'org-scoped scan and shield API access' },
72
+ { re: /\bshsk_[a-zA-Z0-9]{20,}\b/, provider: 'fullcourtdefense', severity: 'high', title: 'Shield API key', impact: 'submit scans as this shield' },
73
+ { re: /\beyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/, provider: 'jwt', severity: 'medium', title: 'JWT token', impact: 'impersonate the session until the token expires' },
74
+ { re: /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY(?: BLOCK)?-----/, provider: 'private_key', severity: 'critical', title: 'Private key material', impact: 'authenticate as the key owner' },
75
+ { re: /postgres(?:ql)?:\/\/[^\s'"]+:[^\s'"]+@/i, provider: 'database', severity: 'critical', title: 'Database connection string', impact: 'direct database read/write access' },
76
+ { re: /mysql:\/\/[^\s'"]+:[^\s'"]+@/i, provider: 'database', severity: 'critical', title: 'MySQL connection string', impact: 'direct database read/write access' },
77
+ { re: /rediss?:\/\/[^\s'"]*:[^\s'"]+@/i, provider: 'database', severity: 'critical', title: 'Redis connection string', impact: 'direct cache/queue access' },
78
+ { re: /mongodb(?:\+srv)?:\/\/[^\s'"]+:[^\s'"]+@/i, provider: 'database', severity: 'critical', title: 'MongoDB connection string', impact: 'direct database read/write access' },
63
79
  ];
80
+ /** Provider-specific rotation guidance (falls back to the generic advice). */
81
+ const PROVIDER_REMEDIATION = {
82
+ openai: 'Revoke it at platform.openai.com → API keys, create a new one, and store it in a secret manager or OS keychain — not in plain files.',
83
+ anthropic: 'Revoke it in the Anthropic console → API keys, create a new one, and store it in a secret manager — not in plain files.',
84
+ github: 'Revoke it at github.com → Settings → Developer settings → Tokens, then create a minimally-scoped replacement.',
85
+ gitlab: 'Revoke it in GitLab → Preferences → Access tokens, then create a minimally-scoped replacement.',
86
+ aws: 'Deactivate the key in IAM → Security credentials, rotate, and prefer short-lived credentials (SSO/assume-role) over static keys.',
87
+ google: 'Regenerate the key in Google Cloud console → APIs & Services → Credentials, and add API/IP restrictions to the new key.',
88
+ stripe: 'Roll the key in the Stripe dashboard → Developers → API keys. Live keys should only exist in your deployment platform\u2019s secret store.',
89
+ sendgrid: 'Delete this key in SendGrid → Settings → API Keys and create a scoped replacement. SendGrid keys are shown once — the new one goes straight into a secret manager.',
90
+ slack: 'Regenerate the token/webhook in your Slack app settings (api.slack.com/apps) and update the consumer.',
91
+ npm: 'Revoke it with `npm token revoke` and create a granular automation token with the minimum package scope.',
92
+ vault: 'Revoke the token (`vault token revoke`) and issue short-TTL tokens instead of long-lived ones.',
93
+ database: 'Change the database password and move the connection string to a secret manager; restrict the DB user\u2019s privileges.',
94
+ };
64
95
  const SKIP_DIR_NAMES = new Set([
65
96
  'node_modules', '.git', 'vendor', 'dist', 'build', '__pycache__', '.venv', 'venv',
66
97
  'Library', 'Caches', 'Cache', 'Temp', 'tmp', 'Windows', 'Program Files', 'Program Files (x86)',
@@ -98,11 +129,17 @@ function scanTextLines(filePath, content, category, out, seen) {
98
129
  const line = lines[i];
99
130
  if (!line.trim() || line.trim().startsWith('#'))
100
131
  continue;
132
+ // First (most specific) pattern wins per matched string — prevents e.g. an
133
+ // sk-ant- Anthropic key from also being reported as a generic OpenAI sk- key.
134
+ const matchedRaws = [];
101
135
  for (const pattern of SECRET_PATTERNS) {
102
136
  const match = line.match(pattern.re);
103
137
  if (!match)
104
138
  continue;
105
139
  const raw = match[0];
140
+ if (matchedRaws.some(prev => prev.includes(raw) || raw.includes(prev)))
141
+ continue;
142
+ matchedRaws.push(raw);
106
143
  const id = findingId([filePath, String(i + 1), pattern.provider, raw.slice(0, 12)]);
107
144
  if (seen.has(id))
108
145
  continue;
@@ -115,11 +152,14 @@ function scanTextLines(filePath, content, category, out, seen) {
115
152
  filePath,
116
153
  lineNumber: i + 1,
117
154
  title: pattern.title,
118
- detail: `Matched ${pattern.provider} pattern in ${path.basename(filePath)}`,
155
+ detail: pattern.impact
156
+ ? `Matched ${pattern.provider} pattern in ${path.basename(filePath)}. If leaked: ${pattern.impact}.`
157
+ : `Matched ${pattern.provider} pattern in ${path.basename(filePath)}`,
119
158
  redactedPreview: redact(raw),
120
159
  remediation: pattern.provider === 'private_key'
121
160
  ? 'Move keys to a secure store; never commit private keys. Use ssh-agent or encrypted keys.'
122
- : 'Rotate this credential immediately and store it in a secret manager or OS keychain — not in plain files.',
161
+ : PROVIDER_REMEDIATION[pattern.provider]
162
+ || 'Rotate this credential immediately and store it in a secret manager or OS keychain — not in plain files.',
123
163
  });
124
164
  if (out.length >= MAX_FINDINGS)
125
165
  return;
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.14.1"
2
+ "version": "1.14.2"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.14.1",
3
+ "version": "1.14.2",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {