@wrongstack/plugins 0.293.0 → 0.295.1
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 +7 -71
- package/dist/accessibility-auditor/index.d.ts +1 -1
- package/dist/accessibility-auditor/index.d.ts.map +1 -1
- package/dist/accessibility-auditor.js +50 -33
- package/dist/agent-handoff/index.d.ts +1 -1
- package/dist/agent-handoff/index.d.ts.map +1 -1
- package/dist/agent-handoff.js +30 -1
- package/dist/api-compatibility-gate/index.d.ts +1 -1
- package/dist/api-compatibility-gate/index.d.ts.map +1 -1
- package/dist/api-compatibility-gate.js +21 -16
- package/dist/audit/index.d.ts +386 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit.d.ts +1 -0
- package/dist/audit.js +454 -0
- package/dist/auto-doc/index.d.ts +1 -1
- package/dist/auto-doc/index.d.ts.map +1 -1
- package/dist/auto-doc.js +3 -1
- package/dist/auto-escalate/index.d.ts +1 -1
- package/dist/auto-escalate/index.d.ts.map +1 -1
- package/dist/auto-i18n-extractor/index.d.ts +1 -1
- package/dist/auto-i18n-extractor/index.d.ts.map +1 -1
- package/dist/auto-i18n-extractor.js +7 -1
- package/dist/branch-guard/index.d.ts +1 -1
- package/dist/branch-guard/index.d.ts.map +1 -1
- package/dist/branch-guard.js +111 -1
- package/dist/catalog.d.ts +5 -30
- package/dist/catalog.d.ts.map +1 -1
- package/dist/changelog-writer/index.d.ts +1 -1
- package/dist/changelog-writer/index.d.ts.map +1 -1
- package/dist/changelog-writer.js +137 -3
- package/dist/checkpoint/index.d.ts +3 -1
- package/dist/checkpoint/index.d.ts.map +1 -1
- package/dist/checkpoint.js +41 -16
- package/dist/code-metrics/index.d.ts +1 -1
- package/dist/code-metrics/index.d.ts.map +1 -1
- package/dist/code-metrics.js +34 -29
- package/dist/commit-validator/index.d.ts +1 -1
- package/dist/commit-validator/index.d.ts.map +1 -1
- package/dist/commit-validator.js +17 -7
- package/dist/config-validator/index.d.ts +1 -1
- package/dist/config-validator/index.d.ts.map +1 -1
- package/dist/context-pins/index.d.ts +1 -1
- package/dist/context-pins/index.d.ts.map +1 -1
- package/dist/context-pins.js +6 -5
- package/dist/cost-tracker/index.d.ts +1 -1
- package/dist/cost-tracker/index.d.ts.map +1 -1
- package/dist/cost-tracker.js +109 -2
- package/dist/cron/index.d.ts +1 -1
- package/dist/cron/index.d.ts.map +1 -1
- package/dist/cron.js +1 -0
- package/dist/dead-code-detector/index.d.ts +1 -1
- package/dist/dead-code-detector/index.d.ts.map +1 -1
- package/dist/dead-code-detector.js +31 -18
- package/dist/dep-guard/index.d.ts +1 -1
- package/dist/dep-guard/index.d.ts.map +1 -1
- package/dist/dependency-vulnerability-gate/index.d.ts +1 -1
- package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -1
- package/dist/dependency-vulnerability-gate.js +67 -20
- package/dist/diff-summary/index.d.ts +1 -1
- package/dist/diff-summary/index.d.ts.map +1 -1
- package/dist/diff-summary.js +128 -30
- package/dist/doc-sync-guard/index.d.ts +1 -1
- package/dist/doc-sync-guard/index.d.ts.map +1 -1
- package/dist/doc-sync-guard.js +7 -1
- package/dist/duplicate-code-detector/index.d.ts +26 -1
- package/dist/duplicate-code-detector/index.d.ts.map +1 -1
- package/dist/duplicate-code-detector.js +280 -69
- package/dist/error-lens/index.d.ts +1 -1
- package/dist/error-lens/index.d.ts.map +1 -1
- package/dist/factories/index.d.ts +4 -0
- package/dist/factories/index.d.ts.map +1 -0
- package/dist/factories.d.ts +1 -0
- package/dist/factories.js +74 -0
- package/dist/feature-flag-tracker/index.d.ts +1 -1
- package/dist/feature-flag-tracker/index.d.ts.map +1 -1
- package/dist/feature-flag-tracker.js +59 -33
- package/dist/file-watcher/index.d.ts +1 -1
- 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 +3 -1
- package/dist/format-on-save/index.d.ts.map +1 -1
- package/dist/format-on-save.js +289 -59
- package/dist/generated-plugin-exports.d.ts +65 -0
- package/dist/generated-plugin-exports.d.ts.map +1 -0
- package/dist/git-autocommit/index.d.ts +1 -1
- package/dist/git-autocommit/index.d.ts.map +1 -1
- package/dist/import-organizer/index.d.ts +1 -1
- package/dist/import-organizer/index.d.ts.map +1 -1
- package/dist/import-organizer.js +141 -20
- package/dist/index.d.ts +4 -183
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18298 -17279
- package/dist/injection-shield/index.d.ts +1 -1
- package/dist/injection-shield/index.d.ts.map +1 -1
- package/dist/interface-contract-guard/index.d.ts +1 -1
- package/dist/interface-contract-guard/index.d.ts.map +1 -1
- package/dist/interface-contract-guard.js +87 -53
- package/dist/knowledge-graph/index.d.ts +1 -1
- package/dist/knowledge-graph/index.d.ts.map +1 -1
- package/dist/knowledge-graph.js +7 -6
- package/dist/license-audit-gate/index.d.ts +1 -1
- package/dist/license-audit-gate/index.d.ts.map +1 -1
- package/dist/lint-gate/index.d.ts +1 -1
- package/dist/lint-gate/index.d.ts.map +1 -1
- package/dist/lint-gate.js +112 -1
- package/dist/llm-cache/index.d.ts +5 -1
- package/dist/llm-cache/index.d.ts.map +1 -1
- package/dist/llm-cache.js +16 -8
- package/dist/loop-breaker/index.d.ts +1 -1
- package/dist/loop-breaker/index.d.ts.map +1 -1
- package/dist/loop-breaker.js +25 -9
- package/dist/manifest/index.d.ts +16 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest.d.ts +1 -0
- package/dist/manifest.js +152 -0
- package/dist/migration-planner/index.d.ts +1 -1
- package/dist/migration-planner/index.d.ts.map +1 -1
- package/dist/migration-planner.js +5 -1
- package/dist/model-router/index.d.ts +1 -1
- package/dist/model-router/index.d.ts.map +1 -1
- package/dist/notify-hub/index.d.ts +1 -1
- package/dist/notify-hub/index.d.ts.map +1 -1
- package/dist/notify-hub/webhook-channel.d.ts +1 -1
- package/dist/notify-hub/webhook-channel.d.ts.map +1 -1
- package/dist/notify-hub.js +41 -4
- package/dist/path-guard/index.d.ts +1 -1
- package/dist/path-guard/index.d.ts.map +1 -1
- package/dist/performance-regression-gate/index.d.ts +1 -1
- package/dist/performance-regression-gate/index.d.ts.map +1 -1
- package/dist/plugin-stack-observer/index.d.ts +1 -1
- package/dist/plugin-stack-observer/index.d.ts.map +1 -1
- package/dist/pr-drafter/index.d.ts +1 -1
- package/dist/pr-drafter/index.d.ts.map +1 -1
- package/dist/pr-drafter.js +28 -29
- package/dist/process-guard/index.d.ts +1 -1
- package/dist/process-guard/index.d.ts.map +1 -1
- package/dist/prompt-firewall/index.d.ts +1 -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 +1 -1
- package/dist/refactor-suggester/index.d.ts.map +1 -1
- package/dist/refactor-suggester.js +148 -33
- package/dist/release-notes-generator/index.d.ts +1 -1
- package/dist/release-notes-generator/index.d.ts.map +1 -1
- package/dist/release-notes-generator.js +29 -22
- 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/index.d.ts +18 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/llm.d.ts +1 -1
- package/dist/runtime/llm.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 +332 -22
- package/dist/schema-evolution-guard/index.d.ts +1 -1
- package/dist/schema-evolution-guard/index.d.ts.map +1 -1
- package/dist/schema-evolution-guard.js +7 -1
- package/dist/secret-scanner/index.d.ts +1 -1
- package/dist/secret-scanner/index.d.ts.map +1 -1
- package/dist/secret-scanner.js +181 -48
- package/dist/security-hotspot-scanner/index.d.ts +1 -1
- package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
- package/dist/security-hotspot-scanner.js +158 -20
- package/dist/semantic-search-indexer/index.d.ts +1 -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 +1 -1
- package/dist/semver-bump/index.d.ts.map +1 -1
- package/dist/semver-bump.js +97 -43
- package/dist/session-recap/index.d.ts +1 -1
- package/dist/session-recap/index.d.ts.map +1 -1
- package/dist/shell-check/index.d.ts +1 -1
- package/dist/shell-check/index.d.ts.map +1 -1
- package/dist/smart-rename/index.d.ts +1 -1
- package/dist/smart-rename/index.d.ts.map +1 -1
- package/dist/spec-linker/index.d.ts +1 -1
- package/dist/spec-linker/index.d.ts.map +1 -1
- package/dist/spec-linker.js +222 -20772
- package/dist/template-engine/index.d.ts +1 -1
- package/dist/template-engine/index.d.ts.map +1 -1
- package/dist/template-engine.js +34 -8
- package/dist/test-coverage-gate/index.d.ts +1 -1
- package/dist/test-coverage-gate/index.d.ts.map +1 -1
- package/dist/test-coverage-gate.js +34 -14
- package/dist/test-flake-detector/index.d.ts +1 -1
- package/dist/test-flake-detector/index.d.ts.map +1 -1
- package/dist/test-flake-detector.js +31 -20
- package/dist/test-generator/index.d.ts +1 -1
- package/dist/test-generator/index.d.ts.map +1 -1
- package/dist/test-runner-gate/index.d.ts +1 -1
- package/dist/test-runner-gate/index.d.ts.map +1 -1
- package/dist/test-runner-gate.js +132 -9
- package/dist/todo-listener/index.d.ts +1 -32
- package/dist/todo-listener/index.d.ts.map +1 -1
- package/dist/todo-listener.js +30 -1
- package/dist/todo-tracker/index.d.ts +1 -1
- package/dist/todo-tracker/index.d.ts.map +1 -1
- package/dist/todo-tracker.js +3 -4
- package/dist/token-budget/index.d.ts +1 -1
- package/dist/token-budget/index.d.ts.map +1 -1
- package/dist/token-throttle/index.d.ts +1 -1
- package/dist/token-throttle/index.d.ts.map +1 -1
- package/dist/token-throttle.js +17 -2
- package/dist/type-gate/index.d.ts +1 -1
- package/dist/type-gate/index.d.ts.map +1 -1
- package/dist/type-gate.js +106 -19
- package/package.json +80 -68
package/dist/secret-scanner.js
CHANGED
|
@@ -1,20 +1,44 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
var
|
|
1
|
+
// src/runtime/credential-patterns.ts
|
|
2
|
+
var CREDENTIAL_PATTERNS = [
|
|
3
3
|
// LLM provider keys
|
|
4
4
|
{
|
|
5
5
|
type: "anthropic_key",
|
|
6
6
|
regex: /(?<![A-Za-z0-9])sk-ant-api\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g
|
|
7
7
|
},
|
|
8
|
-
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g },
|
|
9
|
-
// GitHub
|
|
8
|
+
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?!ant)(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g },
|
|
9
|
+
// GitHub. `ghp_` is only the personal-access-token prefix — the OAuth
|
|
10
|
+
// (`gho_`), user-to-server (`ghu_`), server-to-server (`ghs_`) and
|
|
11
|
+
// refresh (`ghr_`) tokens grant the same or broader access and were
|
|
12
|
+
// previously not detected at all.
|
|
10
13
|
{ type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g },
|
|
14
|
+
{
|
|
15
|
+
type: "github_oauth_token",
|
|
16
|
+
regex: /(?<![A-Za-z0-9])gh[ousr]_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g
|
|
17
|
+
},
|
|
11
18
|
{ type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g },
|
|
19
|
+
// GitLab
|
|
20
|
+
{ type: "gitlab_pat", regex: /(?<![A-Za-z0-9])glpat-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g },
|
|
21
|
+
{
|
|
22
|
+
type: "gitlab_runner_token",
|
|
23
|
+
regex: /(?<![A-Za-z0-9])glrt-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
|
|
24
|
+
},
|
|
25
|
+
// npm — a leaked publish token is a supply-chain compromise.
|
|
26
|
+
{ type: "npm_token", regex: /(?<![A-Za-z0-9])npm_[A-Za-z0-9]{36}(?![A-Za-z0-9])/g },
|
|
12
27
|
// AWS
|
|
13
28
|
{ type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g },
|
|
14
29
|
// GCP
|
|
15
30
|
{ type: "gcp_key", regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g },
|
|
16
|
-
// Slack
|
|
17
|
-
|
|
31
|
+
// Slack. `xoxe` (token-rotation) and `xapp` (app-level) were missing;
|
|
32
|
+
// both are as sensitive as the bot/user tokens already covered.
|
|
33
|
+
{
|
|
34
|
+
type: "slack_token",
|
|
35
|
+
regex: /(?<![A-Za-z0-9-])xox[abposer]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g
|
|
36
|
+
},
|
|
37
|
+
{ type: "slack_app_token", regex: /(?<![A-Za-z0-9-])xapp-\d-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g },
|
|
38
|
+
{
|
|
39
|
+
type: "slack_webhook",
|
|
40
|
+
regex: /https:\/\/hooks\.slack\.com\/services\/T[A-Za-z0-9_-]+\/B[A-Za-z0-9_-]+\/[A-Za-z0-9]{16,}/g
|
|
41
|
+
},
|
|
18
42
|
// Stripe
|
|
19
43
|
{
|
|
20
44
|
type: "stripe_key",
|
|
@@ -25,7 +49,7 @@ var BASE_PATTERNS = [
|
|
|
25
49
|
// Telegram
|
|
26
50
|
{
|
|
27
51
|
type: "telegram_bot_token",
|
|
28
|
-
regex:
|
|
52
|
+
regex: /(?:(?<![A-Za-z0-9_])|(?<=(?:^|[^A-Za-z0-9_])bot))\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
|
|
29
53
|
},
|
|
30
54
|
// JWT
|
|
31
55
|
{
|
|
@@ -35,29 +59,93 @@ var BASE_PATTERNS = [
|
|
|
35
59
|
// Private keys
|
|
36
60
|
{
|
|
37
61
|
type: "private_key",
|
|
38
|
-
regex: /(?:^|\n)
|
|
62
|
+
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
63
|
},
|
|
40
64
|
// AI/ML provider tokens
|
|
41
65
|
{ type: "huggingface_token", regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g },
|
|
42
66
|
{ type: "replicate_token", regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
43
67
|
{ type: "perplexity_key", regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
44
68
|
{ type: "groq_key", regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
69
|
+
// SaaS / infrastructure tokens — each grants API access on the user's
|
|
70
|
+
// account, and each was previously invisible to this gate.
|
|
71
|
+
{ type: "sendgrid_key", regex: /(?<![A-Za-z0-9])SG\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g },
|
|
72
|
+
{ type: "digitalocean_token", regex: /(?<![A-Za-z0-9])dop_v1_[a-f0-9]{64}(?![A-Za-z0-9])/g },
|
|
73
|
+
{ type: "doppler_token", regex: /(?<![A-Za-z0-9])dp\.(?:pt|st|sa|scim|audit)\.[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
74
|
+
{ type: "shopify_token", regex: /(?<![A-Za-z0-9])shp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}(?![A-Za-z0-9])/g },
|
|
75
|
+
{ type: "docker_pat", regex: /(?<![A-Za-z0-9])dckr_pat_[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g },
|
|
76
|
+
{ type: "linear_key", regex: /(?<![A-Za-z0-9])lin_api_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g },
|
|
77
|
+
{ type: "atlassian_token", regex: /(?<![A-Za-z0-9])ATATT3[A-Za-z0-9_\-=]{40,}(?![A-Za-z0-9_\-=])/g },
|
|
78
|
+
{ type: "square_token", regex: /(?<![A-Za-z0-9])(?:sq0(?:atp|csp)-|EAAA)[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g },
|
|
79
|
+
{
|
|
80
|
+
type: "azure_storage_key",
|
|
81
|
+
regex: /AccountKey=[A-Za-z0-9+/]{80,}={0,2}/g
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: "google_oauth_client_secret",
|
|
85
|
+
regex: /(?<![A-Za-z0-9_-])GOCSPX-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
|
|
86
|
+
},
|
|
45
87
|
// Bearer tokens
|
|
46
88
|
{
|
|
47
89
|
type: "bearer_token",
|
|
48
|
-
regex: /(
|
|
90
|
+
regex: /(?<![A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?![A-Za-z0-9._~+/-])/g
|
|
91
|
+
},
|
|
92
|
+
// Database URIs. Require password-bearing user-info; credential-free values stay scannable.
|
|
93
|
+
{ type: "mongodb_uri", regex: /mongodb(?:\+srv)?:\/\/[^\s:/@"'`]*:[^\s/@"'`]+@[^\s"'`]+/g },
|
|
94
|
+
{
|
|
95
|
+
type: "postgres_uri",
|
|
96
|
+
// Query parsers decode percent-encoded parameter names. Recognize each
|
|
97
|
+
// encoded character in `password` so mixed forms such as `pass%77ord`
|
|
98
|
+
// cannot bypass detection while keeping the scan strictly bounded.
|
|
99
|
+
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
100
|
},
|
|
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 }
|
|
101
|
+
{ type: "mysql_uri", regex: /mysql:\/\/[^\s:/@"'`]*:[^\s/@"'`]+@[^\s"'`]+/g },
|
|
102
|
+
{ type: "redis_uri", regex: /redis:\/\/[^\s:/@"'`]*:[^\s/@"'`]+@[^\s"'`]+/g }
|
|
55
103
|
];
|
|
104
|
+
function cloneCredentialPatterns() {
|
|
105
|
+
return CREDENTIAL_PATTERNS.map((p) => ({
|
|
106
|
+
type: p.type,
|
|
107
|
+
regex: new RegExp(p.regex.source, p.regex.flags)
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/secret-scanner/index.ts
|
|
112
|
+
var BASE_PATTERNS = cloneCredentialPatterns();
|
|
56
113
|
var PATTERNS = [...BASE_PATTERNS];
|
|
114
|
+
var patternCacheKey = "";
|
|
115
|
+
var GROUP_INDEX_OF_PATTERN = [];
|
|
57
116
|
var COMBINED_REGEX = buildCombinedRegex(PATTERNS);
|
|
58
|
-
var
|
|
117
|
+
var SCAN_WINDOW_LENGTH = 1e5;
|
|
118
|
+
var SCAN_WINDOW_OVERLAP = 4096;
|
|
119
|
+
var MAX_TOTAL_SCAN_LENGTH = 8 * 1024 * 1024;
|
|
120
|
+
var TOO_LARGE_MARKER = "unscannable_oversized_input";
|
|
59
121
|
var RE_DOS_TIMEOUT_MS = 100;
|
|
122
|
+
function countCaptureGroups(source) {
|
|
123
|
+
try {
|
|
124
|
+
return new RegExp(`${source}|`).exec("").length - 1;
|
|
125
|
+
} catch {
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function patternTypeForGroups(groups) {
|
|
130
|
+
for (let i = 0; i < PATTERNS.length; i++) {
|
|
131
|
+
const at = GROUP_INDEX_OF_PATTERN[i];
|
|
132
|
+
if (at !== void 0 && groups[at] !== void 0) return PATTERNS[i].type;
|
|
133
|
+
}
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
60
136
|
function buildCombinedRegex(patterns) {
|
|
137
|
+
const newCacheKey = JSON.stringify(patterns.map((p) => [p.type, p.regex.source]));
|
|
138
|
+
if (newCacheKey === patternCacheKey && COMBINED_REGEX) {
|
|
139
|
+
return COMBINED_REGEX;
|
|
140
|
+
}
|
|
141
|
+
patternCacheKey = newCacheKey;
|
|
142
|
+
const offsets = [];
|
|
143
|
+
let cursor = 0;
|
|
144
|
+
for (const p of patterns) {
|
|
145
|
+
offsets.push(cursor);
|
|
146
|
+
cursor += 1 + countCaptureGroups(p.regex.source);
|
|
147
|
+
}
|
|
148
|
+
GROUP_INDEX_OF_PATTERN = offsets;
|
|
61
149
|
return new RegExp(patterns.map((p) => `(${p.regex.source})`).join("|"), "g");
|
|
62
150
|
}
|
|
63
151
|
var state = {
|
|
@@ -75,30 +163,38 @@ var state = {
|
|
|
75
163
|
/** PostToolUse hook handle so teardown can unregister. */
|
|
76
164
|
postHookUnregister: null
|
|
77
165
|
};
|
|
78
|
-
function
|
|
79
|
-
if (!text) return [];
|
|
80
|
-
if (text.length > RE_DOS_MAX_SCAN_LENGTH) return [];
|
|
81
|
-
const found = /* @__PURE__ */ new Set();
|
|
166
|
+
function scanWindow(window, found, startTime) {
|
|
82
167
|
COMBINED_REGEX.lastIndex = 0;
|
|
83
168
|
let m;
|
|
84
|
-
|
|
85
|
-
while ((m = COMBINED_REGEX.exec(text)) !== null) {
|
|
169
|
+
while ((m = COMBINED_REGEX.exec(window)) !== null) {
|
|
86
170
|
if (performance.now() - startTime > RE_DOS_TIMEOUT_MS) {
|
|
87
171
|
throw new Error(
|
|
88
|
-
`secret-scanner: ReDoS timeout \u2014 regex scan exceeded ${RE_DOS_TIMEOUT_MS}ms on ${
|
|
172
|
+
`secret-scanner: ReDoS timeout \u2014 regex scan exceeded ${RE_DOS_TIMEOUT_MS}ms on ${window.length}-char window`
|
|
89
173
|
);
|
|
90
174
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
found.add(PATTERNS[i].type);
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
175
|
+
const type = patternTypeForGroups(m.slice(1));
|
|
176
|
+
if (type !== void 0) found.add(type);
|
|
97
177
|
if (m.index === COMBINED_REGEX.lastIndex) {
|
|
98
178
|
COMBINED_REGEX.lastIndex += 1;
|
|
99
179
|
}
|
|
100
180
|
}
|
|
101
|
-
|
|
181
|
+
}
|
|
182
|
+
function findMatches(text) {
|
|
183
|
+
if (!text) return [];
|
|
184
|
+
const found = /* @__PURE__ */ new Set();
|
|
185
|
+
const startTime = performance.now();
|
|
186
|
+
if (text.length <= SCAN_WINDOW_LENGTH) {
|
|
187
|
+
scanWindow(text, found, startTime);
|
|
188
|
+
return Array.from(found).sort();
|
|
189
|
+
}
|
|
190
|
+
if (text.length > MAX_TOTAL_SCAN_LENGTH) {
|
|
191
|
+
return [TOO_LARGE_MARKER];
|
|
192
|
+
}
|
|
193
|
+
const stride = SCAN_WINDOW_LENGTH - SCAN_WINDOW_OVERLAP;
|
|
194
|
+
for (let offset = 0; offset < text.length; offset += stride) {
|
|
195
|
+
scanWindow(text.slice(offset, offset + SCAN_WINDOW_LENGTH), found, startTime);
|
|
196
|
+
}
|
|
197
|
+
return Array.from(found).sort();
|
|
102
198
|
}
|
|
103
199
|
function scanInput(input, depth = 0) {
|
|
104
200
|
if (input === null || input === void 0) return null;
|
|
@@ -123,30 +219,55 @@ function scanInput(input, depth = 0) {
|
|
|
123
219
|
}
|
|
124
220
|
return null;
|
|
125
221
|
}
|
|
222
|
+
function redactString(text) {
|
|
223
|
+
if (text.length > SCAN_WINDOW_LENGTH) {
|
|
224
|
+
return { ok: false, reason: "oversized_input" };
|
|
225
|
+
}
|
|
226
|
+
const startTime = performance.now();
|
|
227
|
+
let cursor = 0;
|
|
228
|
+
let output = "";
|
|
229
|
+
COMBINED_REGEX.lastIndex = 0;
|
|
230
|
+
let match;
|
|
231
|
+
while ((match = COMBINED_REGEX.exec(text)) !== null) {
|
|
232
|
+
if (performance.now() - startTime > RE_DOS_TIMEOUT_MS) {
|
|
233
|
+
return { ok: false, reason: "oversized_input" };
|
|
234
|
+
}
|
|
235
|
+
const type = patternTypeForGroups(match.slice(1));
|
|
236
|
+
output += text.slice(cursor, match.index);
|
|
237
|
+
output += `[REDACTED:${type ?? "unknown"}]`;
|
|
238
|
+
cursor = COMBINED_REGEX.lastIndex;
|
|
239
|
+
if (match.index === COMBINED_REGEX.lastIndex) {
|
|
240
|
+
COMBINED_REGEX.lastIndex += 1;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (cursor === 0) return { ok: true, value: text };
|
|
244
|
+
return { ok: true, value: output + text.slice(cursor) };
|
|
245
|
+
}
|
|
126
246
|
function redactInput(input, depth = 0) {
|
|
127
|
-
if (input === null || input === void 0) return input;
|
|
128
|
-
if (depth > 50) return
|
|
247
|
+
if (input === null || input === void 0) return { ok: true, value: input };
|
|
248
|
+
if (depth > 50) return { ok: false, reason: "maximum_depth_exceeded" };
|
|
129
249
|
if (typeof input === "string") {
|
|
130
|
-
return input
|
|
131
|
-
for (let i = 0; i < PATTERNS.length; i++) {
|
|
132
|
-
if (groups[i] !== void 0) {
|
|
133
|
-
return `[REDACTED:${PATTERNS[i].type}]`;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return "[REDACTED:unknown]";
|
|
137
|
-
});
|
|
250
|
+
return redactString(input);
|
|
138
251
|
}
|
|
139
252
|
if (Array.isArray(input)) {
|
|
140
|
-
|
|
253
|
+
const out = [];
|
|
254
|
+
for (const item of input) {
|
|
255
|
+
const redacted = redactInput(item, depth + 1);
|
|
256
|
+
if (!redacted.ok) return redacted;
|
|
257
|
+
out.push(redacted.value);
|
|
258
|
+
}
|
|
259
|
+
return { ok: true, value: out };
|
|
141
260
|
}
|
|
142
261
|
if (typeof input === "object") {
|
|
143
262
|
const out = {};
|
|
144
263
|
for (const [k, v] of Object.entries(input)) {
|
|
145
|
-
|
|
264
|
+
const redacted = redactInput(v, depth + 1);
|
|
265
|
+
if (!redacted.ok) return redacted;
|
|
266
|
+
out[k] = redacted.value;
|
|
146
267
|
}
|
|
147
|
-
return out;
|
|
268
|
+
return { ok: true, value: out };
|
|
148
269
|
}
|
|
149
|
-
return input;
|
|
270
|
+
return { ok: true, value: input };
|
|
150
271
|
}
|
|
151
272
|
var DEFAULTS = {
|
|
152
273
|
matcher: "bash|write|edit",
|
|
@@ -206,20 +327,21 @@ function buildHook(cfg, log) {
|
|
|
206
327
|
}
|
|
207
328
|
if (cfg.mode === "redact") {
|
|
208
329
|
const redacted = redactInput(input.toolInput);
|
|
209
|
-
if (redacted !== null && typeof redacted === "object" && !Array.isArray(redacted)) {
|
|
330
|
+
if (redacted.ok && redacted.value !== null && typeof redacted.value === "object" && !Array.isArray(redacted.value)) {
|
|
210
331
|
state.redactCount += 1;
|
|
211
332
|
log.info(`[secret-scanner] redacted ${toolName} \u2014 matched: ${summary}`);
|
|
212
333
|
return {
|
|
213
334
|
decision: "allow",
|
|
214
|
-
modifiedInput: redacted,
|
|
335
|
+
modifiedInput: redacted.value,
|
|
215
336
|
additionalContext: `secret-scanner: redacted ${matched.length} credential pattern(s) from the ${toolName} arguments before execution.`
|
|
216
337
|
};
|
|
217
338
|
}
|
|
218
339
|
state.blockCount += 1;
|
|
219
340
|
state.lastBlock = { toolName, matchedTypes: matched, when };
|
|
341
|
+
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";
|
|
220
342
|
return {
|
|
221
343
|
decision: "block",
|
|
222
|
-
reason: `secret-scanner: cannot safely redact '${toolName}'
|
|
344
|
+
reason: `secret-scanner: cannot safely redact '${toolName}' because ${detail}; refusing to run.`
|
|
223
345
|
};
|
|
224
346
|
}
|
|
225
347
|
state.allowCount += 1;
|
|
@@ -237,10 +359,21 @@ function buildPostHook(cfg, log) {
|
|
|
237
359
|
const matched = findMatches(result.content);
|
|
238
360
|
if (matched.length === 0) return;
|
|
239
361
|
const toolName = input.toolName ?? "unknown";
|
|
240
|
-
const
|
|
362
|
+
const oversized = matched.includes(TOO_LARGE_MARKER);
|
|
363
|
+
const credentialMatches = matched.filter((type) => type !== TOO_LARGE_MARKER);
|
|
364
|
+
if (oversized && credentialMatches.length === 0) {
|
|
365
|
+
log.warn(
|
|
366
|
+
`[secret-scanner] POST-TOOL UNSCANNABLE: ${toolName} output exceeds ${MAX_TOTAL_SCAN_LENGTH} characters`
|
|
367
|
+
);
|
|
368
|
+
return {
|
|
369
|
+
additionalContext: `
|
|
370
|
+
\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.`
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const summary = credentialMatches.join(", ");
|
|
241
374
|
const when = (/* @__PURE__ */ new Date()).toISOString();
|
|
242
375
|
state.leakCount += 1;
|
|
243
|
-
state.lastLeak = { toolName, matchedTypes:
|
|
376
|
+
state.lastLeak = { toolName, matchedTypes: credentialMatches, when };
|
|
244
377
|
log.warn(`[secret-scanner] POST-TOOL LEAK: ${toolName} output matched ${summary}`);
|
|
245
378
|
return {
|
|
246
379
|
additionalContext: `
|
|
@@ -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,
|
|
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"}
|
|
@@ -1,9 +1,140 @@
|
|
|
1
1
|
// src/security-hotspot-scanner/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
3
3
|
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
|
|
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("-");
|
|
@@ -16,7 +147,8 @@ function withinProjectPath(projectRoot, candidate) {
|
|
|
16
147
|
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
17
148
|
}
|
|
18
149
|
function withinProject(p) {
|
|
19
|
-
|
|
150
|
+
const cwd = process.cwd();
|
|
151
|
+
return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
|
|
20
152
|
}
|
|
21
153
|
|
|
22
154
|
// src/security-hotspot-scanner/index.ts
|
|
@@ -29,7 +161,7 @@ var state = {
|
|
|
29
161
|
skippedCount: 0,
|
|
30
162
|
blockedCount: 0,
|
|
31
163
|
lastResult: null,
|
|
32
|
-
knownHotspots:
|
|
164
|
+
knownHotspots: new BoundedSet({ max: 5e3 }),
|
|
33
165
|
hookUnregister: null
|
|
34
166
|
};
|
|
35
167
|
var DEFAULTS = {
|
|
@@ -38,14 +170,20 @@ var DEFAULTS = {
|
|
|
38
170
|
maxFindings: 10,
|
|
39
171
|
scanOnChange: [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".py", ".java", ".go", ".rb", ".php"]
|
|
40
172
|
};
|
|
173
|
+
var scanOnChangeSet = new Set(DEFAULTS.scanOnChange);
|
|
41
174
|
function readConfig(raw) {
|
|
42
|
-
if (!raw || typeof raw !== "object")
|
|
175
|
+
if (!raw || typeof raw !== "object") {
|
|
176
|
+
scanOnChangeSet = new Set(DEFAULTS.scanOnChange);
|
|
177
|
+
return { ...DEFAULTS };
|
|
178
|
+
}
|
|
43
179
|
const r = raw;
|
|
180
|
+
const scanOnChange = Array.isArray(r["scanOnChange"]) ? r["scanOnChange"].filter((x) => typeof x === "string") : DEFAULTS.scanOnChange;
|
|
181
|
+
scanOnChangeSet = new Set(scanOnChange);
|
|
44
182
|
return {
|
|
45
183
|
enabled: r["enabled"] === true,
|
|
46
184
|
severity: r["severity"] === "block" ? "block" : DEFAULTS.severity,
|
|
47
185
|
maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 100 ? r["maxFindings"] : DEFAULTS.maxFindings,
|
|
48
|
-
scanOnChange
|
|
186
|
+
scanOnChange
|
|
49
187
|
};
|
|
50
188
|
}
|
|
51
189
|
var PATTERNS = [
|
|
@@ -101,7 +239,7 @@ function isSourceFile(filePath, extensions) {
|
|
|
101
239
|
const lower = filePath.toLowerCase();
|
|
102
240
|
return extensions.some((ext) => lower.endsWith(ext));
|
|
103
241
|
}
|
|
104
|
-
function scanPath(inputPath, cfg) {
|
|
242
|
+
async function scanPath(inputPath, cfg) {
|
|
105
243
|
const start = Date.now();
|
|
106
244
|
const root = process.cwd();
|
|
107
245
|
const resolved = isAbsolute2(inputPath) ? resolve2(inputPath) : resolve2(root, inputPath);
|
|
@@ -118,10 +256,10 @@ function scanPath(inputPath, cfg) {
|
|
|
118
256
|
let filesScanned = 0;
|
|
119
257
|
const allFindings = [];
|
|
120
258
|
const maxPerFile = Math.max(1, Math.floor(cfg.maxFindings / 2));
|
|
121
|
-
const scanFile = (filePath) => {
|
|
259
|
+
const scanFile = async (filePath) => {
|
|
122
260
|
if (!isSourceFile(filePath, cfg.scanOnChange)) return;
|
|
123
261
|
try {
|
|
124
|
-
const content =
|
|
262
|
+
const content = await readFile(filePath, "utf-8");
|
|
125
263
|
filesScanned += 1;
|
|
126
264
|
const findings = scanSource(content, maxPerFile);
|
|
127
265
|
for (const f of findings) {
|
|
@@ -131,10 +269,10 @@ function scanPath(inputPath, cfg) {
|
|
|
131
269
|
} catch {
|
|
132
270
|
}
|
|
133
271
|
};
|
|
134
|
-
const walk = (dir) => {
|
|
272
|
+
const walk = async (dir) => {
|
|
135
273
|
let entries;
|
|
136
274
|
try {
|
|
137
|
-
entries =
|
|
275
|
+
entries = await readdir(dir);
|
|
138
276
|
} catch {
|
|
139
277
|
return;
|
|
140
278
|
}
|
|
@@ -142,25 +280,25 @@ function scanPath(inputPath, cfg) {
|
|
|
142
280
|
if (allFindings.length >= cfg.maxFindings) return;
|
|
143
281
|
const full = resolve2(dir, entry);
|
|
144
282
|
try {
|
|
145
|
-
const st =
|
|
283
|
+
const st = await stat(full);
|
|
146
284
|
if (st.isDirectory()) {
|
|
147
285
|
if (entry.startsWith(".") || entry === "node_modules" || entry === "dist" || entry === "coverage") {
|
|
148
286
|
continue;
|
|
149
287
|
}
|
|
150
|
-
walk(full);
|
|
288
|
+
await walk(full);
|
|
151
289
|
} else if (st.isFile()) {
|
|
152
|
-
scanFile(full);
|
|
290
|
+
await scanFile(full);
|
|
153
291
|
}
|
|
154
292
|
} catch {
|
|
155
293
|
}
|
|
156
294
|
}
|
|
157
295
|
};
|
|
158
296
|
try {
|
|
159
|
-
const st =
|
|
297
|
+
const st = await stat(resolved);
|
|
160
298
|
if (st.isDirectory()) {
|
|
161
|
-
walk(resolved);
|
|
299
|
+
await walk(resolved);
|
|
162
300
|
} else if (st.isFile()) {
|
|
163
|
-
scanFile(resolved);
|
|
301
|
+
await scanFile(resolved);
|
|
164
302
|
} else {
|
|
165
303
|
return {
|
|
166
304
|
path: inputPath,
|
|
@@ -238,7 +376,7 @@ var plugin = {
|
|
|
238
376
|
state.hookUnregister = null;
|
|
239
377
|
}
|
|
240
378
|
const cfg = readConfig(api.config.extensions?.["security-hotspot-scanner"]);
|
|
241
|
-
const hook = (input) => {
|
|
379
|
+
const hook = async (input) => {
|
|
242
380
|
if (!cfg.enabled) return;
|
|
243
381
|
if (input.toolResult?.isError) return;
|
|
244
382
|
const inp = input.toolInput ?? {};
|
|
@@ -246,12 +384,12 @@ var plugin = {
|
|
|
246
384
|
if (!sourcePath || typeof sourcePath !== "string") return;
|
|
247
385
|
if (!withinProject(sourcePath)) return;
|
|
248
386
|
const ext = sourcePath.includes(".") ? sourcePath.slice(sourcePath.lastIndexOf(".")).toLowerCase() : "";
|
|
249
|
-
if (!
|
|
387
|
+
if (!scanOnChangeSet.has(ext)) {
|
|
250
388
|
state.skippedCount += 1;
|
|
251
389
|
return;
|
|
252
390
|
}
|
|
253
391
|
state.hookInvocationCount += 1;
|
|
254
|
-
const result = scanPath(sourcePath, cfg);
|
|
392
|
+
const result = await scanPath(sourcePath, cfg);
|
|
255
393
|
state.scanCount += 1;
|
|
256
394
|
state.fileScanCount += result.filesScanned;
|
|
257
395
|
if (!result.scanned) {
|
|
@@ -307,7 +445,7 @@ Review or remove the risky pattern(s).`;
|
|
|
307
445
|
mutating: false,
|
|
308
446
|
async execute(input) {
|
|
309
447
|
if (!cfg.enabled) return { ok: false, error: "security-hotspot-scanner is disabled" };
|
|
310
|
-
const result = scanPath(input.path, cfg);
|
|
448
|
+
const result = await scanPath(input.path, cfg);
|
|
311
449
|
state.scanCount += 1;
|
|
312
450
|
state.fileScanCount += result.filesScanned;
|
|
313
451
|
state.findingCount += result.findings.length;
|
|
@@ -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,
|
|
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,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - semver_current: Show the current version from package.json
|
|
7
7
|
* - semver_changelog: Generate a changelog between two versions
|
|
8
8
|
*/
|
|
9
|
-
import type { Plugin } from '@wrongstack/core';
|
|
9
|
+
import type { Plugin } from '@wrongstack/core/types';
|
|
10
10
|
type BumpType = 'major' | 'minor' | 'patch' | 'auto';
|
|
11
11
|
interface ConventionalCommit {
|
|
12
12
|
hash: string;
|