thumbgate 1.27.18 → 1.27.20
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/.claude/commands/dashboard.md +15 -0
- package/.claude/commands/thumbgate-blocked.md +27 -0
- package/.claude/commands/thumbgate-dashboard.md +15 -0
- package/.claude/commands/thumbgate-doctor.md +30 -0
- package/.claude/commands/thumbgate-guard.md +36 -0
- package/.claude/commands/thumbgate-protect.md +30 -0
- package/.claude/commands/thumbgate-rules.md +30 -0
- package/.claude-plugin/plugin.json +4 -3
- package/.well-known/agentic-verify.txt +1 -0
- package/.well-known/llms.txt +33 -12
- package/.well-known/mcp/server-card.json +8 -8
- package/README.md +246 -30
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/codex/config.toml +2 -2
- package/adapters/gcp/dfcx-webhook-gate.js +295 -0
- package/adapters/gemini/function-declarations.json +1 -0
- package/adapters/letta/README.md +41 -0
- package/adapters/letta/thumbgate-letta-adapter.js +133 -0
- package/adapters/mcp/server-stdio.js +263 -11
- package/adapters/opencode/opencode.json +1 -1
- package/adapters/policy-engine/ethicore-guardian-client.js +68 -0
- package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +260 -0
- package/bench/observability-eval-suite.json +26 -0
- package/bench/thumbgate-bench.json +2 -2
- package/bin/cli.js +1456 -122
- package/bin/dashboard-cli.js +7 -0
- package/bin/postinstall.js +11 -27
- package/commands/dashboard.md +15 -0
- package/commands/thumbgate-dashboard.md +15 -0
- package/config/gate-classifier-routing.json +98 -0
- package/config/gate-templates.json +216 -0
- package/config/gates/claim-verification.json +12 -0
- package/config/gates/default.json +31 -2
- package/config/github-about.json +2 -2
- package/config/mcp-allowlists.json +23 -13
- package/config/merge-quality-checks.json +0 -1
- package/config/model-candidates.json +121 -6
- package/config/post-deploy-marketing-pages.json +80 -0
- package/config/tessl-tiles.json +1 -3
- package/openapi/openapi.yaml +12 -0
- package/package.json +225 -100
- package/public/about.html +162 -0
- package/public/agent-manager.html +179 -0
- package/public/agents-cost-savings.html +153 -0
- package/public/ai-malpractice-prevention.html +818 -0
- package/public/assets/brand/github-social-preview.png +0 -0
- package/public/assets/brand/thumbgate-icon-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
- package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
- package/public/assets/brand/thumbgate-logo-transparent.svg +28 -0
- package/public/assets/brand/thumbgate-mark-inline-v3.svg +18 -0
- package/public/assets/brand/thumbgate-mark-pro.svg +23 -0
- package/public/assets/brand/thumbgate-mark-team.svg +26 -0
- package/public/assets/brand/thumbgate-mark.svg +21 -0
- package/public/assets/brand/thumbgate-wordmark.svg +20 -0
- package/public/assets/claude-thumbgate-statusbar.svg +8 -0
- package/public/assets/codex-thumbgate-statusbar-test.svg +9 -0
- package/public/assets/legal-intake-control-flow.svg +66 -0
- package/public/blog.html +4 -4
- package/public/brand/thumbgate-mark.svg +19 -0
- package/public/brand/thumbgate-og.svg +16 -0
- package/public/chatgpt-app.html +330 -0
- package/public/codex-enterprise.html +123 -0
- package/public/codex-plugin.html +72 -20
- package/public/compare.html +31 -8
- package/public/dashboard.html +930 -166
- package/public/diagnostic.html +345 -0
- package/public/federal.html +2 -2
- package/public/guide.html +33 -13
- package/public/index.html +469 -111
- package/public/install.html +193 -0
- package/public/js/buyer-intent.js +672 -0
- package/public/learn.html +183 -18
- package/public/lessons.html +168 -10
- package/public/numbers.html +7 -7
- package/public/pricing.html +399 -0
- package/public/pro.html +34 -11
- package/scripts/action-receipts.js +324 -0
- package/scripts/activation-quickstart.js +187 -0
- package/scripts/agent-memory-lifecycle.js +211 -0
- package/scripts/agent-operations-planner.js +621 -0
- package/scripts/agent-readiness.js +20 -3
- package/scripts/agent-reward-model.js +53 -1
- package/scripts/ai-component-inventory.js +367 -0
- package/scripts/async-eval-observability.js +236 -0
- package/scripts/audit.js +65 -0
- package/scripts/auto-promote-gates.js +82 -10
- package/scripts/auto-wire-hooks.js +14 -0
- package/scripts/aws-blocks-guardrails.js +272 -0
- package/scripts/billing.js +93 -1
- package/scripts/bot-detection.js +61 -3
- package/scripts/build-metadata.js +50 -10
- package/scripts/classifier-routing.js +130 -0
- package/scripts/cli-feedback.js +4 -2
- package/scripts/cli-schema.js +97 -0
- package/scripts/cli-telemetry.js +6 -1
- package/scripts/commercial-offer.js +82 -2
- package/scripts/context-manager.js +74 -6
- package/scripts/dashboard-chat.js +332 -0
- package/scripts/dashboard.js +68 -2
- package/scripts/export-databricks-bundle.js +5 -1
- package/scripts/export-dpo-pairs.js +7 -2
- package/scripts/feedback-aggregate.js +281 -0
- package/scripts/feedback-loop.js +123 -1
- package/scripts/feedback-quality.js +87 -0
- package/scripts/feedback-sanitizer.js +105 -0
- package/scripts/filesystem-search.js +35 -10
- package/scripts/gate-stats.js +89 -0
- package/scripts/gates-engine.js +1176 -85
- package/scripts/gemini-embedding-policy.js +2 -1
- package/scripts/hook-runtime.js +20 -14
- package/scripts/hook-stop-anti-claim.js +301 -0
- package/scripts/hook-thumbgate-cache-updater.js +18 -2
- package/scripts/hybrid-feedback-context.js +142 -7
- package/scripts/install-shim.js +87 -0
- package/scripts/lesson-inference.js +8 -3
- package/scripts/lesson-search.js +17 -1
- package/scripts/license.js +10 -10
- package/scripts/llm-client.js +169 -4
- package/scripts/local-model-profile.js +15 -8
- package/scripts/mcp-config.js +7 -1
- package/scripts/mcp-oauth.js +293 -0
- package/scripts/memory-scope-readiness.js +159 -0
- package/scripts/meta-agent-loop.js +36 -0
- package/scripts/noop-detect.js +285 -0
- package/scripts/operational-integrity.js +39 -5
- package/scripts/oss-pr-opportunity-scout.js +35 -5
- package/scripts/parallel-workflow-orchestrator.js +293 -0
- package/scripts/plan-gate.js +243 -0
- package/scripts/plausible-domain-config.js +99 -0
- package/scripts/plausible-server-events.js +9 -6
- package/scripts/pro-local-dashboard.js +4 -4
- package/scripts/proxy-pointer-rag-guardrails.js +42 -1
- package/scripts/published-cli.js +0 -8
- package/scripts/qa-scenario-planner.js +136 -0
- package/scripts/rate-limiter.js +64 -13
- package/scripts/repeat-metric.js +137 -0
- package/scripts/secret-fixture-tokens.js +61 -0
- package/scripts/secret-redaction.js +166 -0
- package/scripts/secret-scanner.js +44 -5
- package/scripts/security-scanner.js +260 -10
- package/scripts/self-distill-agent.js +3 -1
- package/scripts/self-harness-optimizer.js +141 -0
- package/scripts/self-healing-check.js +193 -0
- package/scripts/self-protection.js +90 -0
- package/scripts/seo-gsd.js +916 -7
- package/scripts/silent-failure-cluster.js +531 -0
- package/scripts/statusline-cache-path.js +17 -2
- package/scripts/statusline-cache-read.js +57 -0
- package/scripts/statusline-local-stats.js +9 -1
- package/scripts/statusline-meta.js +28 -2
- package/scripts/statusline.sh +20 -4
- package/scripts/sync-telemetry-from-prod.js +374 -0
- package/scripts/telemetry-analytics.js +357 -0
- package/scripts/thompson-sampling.js +31 -10
- package/scripts/thumbgate-bench.js +16 -1
- package/scripts/thumbgate-search.js +85 -19
- package/scripts/tool-contract-validator.js +76 -0
- package/scripts/tool-registry.js +169 -1
- package/scripts/trajectory-scorer.js +63 -0
- package/scripts/vector-store.js +45 -0
- package/scripts/verify-marketing-pages-deployed.js +212 -0
- package/scripts/visitor-journey.js +172 -0
- package/scripts/workflow-sentinel.js +286 -53
- package/scripts/workspace-evolver.js +62 -2
- package/src/api/server.js +2683 -319
- package/.claude-plugin/marketplace.json +0 -85
- package/adapters/chatgpt/openapi.yaml +0 -1695
- package/scripts/bot-detector.js +0 -50
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* secret-redaction.js — the single, canonical secret-redaction helper.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS
|
|
7
|
+
* On 2026-06-10 a live Stripe `sk_live_` key was found in plaintext inside a captured
|
|
8
|
+
* `.thumbgate/conversation-window.jsonl`. ThumbGate must never become a secret-leak vector:
|
|
9
|
+
* secrets must be redacted (a) before any conversation/feedback/memory record lands on disk,
|
|
10
|
+
* and (b) before any training/analytics dataset is exported or shared.
|
|
11
|
+
*
|
|
12
|
+
* This module is the ONE place that owns redaction-at-rest. Capture writers and dataset
|
|
13
|
+
* exporters all call into it instead of each rolling their own. It is intentionally separate
|
|
14
|
+
* from `secret-scanner.js`: that module is the PreToolUse *scan/block* layer and deliberately
|
|
15
|
+
* tolerates `sk_test_`/`test_token` inside commands. For data persisted to disk we redact
|
|
16
|
+
* test-mode secrets too — a `sk_test_` key is still a credential we should not store.
|
|
17
|
+
*
|
|
18
|
+
* NOT REDACTED ON PURPOSE
|
|
19
|
+
* - Stripe publishable keys (`pk_live_`, `pk_test_`) are public by design. No pattern matches
|
|
20
|
+
* them, and the generic `key=value` pattern keys on `api_key|secret|token|...`, never on a
|
|
21
|
+
* bare `publishable_key`, so they are preserved verbatim.
|
|
22
|
+
*
|
|
23
|
+
* Properties:
|
|
24
|
+
* - Idempotent: re-redacting already-redacted text is a no-op (the `[REDACTED:id]` marker
|
|
25
|
+
* contains characters outside every value charset).
|
|
26
|
+
* - Non-mutating: `redactSecretsDeep` returns a redacted copy; inputs are untouched.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const REDACTED = (id) => `[REDACTED:${id}]`;
|
|
30
|
+
|
|
31
|
+
// Identifier names that denote a credential, used by the generic `key = value` pattern. Kept as
|
|
32
|
+
// two small matchers (instead of one mega-alternation) so the assignment regex stays simple.
|
|
33
|
+
const SECRET_KEY_NAME = /(?:api[_-]?key|secret|token|password|passwd|credential|private[_-]?key|access[_-]?token|client[_-]?secret)/i;
|
|
34
|
+
// Public identifiers whose values are not secrets (e.g. Stripe publishable `pk_*` keys). Excluded
|
|
35
|
+
// so `publishable_key = pk_live_…` is preserved verbatim.
|
|
36
|
+
const PUBLIC_KEY_NAME = /publishable|public/i;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Ordered list of redaction patterns. Order matters: the most specific provider patterns run
|
|
40
|
+
* before the broad `key=value` fallback so a known secret keeps its precise label. Every regex
|
|
41
|
+
* is `g` (and case-insensitive where appropriate) so all occurrences are replaced.
|
|
42
|
+
*
|
|
43
|
+
* `replace` may be a string or a function `(match, ...groups) => string`.
|
|
44
|
+
*/
|
|
45
|
+
const SECRET_REDACTION_PATTERNS = [
|
|
46
|
+
// PEM / OpenSSH private key blocks — redact the whole block.
|
|
47
|
+
{
|
|
48
|
+
id: 'private_key_block',
|
|
49
|
+
label: 'Private key block',
|
|
50
|
+
regex: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----[\s\S]+?-----END (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/g,
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Stripe secret-bearing keys. Publishable keys (pk_*) are intentionally absent.
|
|
54
|
+
{ id: 'stripe_live_secret', label: 'Stripe live secret key', regex: /\bsk_live_[A-Za-z0-9]{8,}/g },
|
|
55
|
+
{ id: 'stripe_test_secret', label: 'Stripe test secret key', regex: /\bsk_test_[A-Za-z0-9]{8,}/g },
|
|
56
|
+
{ id: 'stripe_restricted_live', label: 'Stripe restricted live key', regex: /\brk_live_[A-Za-z0-9]{8,}/g },
|
|
57
|
+
{ id: 'stripe_restricted_test', label: 'Stripe restricted test key', regex: /\brk_test_[A-Za-z0-9]{8,}/g },
|
|
58
|
+
{ id: 'stripe_webhook_secret', label: 'Stripe webhook signing secret', regex: /\bwhsec_[A-Za-z0-9]{8,}/g },
|
|
59
|
+
|
|
60
|
+
// Anthropic / OpenAI. Run before the legacy `sk-` pattern so the precise label wins.
|
|
61
|
+
{ id: 'anthropic_api_key', label: 'Anthropic API key', regex: /\bsk-ant-[A-Za-z0-9_-]{16,}/g },
|
|
62
|
+
{ id: 'openai_project_key', label: 'OpenAI project key', regex: /\bsk-proj-[A-Za-z0-9_-]{16,}/g },
|
|
63
|
+
{ id: 'openai_api_key', label: 'OpenAI API key', regex: /\bsk-[A-Za-z0-9]{20,}/g },
|
|
64
|
+
|
|
65
|
+
// GitHub tokens.
|
|
66
|
+
{ id: 'github_pat', label: 'GitHub personal access token', regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{16,}/g },
|
|
67
|
+
{ id: 'github_fine_grained_pat', label: 'GitHub fine-grained token', regex: /\bgithub_pat_\w{20,}/g },
|
|
68
|
+
|
|
69
|
+
// Slack tokens.
|
|
70
|
+
{ id: 'slack_token', label: 'Slack token', regex: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g },
|
|
71
|
+
|
|
72
|
+
// Google API key.
|
|
73
|
+
{ id: 'google_api_key', label: 'Google API key', regex: /\bAIza[0-9A-Za-z_-]{35}/g },
|
|
74
|
+
|
|
75
|
+
// AWS access key id (long-lived AKIA + temporary ASIA).
|
|
76
|
+
{ id: 'aws_access_key', label: 'AWS access key id', regex: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g },
|
|
77
|
+
|
|
78
|
+
// JSON Web Tokens.
|
|
79
|
+
{
|
|
80
|
+
id: 'jwt',
|
|
81
|
+
label: 'JSON Web Token',
|
|
82
|
+
regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9._-]{8,}\.[A-Za-z0-9._-]{8,}/g,
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
// `Authorization: Bearer <token>` style. Keep the scheme word, redact the credential.
|
|
86
|
+
{
|
|
87
|
+
id: 'bearer_token',
|
|
88
|
+
label: 'Bearer token',
|
|
89
|
+
regex: /\b(Bearer|Token)\s+[a-z0-9._~+/=-]{16,}/gi,
|
|
90
|
+
replace: (_m, scheme) => `${scheme} ${REDACTED('bearer_token')}`,
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Generic `<name> = <value>` / `<name>: "<value>"` assignment with a 16+ char whitespace-free
|
|
94
|
+
// value. A simple matcher finds every assignment; the replace fn redacts only when the name is a
|
|
95
|
+
// credential (SECRET_KEY_NAME) and not a public identifier (PUBLIC_KEY_NAME), so `publishable_key`
|
|
96
|
+
// is preserved. Runs last so provider-specific labels are preferred.
|
|
97
|
+
{
|
|
98
|
+
id: 'secret_assignment',
|
|
99
|
+
label: 'Secret assignment',
|
|
100
|
+
regex: /\b([\w.-]{2,40})(\s*[:=]\s*["']?)([A-Za-z0-9._+=~/-]{16,})(["']?)/g,
|
|
101
|
+
replace: (match, key, pre, _value, post) =>
|
|
102
|
+
(SECRET_KEY_NAME.test(key) && !PUBLIC_KEY_NAME.test(key))
|
|
103
|
+
? `${key}${pre}${REDACTED('secret_assignment')}${post}`
|
|
104
|
+
: match,
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Redact secrets from a single string. Returns the input unchanged when it is not a non-empty
|
|
110
|
+
* string. Each pattern's lastIndex is reset before use so the module-level (stateful, `g`-flag)
|
|
111
|
+
* regexes are safe to reuse across calls.
|
|
112
|
+
*
|
|
113
|
+
* @param {string} text
|
|
114
|
+
* @returns {string}
|
|
115
|
+
*/
|
|
116
|
+
function redactSecrets(text) {
|
|
117
|
+
if (typeof text !== 'string' || text.length === 0) return text;
|
|
118
|
+
let out = text;
|
|
119
|
+
for (const pattern of SECRET_REDACTION_PATTERNS) {
|
|
120
|
+
pattern.regex.lastIndex = 0;
|
|
121
|
+
const replacement = pattern.replace || REDACTED(pattern.id);
|
|
122
|
+
out = out.replace(pattern.regex, replacement);
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Returns true if `text` contains at least one redactable secret.
|
|
129
|
+
* @param {string} text
|
|
130
|
+
* @returns {boolean}
|
|
131
|
+
*/
|
|
132
|
+
function containsSecret(text) {
|
|
133
|
+
if (typeof text !== 'string' || text.length === 0) return false;
|
|
134
|
+
return SECRET_REDACTION_PATTERNS.some((pattern) => {
|
|
135
|
+
pattern.regex.lastIndex = 0;
|
|
136
|
+
return pattern.regex.test(text);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Recursively redact every string contained in `value`. Objects and arrays are deep-copied so
|
|
142
|
+
* the input is never mutated; non-string primitives pass through untouched. Object keys are left
|
|
143
|
+
* as-is (only values are redacted).
|
|
144
|
+
*
|
|
145
|
+
* @param {*} value
|
|
146
|
+
* @returns {*} redacted copy
|
|
147
|
+
*/
|
|
148
|
+
function redactSecretsDeep(value) {
|
|
149
|
+
if (typeof value === 'string') return redactSecrets(value);
|
|
150
|
+
if (Array.isArray(value)) return value.map((item) => redactSecretsDeep(item));
|
|
151
|
+
if (value && typeof value === 'object') {
|
|
152
|
+
const out = {};
|
|
153
|
+
for (const [key, val] of Object.entries(value)) {
|
|
154
|
+
out[key] = redactSecretsDeep(val);
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = {
|
|
162
|
+
SECRET_REDACTION_PATTERNS,
|
|
163
|
+
redactSecrets,
|
|
164
|
+
redactSecretsDeep,
|
|
165
|
+
containsSecret,
|
|
166
|
+
};
|
|
@@ -55,6 +55,11 @@ const BASH_SECRET_READ_PREFIXES = [
|
|
|
55
55
|
];
|
|
56
56
|
|
|
57
57
|
const EDIT_LIKE_TOOLS = new Set(['Edit', 'Write', 'MultiEdit']);
|
|
58
|
+
const SAFE_SECRET_STORAGE_DIRS = [
|
|
59
|
+
'.resume_secrets',
|
|
60
|
+
'.thumbgate/secrets',
|
|
61
|
+
'.config/thumbgate',
|
|
62
|
+
];
|
|
58
63
|
|
|
59
64
|
function redactText(text) {
|
|
60
65
|
if (!text) return '';
|
|
@@ -172,6 +177,13 @@ function heuristicScanText(text, source = 'text') {
|
|
|
172
177
|
pattern.regex.lastIndex = 0;
|
|
173
178
|
let match = pattern.regex.exec(input);
|
|
174
179
|
while (match) {
|
|
180
|
+
// Safe test key bypass
|
|
181
|
+
const matchedString = match[0].toLowerCase();
|
|
182
|
+
if (pattern.id === 'generic_assignment' && (matchedString.includes('sk_test_') || matchedString.includes('test_token'))) {
|
|
183
|
+
match = pattern.regex.exec(input);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
175
187
|
findings.push({
|
|
176
188
|
id: pattern.id,
|
|
177
189
|
label: pattern.label,
|
|
@@ -295,6 +307,26 @@ function resolvePathToken(token, cwd) {
|
|
|
295
307
|
return path.join(cwd || process.cwd(), normalized);
|
|
296
308
|
}
|
|
297
309
|
|
|
310
|
+
function normalizePathForPolicy(filePath) {
|
|
311
|
+
return path.resolve(String(filePath || '').replace(/^~(?=\/|$)/, os.homedir()));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function isSafeSecretStoragePath(filePath) {
|
|
315
|
+
if (!filePath) return false;
|
|
316
|
+
const normalized = normalizePathForPolicy(filePath);
|
|
317
|
+
const home = normalizePathForPolicy(os.homedir());
|
|
318
|
+
return SAFE_SECRET_STORAGE_DIRS.some((dir) => {
|
|
319
|
+
const allowedRoot = path.join(home, dir);
|
|
320
|
+
return normalized === allowedRoot || normalized.startsWith(`${allowedRoot}${path.sep}`);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function isSafeSecretStorageWrite(toolName, toolInput = {}, cwd = process.cwd()) {
|
|
325
|
+
if (!EDIT_LIKE_TOOLS.has(toolName)) return false;
|
|
326
|
+
const paths = getToolInputPaths(toolInput, cwd);
|
|
327
|
+
return paths.length > 0 && paths.every((filePath) => isSafeSecretStoragePath(filePath));
|
|
328
|
+
}
|
|
329
|
+
|
|
298
330
|
function scanBashCommand(command, options = {}) {
|
|
299
331
|
const cwd = options.cwd || process.cwd();
|
|
300
332
|
const findings = [];
|
|
@@ -347,6 +379,7 @@ function scanHookInput(input = {}, options = {}) {
|
|
|
347
379
|
let provider = resolveProvider(options.provider);
|
|
348
380
|
let commandHash = null;
|
|
349
381
|
let fileHashes = [];
|
|
382
|
+
const safeSecretStorageWrite = isSafeSecretStorageWrite(toolName, toolInput, cwd);
|
|
350
383
|
|
|
351
384
|
const contentFields = [
|
|
352
385
|
toolInput.content,
|
|
@@ -376,11 +409,13 @@ function scanHookInput(input = {}, options = {}) {
|
|
|
376
409
|
}
|
|
377
410
|
}
|
|
378
411
|
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
412
|
+
if (!safeSecretStorageWrite) {
|
|
413
|
+
for (const content of contentFields) {
|
|
414
|
+
const result = scanText(content, { provider, source: 'tool_input' });
|
|
415
|
+
if (result.detected) {
|
|
416
|
+
provider = result.provider;
|
|
417
|
+
findings.push(...result.findings);
|
|
418
|
+
}
|
|
384
419
|
}
|
|
385
420
|
}
|
|
386
421
|
|
|
@@ -402,6 +437,8 @@ function buildSafeSummary(findings, prefix) {
|
|
|
402
437
|
module.exports = {
|
|
403
438
|
SECRET_PATTERNS,
|
|
404
439
|
SECRET_FILE_PATTERNS,
|
|
440
|
+
SAFE_SECRET_STORAGE_DIRS,
|
|
441
|
+
EDIT_LIKE_TOOLS,
|
|
405
442
|
redactText,
|
|
406
443
|
resolveProvider,
|
|
407
444
|
scanText,
|
|
@@ -409,6 +446,8 @@ module.exports = {
|
|
|
409
446
|
scanBashCommand,
|
|
410
447
|
scanHookInput,
|
|
411
448
|
classifySecretPath,
|
|
449
|
+
isSafeSecretStoragePath,
|
|
450
|
+
isSafeSecretStorageWrite,
|
|
412
451
|
buildSafeSummary,
|
|
413
452
|
tokenizeCommand,
|
|
414
453
|
};
|