@wrongstack/plugins 0.295.0 → 0.296.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.
- package/dist/accessibility-auditor/index.d.ts.map +1 -1
- package/dist/accessibility-auditor.js +31 -3
- package/dist/agent-handoff/index.d.ts.map +1 -1
- package/dist/agent-handoff.js +30 -1
- package/dist/auto-doc/index.d.ts.map +1 -1
- package/dist/auto-doc.js +3 -1
- package/dist/auto-i18n-extractor.js +5 -0
- package/dist/branch-guard/index.d.ts.map +1 -1
- package/dist/branch-guard.js +116 -3
- package/dist/changelog-writer/index.d.ts.map +1 -1
- package/dist/changelog-writer.js +137 -3
- package/dist/checkpoint/index.d.ts +2 -0
- package/dist/checkpoint/index.d.ts.map +1 -1
- package/dist/checkpoint.js +34 -8
- package/dist/code-metrics.js +5 -0
- package/dist/commit-validator/index.d.ts.map +1 -1
- package/dist/commit-validator.js +35 -8
- package/dist/context-pins/index.d.ts.map +1 -1
- package/dist/context-pins.js +6 -5
- package/dist/cost-tracker/index.d.ts.map +1 -1
- package/dist/cost-tracker.js +101 -1
- package/dist/cron/index.d.ts.map +1 -1
- package/dist/cron.js +1 -0
- package/dist/dead-code-detector/index.d.ts.map +1 -1
- package/dist/dead-code-detector.js +9 -1
- package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -1
- package/dist/dependency-vulnerability-gate.js +43 -8
- package/dist/diff-summary/index.d.ts.map +1 -1
- package/dist/diff-summary.js +112 -2
- package/dist/doc-sync-guard.js +17 -1
- package/dist/duplicate-code-detector/index.d.ts.map +1 -1
- package/dist/duplicate-code-detector.js +102 -2
- package/dist/feature-flag-tracker/index.d.ts.map +1 -1
- package/dist/feature-flag-tracker.js +30 -4
- package/dist/file-watcher/index.d.ts.map +1 -1
- package/dist/file-watcher.js +6 -7
- package/dist/format-on-save/index.d.ts +2 -0
- package/dist/format-on-save/index.d.ts.map +1 -1
- package/dist/format-on-save.js +288 -59
- package/dist/git-autocommit/index.d.ts +12 -0
- package/dist/git-autocommit/index.d.ts.map +1 -1
- package/dist/git-autocommit.js +58 -4
- package/dist/import-organizer/index.d.ts.map +1 -1
- package/dist/import-organizer.js +152 -21
- package/dist/index.js +1413 -590
- package/dist/injection-shield/index.d.ts +16 -0
- package/dist/injection-shield/index.d.ts.map +1 -1
- package/dist/injection-shield.js +12 -4
- package/dist/interface-contract-guard/index.d.ts.map +1 -1
- package/dist/interface-contract-guard.js +60 -26
- package/dist/knowledge-graph/index.d.ts.map +1 -1
- package/dist/knowledge-graph.js +7 -6
- package/dist/lint-gate/index.d.ts.map +1 -1
- package/dist/lint-gate.js +115 -2
- package/dist/loop-breaker/index.d.ts.map +1 -1
- package/dist/loop-breaker.js +15 -5
- package/dist/migration-planner.js +15 -1
- package/dist/notify-hub/index.d.ts.map +1 -1
- package/dist/notify-hub.js +41 -4
- package/dist/pr-drafter/index.d.ts.map +1 -1
- package/dist/pr-drafter.js +20 -1
- package/dist/prompt-firewall/index.d.ts.map +1 -1
- package/dist/prompt-firewall.js +140 -11
- package/dist/refactor-suggester/index.d.ts.map +1 -1
- package/dist/refactor-suggester.js +119 -4
- package/dist/runtime/bounded-map.d.ts +86 -0
- package/dist/runtime/bounded-map.d.ts.map +1 -0
- package/dist/runtime/credential-patterns.d.ts +42 -0
- package/dist/runtime/credential-patterns.d.ts.map +1 -0
- package/dist/runtime/handles.d.ts +46 -0
- package/dist/runtime/handles.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/local-bin.d.ts +120 -0
- package/dist/runtime/local-bin.d.ts.map +1 -0
- package/dist/runtime/safe-json.d.ts +25 -0
- package/dist/runtime/safe-json.d.ts.map +1 -0
- package/dist/runtime.js +309 -22
- package/dist/schema-evolution-guard.js +5 -0
- package/dist/secret-scanner/index.d.ts.map +1 -1
- package/dist/secret-scanner.js +194 -50
- package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
- package/dist/security-hotspot-scanner.js +132 -1
- package/dist/semantic-search-indexer/index.d.ts.map +1 -1
- package/dist/semantic-search-indexer.js +7 -1
- package/dist/semver-bump/index.d.ts.map +1 -1
- package/dist/semver-bump.js +21 -2
- package/dist/session-recap/index.d.ts.map +1 -1
- package/dist/session-recap.js +18 -1
- package/dist/smart-rename/index.d.ts +2 -0
- package/dist/smart-rename/index.d.ts.map +1 -1
- package/dist/smart-rename.js +16 -2
- package/dist/spec-linker/index.d.ts.map +1 -1
- package/dist/spec-linker.js +19 -2
- package/dist/template-engine/index.d.ts.map +1 -1
- package/dist/template-engine.js +45 -3
- package/dist/test-coverage-gate.js +17 -1
- package/dist/test-runner-gate/index.d.ts.map +1 -1
- package/dist/test-runner-gate.js +142 -9
- package/dist/todo-listener/index.d.ts.map +1 -1
- package/dist/todo-listener.js +42 -2
- package/dist/todo-tracker/index.d.ts.map +1 -1
- package/dist/todo-tracker.js +3 -4
- package/dist/token-budget/index.d.ts.map +1 -1
- package/dist/token-budget.js +31 -19
- package/dist/token-throttle/index.d.ts.map +1 -1
- package/dist/token-throttle.js +17 -2
- package/dist/type-gate/index.d.ts.map +1 -1
- package/dist/type-gate.js +108 -17
- package/package.json +3 -3
package/dist/secret-scanner.js
CHANGED
|
@@ -1,20 +1,61 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
1
|
+
// src/runtime/local-bin.ts
|
|
2
|
+
import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
|
|
3
|
+
|
|
4
|
+
// src/runtime/handles.ts
|
|
5
|
+
function releaseHandle(off) {
|
|
6
|
+
if (off) {
|
|
7
|
+
try {
|
|
8
|
+
off();
|
|
9
|
+
} catch {
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/runtime/index.ts
|
|
16
|
+
var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
17
|
+
|
|
18
|
+
// src/runtime/credential-patterns.ts
|
|
19
|
+
var CREDENTIAL_PATTERNS = [
|
|
3
20
|
// LLM provider keys
|
|
4
21
|
{
|
|
5
22
|
type: "anthropic_key",
|
|
6
23
|
regex: /(?<![A-Za-z0-9])sk-ant-api\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g
|
|
7
24
|
},
|
|
8
|
-
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g },
|
|
9
|
-
// GitHub
|
|
25
|
+
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?!ant)(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g },
|
|
26
|
+
// GitHub. `ghp_` is only the personal-access-token prefix — the OAuth
|
|
27
|
+
// (`gho_`), user-to-server (`ghu_`), server-to-server (`ghs_`) and
|
|
28
|
+
// refresh (`ghr_`) tokens grant the same or broader access and were
|
|
29
|
+
// previously not detected at all.
|
|
10
30
|
{ type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g },
|
|
31
|
+
{
|
|
32
|
+
type: "github_oauth_token",
|
|
33
|
+
regex: /(?<![A-Za-z0-9])gh[ousr]_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g
|
|
34
|
+
},
|
|
11
35
|
{ type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g },
|
|
36
|
+
// GitLab
|
|
37
|
+
{ type: "gitlab_pat", regex: /(?<![A-Za-z0-9])glpat-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g },
|
|
38
|
+
{
|
|
39
|
+
type: "gitlab_runner_token",
|
|
40
|
+
regex: /(?<![A-Za-z0-9])glrt-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
|
|
41
|
+
},
|
|
42
|
+
// npm — a leaked publish token is a supply-chain compromise.
|
|
43
|
+
{ type: "npm_token", regex: /(?<![A-Za-z0-9])npm_[A-Za-z0-9]{36}(?![A-Za-z0-9])/g },
|
|
12
44
|
// AWS
|
|
13
45
|
{ type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g },
|
|
14
46
|
// GCP
|
|
15
47
|
{ type: "gcp_key", regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g },
|
|
16
|
-
// Slack
|
|
17
|
-
|
|
48
|
+
// Slack. `xoxe` (token-rotation) and `xapp` (app-level) were missing;
|
|
49
|
+
// both are as sensitive as the bot/user tokens already covered.
|
|
50
|
+
{
|
|
51
|
+
type: "slack_token",
|
|
52
|
+
regex: /(?<![A-Za-z0-9-])xox[abposer]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g
|
|
53
|
+
},
|
|
54
|
+
{ type: "slack_app_token", regex: /(?<![A-Za-z0-9-])xapp-\d-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g },
|
|
55
|
+
{
|
|
56
|
+
type: "slack_webhook",
|
|
57
|
+
regex: /https:\/\/hooks\.slack\.com\/services\/T[A-Za-z0-9_-]+\/B[A-Za-z0-9_-]+\/[A-Za-z0-9]{16,}/g
|
|
58
|
+
},
|
|
18
59
|
// Stripe
|
|
19
60
|
{
|
|
20
61
|
type: "stripe_key",
|
|
@@ -25,7 +66,7 @@ var BASE_PATTERNS = [
|
|
|
25
66
|
// Telegram
|
|
26
67
|
{
|
|
27
68
|
type: "telegram_bot_token",
|
|
28
|
-
regex:
|
|
69
|
+
regex: /(?:(?<![A-Za-z0-9_])|(?<=(?:^|[^A-Za-z0-9_])bot))\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
|
|
29
70
|
},
|
|
30
71
|
// JWT
|
|
31
72
|
{
|
|
@@ -35,35 +76,93 @@ var BASE_PATTERNS = [
|
|
|
35
76
|
// Private keys
|
|
36
77
|
{
|
|
37
78
|
type: "private_key",
|
|
38
|
-
regex: /(?:^|\n)
|
|
79
|
+
regex: /(?:^|\n)(?:-----BEGIN (?:RSA|EC|OPENSSH|DSA)? ?PRIVATE KEY-----[\s\S]*?-----END (?:RSA|EC|OPENSSH|DSA)? ?PRIVATE KEY-----|-----BEGIN PGP PRIVATE KEY BLOCK-----[\s\S]*?-----END PGP PRIVATE KEY BLOCK-----)(?!\S)/g
|
|
39
80
|
},
|
|
40
81
|
// AI/ML provider tokens
|
|
41
82
|
{ type: "huggingface_token", regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g },
|
|
42
83
|
{ type: "replicate_token", regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
43
84
|
{ type: "perplexity_key", regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
44
85
|
{ type: "groq_key", regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
86
|
+
// SaaS / infrastructure tokens — each grants API access on the user's
|
|
87
|
+
// account, and each was previously invisible to this gate.
|
|
88
|
+
{ type: "sendgrid_key", regex: /(?<![A-Za-z0-9])SG\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g },
|
|
89
|
+
{ type: "digitalocean_token", regex: /(?<![A-Za-z0-9])dop_v1_[a-f0-9]{64}(?![A-Za-z0-9])/g },
|
|
90
|
+
{ type: "doppler_token", regex: /(?<![A-Za-z0-9])dp\.(?:pt|st|sa|scim|audit)\.[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
91
|
+
{ type: "shopify_token", regex: /(?<![A-Za-z0-9])shp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}(?![A-Za-z0-9])/g },
|
|
92
|
+
{ type: "docker_pat", regex: /(?<![A-Za-z0-9])dckr_pat_[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g },
|
|
93
|
+
{ type: "linear_key", regex: /(?<![A-Za-z0-9])lin_api_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
94
|
+
{ type: "atlassian_token", regex: /(?<![A-Za-z0-9])ATATT3[A-Za-z0-9_\-=]{40,}(?![A-Za-z0-9_\-=])/g },
|
|
95
|
+
{ type: "square_token", regex: /(?<![A-Za-z0-9])(?:sq0(?:atp|csp)-|EAAA)[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g },
|
|
96
|
+
{
|
|
97
|
+
type: "azure_storage_key",
|
|
98
|
+
regex: /AccountKey=[A-Za-z0-9+/]{80,}={0,2}/g
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
type: "google_oauth_client_secret",
|
|
102
|
+
regex: /(?<![A-Za-z0-9_-])GOCSPX-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
|
|
103
|
+
},
|
|
45
104
|
// Bearer tokens
|
|
46
105
|
{
|
|
47
106
|
type: "bearer_token",
|
|
48
|
-
regex: /(
|
|
107
|
+
regex: /(?<![A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?![A-Za-z0-9._~+/-])/g
|
|
108
|
+
},
|
|
109
|
+
// Database URIs. Require password-bearing user-info; credential-free values stay scannable.
|
|
110
|
+
{ type: "mongodb_uri", regex: /mongodb(?:\+srv)?:\/\/[^\s:/@"'`]*:[^\s/@"'`]+@[^\s"'`]+/g },
|
|
111
|
+
{
|
|
112
|
+
type: "postgres_uri",
|
|
113
|
+
// Query parsers decode percent-encoded parameter names. Recognize each
|
|
114
|
+
// encoded character in `password` so mixed forms such as `pass%77ord`
|
|
115
|
+
// cannot bypass detection while keeping the scan strictly bounded.
|
|
116
|
+
regex: /postgres(?:ql)?:\/\/(?:[^\s:/@"'`]*:[^\s/@"'`]+@[^\s"'`]+|[^&\s?"'`#]{1,2048}\?(?:(?!(?:p|%70)(?:a|%61)(?:s|%73)(?:s|%73)(?:w|%77)(?:o|%6[fF])(?:r|%72)(?:d|%64)=)[^&\s#"'`]{1,256}&){0,32}(?:p|%70)(?:a|%61)(?:s|%73)(?:s|%73)(?:w|%77)(?:o|%6[fF])(?:r|%72)(?:d|%64)=[^&\s#"'`]{1,4096})/g
|
|
49
117
|
},
|
|
50
|
-
|
|
51
|
-
{ type: "
|
|
52
|
-
{ type: "postgres_uri", regex: /postgres(?:ql)?:\/\/[^\s"'`]+/g },
|
|
53
|
-
{ type: "mysql_uri", regex: /mysql:\/\/[^\s"'`]+/g },
|
|
54
|
-
{ type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g }
|
|
118
|
+
{ type: "mysql_uri", regex: /mysql:\/\/[^\s:/@"'`]*:[^\s/@"'`]+@[^\s"'`]+/g },
|
|
119
|
+
{ type: "redis_uri", regex: /redis:\/\/[^\s:/@"'`]*:[^\s/@"'`]+@[^\s"'`]+/g }
|
|
55
120
|
];
|
|
121
|
+
function cloneCredentialPatterns() {
|
|
122
|
+
return CREDENTIAL_PATTERNS.map((p) => ({
|
|
123
|
+
type: p.type,
|
|
124
|
+
regex: new RegExp(p.regex.source, p.regex.flags)
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/secret-scanner/index.ts
|
|
129
|
+
var BASE_PATTERNS = cloneCredentialPatterns();
|
|
56
130
|
var PATTERNS = [...BASE_PATTERNS];
|
|
57
131
|
var patternCacheKey = "";
|
|
132
|
+
var GROUP_INDEX_OF_PATTERN = [];
|
|
58
133
|
var COMBINED_REGEX = buildCombinedRegex(PATTERNS);
|
|
59
|
-
var
|
|
134
|
+
var SCAN_WINDOW_LENGTH = 1e5;
|
|
135
|
+
var SCAN_WINDOW_OVERLAP = 4096;
|
|
136
|
+
var MAX_TOTAL_SCAN_LENGTH = 8 * 1024 * 1024;
|
|
137
|
+
var TOO_LARGE_MARKER = "unscannable_oversized_input";
|
|
60
138
|
var RE_DOS_TIMEOUT_MS = 100;
|
|
139
|
+
function countCaptureGroups(source) {
|
|
140
|
+
try {
|
|
141
|
+
return new RegExp(`${source}|`).exec("").length - 1;
|
|
142
|
+
} catch {
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function patternTypeForGroups(groups) {
|
|
147
|
+
for (let i = 0; i < PATTERNS.length; i++) {
|
|
148
|
+
const at = GROUP_INDEX_OF_PATTERN[i];
|
|
149
|
+
if (at !== void 0 && groups[at] !== void 0) return PATTERNS[i].type;
|
|
150
|
+
}
|
|
151
|
+
return void 0;
|
|
152
|
+
}
|
|
61
153
|
function buildCombinedRegex(patterns) {
|
|
62
|
-
const newCacheKey = JSON.stringify(patterns.map((p) => p.type));
|
|
154
|
+
const newCacheKey = JSON.stringify(patterns.map((p) => [p.type, p.regex.source]));
|
|
63
155
|
if (newCacheKey === patternCacheKey && COMBINED_REGEX) {
|
|
64
156
|
return COMBINED_REGEX;
|
|
65
157
|
}
|
|
66
158
|
patternCacheKey = newCacheKey;
|
|
159
|
+
const offsets = [];
|
|
160
|
+
let cursor = 0;
|
|
161
|
+
for (const p of patterns) {
|
|
162
|
+
offsets.push(cursor);
|
|
163
|
+
cursor += 1 + countCaptureGroups(p.regex.source);
|
|
164
|
+
}
|
|
165
|
+
GROUP_INDEX_OF_PATTERN = offsets;
|
|
67
166
|
return new RegExp(patterns.map((p) => `(${p.regex.source})`).join("|"), "g");
|
|
68
167
|
}
|
|
69
168
|
var state = {
|
|
@@ -81,29 +180,37 @@ var state = {
|
|
|
81
180
|
/** PostToolUse hook handle so teardown can unregister. */
|
|
82
181
|
postHookUnregister: null
|
|
83
182
|
};
|
|
84
|
-
function
|
|
85
|
-
if (!text) return [];
|
|
86
|
-
if (text.length > RE_DOS_MAX_SCAN_LENGTH) return [];
|
|
87
|
-
const found = /* @__PURE__ */ new Set();
|
|
183
|
+
function scanWindow(window, found, startTime) {
|
|
88
184
|
COMBINED_REGEX.lastIndex = 0;
|
|
89
185
|
let m;
|
|
90
|
-
|
|
91
|
-
while ((m = COMBINED_REGEX.exec(text)) !== null) {
|
|
186
|
+
while ((m = COMBINED_REGEX.exec(window)) !== null) {
|
|
92
187
|
if (performance.now() - startTime > RE_DOS_TIMEOUT_MS) {
|
|
93
188
|
throw new Error(
|
|
94
|
-
`secret-scanner: ReDoS timeout \u2014 regex scan exceeded ${RE_DOS_TIMEOUT_MS}ms on ${
|
|
189
|
+
`secret-scanner: ReDoS timeout \u2014 regex scan exceeded ${RE_DOS_TIMEOUT_MS}ms on ${window.length}-char window`
|
|
95
190
|
);
|
|
96
191
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
found.add(PATTERNS[i].type);
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
192
|
+
const type = patternTypeForGroups(m.slice(1));
|
|
193
|
+
if (type !== void 0) found.add(type);
|
|
103
194
|
if (m.index === COMBINED_REGEX.lastIndex) {
|
|
104
195
|
COMBINED_REGEX.lastIndex += 1;
|
|
105
196
|
}
|
|
106
197
|
}
|
|
198
|
+
}
|
|
199
|
+
function findMatches(text) {
|
|
200
|
+
if (!text) return [];
|
|
201
|
+
const found = /* @__PURE__ */ new Set();
|
|
202
|
+
const startTime = performance.now();
|
|
203
|
+
if (text.length <= SCAN_WINDOW_LENGTH) {
|
|
204
|
+
scanWindow(text, found, startTime);
|
|
205
|
+
return Array.from(found).sort();
|
|
206
|
+
}
|
|
207
|
+
if (text.length > MAX_TOTAL_SCAN_LENGTH) {
|
|
208
|
+
return [TOO_LARGE_MARKER];
|
|
209
|
+
}
|
|
210
|
+
const stride = SCAN_WINDOW_LENGTH - SCAN_WINDOW_OVERLAP;
|
|
211
|
+
for (let offset = 0; offset < text.length; offset += stride) {
|
|
212
|
+
scanWindow(text.slice(offset, offset + SCAN_WINDOW_LENGTH), found, startTime);
|
|
213
|
+
}
|
|
107
214
|
return Array.from(found).sort();
|
|
108
215
|
}
|
|
109
216
|
function scanInput(input, depth = 0) {
|
|
@@ -129,30 +236,55 @@ function scanInput(input, depth = 0) {
|
|
|
129
236
|
}
|
|
130
237
|
return null;
|
|
131
238
|
}
|
|
239
|
+
function redactString(text) {
|
|
240
|
+
if (text.length > SCAN_WINDOW_LENGTH) {
|
|
241
|
+
return { ok: false, reason: "oversized_input" };
|
|
242
|
+
}
|
|
243
|
+
const startTime = performance.now();
|
|
244
|
+
let cursor = 0;
|
|
245
|
+
let output = "";
|
|
246
|
+
COMBINED_REGEX.lastIndex = 0;
|
|
247
|
+
let match;
|
|
248
|
+
while ((match = COMBINED_REGEX.exec(text)) !== null) {
|
|
249
|
+
if (performance.now() - startTime > RE_DOS_TIMEOUT_MS) {
|
|
250
|
+
return { ok: false, reason: "oversized_input" };
|
|
251
|
+
}
|
|
252
|
+
const type = patternTypeForGroups(match.slice(1));
|
|
253
|
+
output += text.slice(cursor, match.index);
|
|
254
|
+
output += `[REDACTED:${type ?? "unknown"}]`;
|
|
255
|
+
cursor = COMBINED_REGEX.lastIndex;
|
|
256
|
+
if (match.index === COMBINED_REGEX.lastIndex) {
|
|
257
|
+
COMBINED_REGEX.lastIndex += 1;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (cursor === 0) return { ok: true, value: text };
|
|
261
|
+
return { ok: true, value: output + text.slice(cursor) };
|
|
262
|
+
}
|
|
132
263
|
function redactInput(input, depth = 0) {
|
|
133
|
-
if (input === null || input === void 0) return input;
|
|
134
|
-
if (depth > 50) return
|
|
264
|
+
if (input === null || input === void 0) return { ok: true, value: input };
|
|
265
|
+
if (depth > 50) return { ok: false, reason: "maximum_depth_exceeded" };
|
|
135
266
|
if (typeof input === "string") {
|
|
136
|
-
return input
|
|
137
|
-
for (let i = 0; i < PATTERNS.length; i++) {
|
|
138
|
-
if (groups[i] !== void 0) {
|
|
139
|
-
return `[REDACTED:${PATTERNS[i].type}]`;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return "[REDACTED:unknown]";
|
|
143
|
-
});
|
|
267
|
+
return redactString(input);
|
|
144
268
|
}
|
|
145
269
|
if (Array.isArray(input)) {
|
|
146
|
-
|
|
270
|
+
const out = [];
|
|
271
|
+
for (const item of input) {
|
|
272
|
+
const redacted = redactInput(item, depth + 1);
|
|
273
|
+
if (!redacted.ok) return redacted;
|
|
274
|
+
out.push(redacted.value);
|
|
275
|
+
}
|
|
276
|
+
return { ok: true, value: out };
|
|
147
277
|
}
|
|
148
278
|
if (typeof input === "object") {
|
|
149
279
|
const out = {};
|
|
150
280
|
for (const [k, v] of Object.entries(input)) {
|
|
151
|
-
|
|
281
|
+
const redacted = redactInput(v, depth + 1);
|
|
282
|
+
if (!redacted.ok) return redacted;
|
|
283
|
+
out[k] = redacted.value;
|
|
152
284
|
}
|
|
153
|
-
return out;
|
|
285
|
+
return { ok: true, value: out };
|
|
154
286
|
}
|
|
155
|
-
return input;
|
|
287
|
+
return { ok: true, value: input };
|
|
156
288
|
}
|
|
157
289
|
var DEFAULTS = {
|
|
158
290
|
matcher: "bash|write|edit",
|
|
@@ -212,20 +344,21 @@ function buildHook(cfg, log) {
|
|
|
212
344
|
}
|
|
213
345
|
if (cfg.mode === "redact") {
|
|
214
346
|
const redacted = redactInput(input.toolInput);
|
|
215
|
-
if (redacted !== null && typeof redacted === "object" && !Array.isArray(redacted)) {
|
|
347
|
+
if (redacted.ok && redacted.value !== null && typeof redacted.value === "object" && !Array.isArray(redacted.value)) {
|
|
216
348
|
state.redactCount += 1;
|
|
217
349
|
log.info(`[secret-scanner] redacted ${toolName} \u2014 matched: ${summary}`);
|
|
218
350
|
return {
|
|
219
351
|
decision: "allow",
|
|
220
|
-
modifiedInput: redacted,
|
|
352
|
+
modifiedInput: redacted.value,
|
|
221
353
|
additionalContext: `secret-scanner: redacted ${matched.length} credential pattern(s) from the ${toolName} arguments before execution.`
|
|
222
354
|
};
|
|
223
355
|
}
|
|
224
356
|
state.blockCount += 1;
|
|
225
357
|
state.lastBlock = { toolName, matchedTypes: matched, when };
|
|
358
|
+
const detail = !redacted.ok ? redacted.reason === "oversized_input" ? "an input field exceeds the safe scan limit" : "the input exceeds the safe nesting depth" : "the input has a non-object shape";
|
|
226
359
|
return {
|
|
227
360
|
decision: "block",
|
|
228
|
-
reason: `secret-scanner: cannot safely redact '${toolName}'
|
|
361
|
+
reason: `secret-scanner: cannot safely redact '${toolName}' because ${detail}; refusing to run.`
|
|
229
362
|
};
|
|
230
363
|
}
|
|
231
364
|
state.allowCount += 1;
|
|
@@ -243,10 +376,21 @@ function buildPostHook(cfg, log) {
|
|
|
243
376
|
const matched = findMatches(result.content);
|
|
244
377
|
if (matched.length === 0) return;
|
|
245
378
|
const toolName = input.toolName ?? "unknown";
|
|
246
|
-
const
|
|
379
|
+
const oversized = matched.includes(TOO_LARGE_MARKER);
|
|
380
|
+
const credentialMatches = matched.filter((type) => type !== TOO_LARGE_MARKER);
|
|
381
|
+
if (oversized && credentialMatches.length === 0) {
|
|
382
|
+
log.warn(
|
|
383
|
+
`[secret-scanner] POST-TOOL UNSCANNABLE: ${toolName} output exceeds ${MAX_TOTAL_SCAN_LENGTH} characters`
|
|
384
|
+
);
|
|
385
|
+
return {
|
|
386
|
+
additionalContext: `
|
|
387
|
+
\u26A0\uFE0F secret-scanner: the output of '${toolName}' exceeds the safe scan limit and could not be inspected for plaintext credentials. Do not treat this output as verified clean; avoid echoing, storing, committing, or transmitting it until it can be reviewed in smaller bounded sections.`
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
const summary = credentialMatches.join(", ");
|
|
247
391
|
const when = (/* @__PURE__ */ new Date()).toISOString();
|
|
248
392
|
state.leakCount += 1;
|
|
249
|
-
state.lastLeak = { toolName, matchedTypes:
|
|
393
|
+
state.lastLeak = { toolName, matchedTypes: credentialMatches, when };
|
|
250
394
|
log.warn(`[secret-scanner] POST-TOOL LEAK: ${toolName} output matched ${summary}`);
|
|
251
395
|
return {
|
|
252
396
|
additionalContext: `
|
|
@@ -308,8 +452,8 @@ var plugin = {
|
|
|
308
452
|
state.leakCount = 0;
|
|
309
453
|
state.lastBlock = null;
|
|
310
454
|
state.lastLeak = null;
|
|
311
|
-
state.hookUnregister =
|
|
312
|
-
state.postHookUnregister =
|
|
455
|
+
state.hookUnregister = releaseHandle(state.hookUnregister);
|
|
456
|
+
state.postHookUnregister = releaseHandle(state.postHookUnregister);
|
|
313
457
|
const cfg = readConfig(api.config.extensions?.["secret-scanner"]);
|
|
314
458
|
PATTERNS = [...BASE_PATTERNS];
|
|
315
459
|
for (const cp of cfg.customPatterns) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/security-hotspot-scanner/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/security-hotspot-scanner/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAkRrD,QAAA,MAAM,MAAM,EAAE,MAkQb,CAAC;eAEa,MAAM"}
|
|
@@ -4,6 +4,137 @@ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 }
|
|
|
4
4
|
|
|
5
5
|
// src/runtime/index.ts
|
|
6
6
|
import { basename, extname, isAbsolute, relative, resolve } from "node:path";
|
|
7
|
+
|
|
8
|
+
// src/runtime/local-bin.ts
|
|
9
|
+
import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
|
|
10
|
+
|
|
11
|
+
// src/runtime/bounded-map.ts
|
|
12
|
+
var BoundedMap = class {
|
|
13
|
+
map = /* @__PURE__ */ new Map();
|
|
14
|
+
max;
|
|
15
|
+
ttlMs;
|
|
16
|
+
now;
|
|
17
|
+
/** Entries dropped to stay under `max`. Surfaced by plugin health(). */
|
|
18
|
+
evictions = 0;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
const normalizedMax = Math.floor(options.max);
|
|
21
|
+
this.max = Number.isSafeInteger(normalizedMax) ? Math.max(1, normalizedMax) : 1;
|
|
22
|
+
this.ttlMs = options.ttlMs;
|
|
23
|
+
this.now = options.now ?? Date.now;
|
|
24
|
+
}
|
|
25
|
+
expired(entry) {
|
|
26
|
+
return this.ttlMs !== void 0 && this.now() - entry.storedAt > this.ttlMs;
|
|
27
|
+
}
|
|
28
|
+
get(key) {
|
|
29
|
+
const entry = this.map.get(key);
|
|
30
|
+
if (entry === void 0) return void 0;
|
|
31
|
+
if (this.expired(entry)) {
|
|
32
|
+
this.map.delete(key);
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
this.map.delete(key);
|
|
36
|
+
this.map.set(key, entry);
|
|
37
|
+
return entry.value;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read without promoting the key to most-recently-used. Use for
|
|
41
|
+
* diagnostics that must not perturb the eviction order.
|
|
42
|
+
*/
|
|
43
|
+
peek(key) {
|
|
44
|
+
const entry = this.map.get(key);
|
|
45
|
+
if (entry === void 0 || this.expired(entry)) return void 0;
|
|
46
|
+
return entry.value;
|
|
47
|
+
}
|
|
48
|
+
has(key) {
|
|
49
|
+
const entry = this.map.get(key);
|
|
50
|
+
if (entry === void 0) return false;
|
|
51
|
+
if (this.expired(entry)) {
|
|
52
|
+
this.map.delete(key);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
set(key, value) {
|
|
58
|
+
this.map.delete(key);
|
|
59
|
+
this.map.set(key, { value, storedAt: this.now() });
|
|
60
|
+
while (this.map.size > this.max) {
|
|
61
|
+
const coldest = this.map.keys().next().value;
|
|
62
|
+
if (coldest === void 0) break;
|
|
63
|
+
this.map.delete(coldest);
|
|
64
|
+
this.evictions += 1;
|
|
65
|
+
}
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
delete(key) {
|
|
69
|
+
return this.map.delete(key);
|
|
70
|
+
}
|
|
71
|
+
clear() {
|
|
72
|
+
this.map.clear();
|
|
73
|
+
this.evictions = 0;
|
|
74
|
+
}
|
|
75
|
+
get size() {
|
|
76
|
+
return this.map.size;
|
|
77
|
+
}
|
|
78
|
+
/** How many entries have been dropped to respect `max`, since the last clear. */
|
|
79
|
+
get evictionCount() {
|
|
80
|
+
return this.evictions;
|
|
81
|
+
}
|
|
82
|
+
/** Drop every expired entry. Cheap enough to call from a status tool. */
|
|
83
|
+
prune() {
|
|
84
|
+
if (this.ttlMs === void 0) return 0;
|
|
85
|
+
let removed = 0;
|
|
86
|
+
for (const [key, entry] of this.map) {
|
|
87
|
+
if (this.expired(entry)) {
|
|
88
|
+
this.map.delete(key);
|
|
89
|
+
removed += 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return removed;
|
|
93
|
+
}
|
|
94
|
+
/** Live (non-expired) entries, coldest first. */
|
|
95
|
+
*entries() {
|
|
96
|
+
for (const [key, entry] of this.map) {
|
|
97
|
+
if (!this.expired(entry)) yield [key, entry.value];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
[Symbol.iterator]() {
|
|
101
|
+
return this.entries();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var BoundedSet = class {
|
|
105
|
+
inner;
|
|
106
|
+
constructor(options) {
|
|
107
|
+
this.inner = new BoundedMap(options);
|
|
108
|
+
}
|
|
109
|
+
has(value) {
|
|
110
|
+
return this.inner.has(value);
|
|
111
|
+
}
|
|
112
|
+
add(value) {
|
|
113
|
+
this.inner.set(value, true);
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
delete(value) {
|
|
117
|
+
return this.inner.delete(value);
|
|
118
|
+
}
|
|
119
|
+
clear() {
|
|
120
|
+
this.inner.clear();
|
|
121
|
+
}
|
|
122
|
+
get size() {
|
|
123
|
+
return this.inner.size;
|
|
124
|
+
}
|
|
125
|
+
/** How many entries have been dropped to respect `max`, since the last clear. */
|
|
126
|
+
get evictionCount() {
|
|
127
|
+
return this.inner.evictionCount;
|
|
128
|
+
}
|
|
129
|
+
*values() {
|
|
130
|
+
for (const [key] of this.inner) yield key;
|
|
131
|
+
}
|
|
132
|
+
[Symbol.iterator]() {
|
|
133
|
+
return this.values();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/runtime/index.ts
|
|
7
138
|
var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
8
139
|
function hasLeadingDash(arg) {
|
|
9
140
|
return arg.length > 0 && arg.startsWith("-");
|
|
@@ -30,7 +161,7 @@ var state = {
|
|
|
30
161
|
skippedCount: 0,
|
|
31
162
|
blockedCount: 0,
|
|
32
163
|
lastResult: null,
|
|
33
|
-
knownHotspots:
|
|
164
|
+
knownHotspots: new BoundedSet({ max: 5e3 }),
|
|
34
165
|
hookUnregister: null
|
|
35
166
|
};
|
|
36
167
|
var DEFAULTS = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/semantic-search-indexer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/semantic-search-indexer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAqcrD,QAAA,MAAM,MAAM,EAAE,MA6Nb,CAAC;eAEa,MAAM"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// src/semantic-search-indexer/index.ts
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
4
|
+
import { DEFAULT_WALK_IGNORE_DIRS } from "@wrongstack/core/utils";
|
|
4
5
|
var API_VERSION = "^0.1.10";
|
|
6
|
+
var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5
7
|
var state = {
|
|
6
8
|
index: null,
|
|
7
9
|
cachedPath: null,
|
|
@@ -45,7 +47,11 @@ var DEFAULTS = {
|
|
|
45
47
|
".scss",
|
|
46
48
|
".html"
|
|
47
49
|
],
|
|
48
|
-
excludePatterns: [
|
|
50
|
+
excludePatterns: [
|
|
51
|
+
...DEFAULT_WALK_IGNORE_DIRS.map(escapeRegex),
|
|
52
|
+
"\\.wrongstack",
|
|
53
|
+
"\\.temp_files"
|
|
54
|
+
],
|
|
49
55
|
maxFileBytes: 1e6,
|
|
50
56
|
defaultLimit: 10,
|
|
51
57
|
minTokenLength: 2,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/semver-bump/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAuCrD,KAAK,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAErD,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAqGD;+DAC+D;AAC/D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAQnF;AAmBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,QAAQ,CAIrE;AAmED,QAAA,MAAM,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/semver-bump/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAuCrD,KAAK,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAErD,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAqGD;+DAC+D;AAC/D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAQnF;AAmBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,QAAQ,CAIrE;AAmED,QAAA,MAAM,MAAM,EAAE,MA0db,CAAC;eAEa,MAAM"}
|
package/dist/semver-bump.js
CHANGED
|
@@ -293,10 +293,29 @@ var plugin = {
|
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
} else {
|
|
296
|
+
const pending = [];
|
|
296
297
|
for (const manifest of changed) {
|
|
297
|
-
|
|
298
|
+
let pkgData;
|
|
299
|
+
try {
|
|
300
|
+
pkgData = JSON.parse(await readFile(manifest, "utf-8"));
|
|
301
|
+
} catch (err) {
|
|
302
|
+
return {
|
|
303
|
+
ok: false,
|
|
304
|
+
error: `cannot bump: ${manifest} is not readable as JSON (${toErrorMessage(err)}). No manifests were modified.`
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
if (!pkgData || typeof pkgData !== "object") {
|
|
308
|
+
return {
|
|
309
|
+
ok: false,
|
|
310
|
+
error: `cannot bump: ${manifest} does not contain a JSON object. No manifests were modified.`
|
|
311
|
+
};
|
|
312
|
+
}
|
|
298
313
|
pkgData.version = newVersion;
|
|
299
|
-
|
|
314
|
+
pending.push({ path: manifest, contents: `${JSON.stringify(pkgData, null, 2)}
|
|
315
|
+
` });
|
|
316
|
+
}
|
|
317
|
+
for (const { path, contents } of pending) {
|
|
318
|
+
await writeFile(path, contents, "utf-8");
|
|
300
319
|
}
|
|
301
320
|
}
|
|
302
321
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session-recap/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session-recap/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA6MrD,QAAA,MAAM,MAAM,EAAE,MA4Yb,CAAC;eAEa,MAAM"}
|
package/dist/session-recap.js
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
// src/runtime/local-bin.ts
|
|
2
|
+
import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
|
|
3
|
+
|
|
4
|
+
// src/runtime/handles.ts
|
|
5
|
+
function releaseHandle(off) {
|
|
6
|
+
if (off) {
|
|
7
|
+
try {
|
|
8
|
+
off();
|
|
9
|
+
} catch {
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/runtime/index.ts
|
|
16
|
+
var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
17
|
+
|
|
1
18
|
// src/session-recap/index.ts
|
|
2
19
|
var state = {
|
|
3
20
|
recapsPublished: 0,
|
|
@@ -155,7 +172,7 @@ var plugin = {
|
|
|
155
172
|
state.commitCount = 0;
|
|
156
173
|
state.startedAt = null;
|
|
157
174
|
state.lastActivityAt = null;
|
|
158
|
-
state.stopHookUnregister =
|
|
175
|
+
state.stopHookUnregister = releaseHandle(state.stopHookUnregister);
|
|
159
176
|
for (const off of state.eventUnsubscribers) {
|
|
160
177
|
try {
|
|
161
178
|
off();
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
* @public
|
|
20
20
|
*/
|
|
21
21
|
import type { Plugin } from '@wrongstack/core/types';
|
|
22
|
+
/** True when `name` is something this tool may safely substitute. */
|
|
23
|
+
export declare function isIdentifier(name: string): boolean;
|
|
22
24
|
declare const plugin: Plugin;
|
|
23
25
|
export default plugin;
|
|
24
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/smart-rename/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/smart-rename/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAkFrD,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAmCD,QAAA,MAAM,MAAM,EAAE,MAgJb,CAAC;eAEa,MAAM"}
|
package/dist/smart-rename.js
CHANGED
|
@@ -38,8 +38,15 @@ function relativePath(p) {
|
|
|
38
38
|
function escapeRegex(s) {
|
|
39
39
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
40
40
|
}
|
|
41
|
+
var IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
42
|
+
function isIdentifier(name) {
|
|
43
|
+
return IDENTIFIER_RE.test(name);
|
|
44
|
+
}
|
|
41
45
|
function renameInContent(content, oldName, newName) {
|
|
42
|
-
const re = new RegExp(
|
|
46
|
+
const re = new RegExp(
|
|
47
|
+
`(?<![A-Za-z0-9_$])${escapeRegex(oldName)}(?![A-Za-z0-9_$])`,
|
|
48
|
+
"g"
|
|
49
|
+
);
|
|
43
50
|
let replacements = 0;
|
|
44
51
|
const preview = content.replace(re, () => {
|
|
45
52
|
replacements++;
|
|
@@ -101,6 +108,12 @@ var plugin = {
|
|
|
101
108
|
if (!newName || typeof newName !== "string" || newName.length === 0) {
|
|
102
109
|
return { ok: false, error: "newName is required" };
|
|
103
110
|
}
|
|
111
|
+
if (!isIdentifier(oldName)) {
|
|
112
|
+
return { ok: false, error: `oldName "${oldName}" is not a valid identifier` };
|
|
113
|
+
}
|
|
114
|
+
if (!isIdentifier(newName)) {
|
|
115
|
+
return { ok: false, error: `newName "${newName}" is not a valid identifier` };
|
|
116
|
+
}
|
|
104
117
|
if (!withinProject(rawPath)) {
|
|
105
118
|
return { ok: false, error: "path is outside the project root" };
|
|
106
119
|
}
|
|
@@ -166,5 +179,6 @@ var plugin = {
|
|
|
166
179
|
};
|
|
167
180
|
var smart_rename_default = plugin;
|
|
168
181
|
export {
|
|
169
|
-
smart_rename_default as default
|
|
182
|
+
smart_rename_default as default,
|
|
183
|
+
isIdentifier
|
|
170
184
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/spec-linker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/spec-linker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAyPrD,QAAA,MAAM,MAAM,EAAE,MAuOb,CAAC;eAEa,MAAM"}
|