@activemind/scd 1.4.0
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/LICENSE.md +35 -0
- package/README.md +417 -0
- package/bin/scd.js +140 -0
- package/lib/audit-report.js +93 -0
- package/lib/audit-sync.js +172 -0
- package/lib/audit.js +356 -0
- package/lib/cli-helpers.js +108 -0
- package/lib/commands/accept.js +28 -0
- package/lib/commands/audit.js +17 -0
- package/lib/commands/configure.js +200 -0
- package/lib/commands/doctor.js +14 -0
- package/lib/commands/exceptions.js +19 -0
- package/lib/commands/export-findings.js +46 -0
- package/lib/commands/findings.js +306 -0
- package/lib/commands/ignore.js +28 -0
- package/lib/commands/init.js +16 -0
- package/lib/commands/insights.js +24 -0
- package/lib/commands/install.js +15 -0
- package/lib/commands/list.js +109 -0
- package/lib/commands/remove.js +16 -0
- package/lib/commands/repo.js +862 -0
- package/lib/commands/report.js +234 -0
- package/lib/commands/resolve.js +25 -0
- package/lib/commands/rules.js +185 -0
- package/lib/commands/scan.js +519 -0
- package/lib/commands/scope.js +341 -0
- package/lib/commands/sync.js +40 -0
- package/lib/commands/uninstall.js +15 -0
- package/lib/commands/version.js +33 -0
- package/lib/comment-map.js +388 -0
- package/lib/config.js +325 -0
- package/lib/context-modifiers.js +211 -0
- package/lib/deep-analyzer.js +225 -0
- package/lib/doctor.js +236 -0
- package/lib/exception-manager.js +675 -0
- package/lib/export-findings.js +376 -0
- package/lib/file-context.js +380 -0
- package/lib/file-filter.js +204 -0
- package/lib/file-manifest.js +145 -0
- package/lib/git-utils.js +102 -0
- package/lib/global-config.js +239 -0
- package/lib/hooks-manager.js +130 -0
- package/lib/init-repo.js +147 -0
- package/lib/insights-analyzer.js +416 -0
- package/lib/insights-output.js +160 -0
- package/lib/installer.js +128 -0
- package/lib/output-constants.js +32 -0
- package/lib/output-terminal.js +407 -0
- package/lib/push-queue.js +322 -0
- package/lib/remove-repo.js +108 -0
- package/lib/repo-context.js +187 -0
- package/lib/report-html.js +1154 -0
- package/lib/report-index.js +157 -0
- package/lib/report-json.js +136 -0
- package/lib/report-markdown.js +250 -0
- package/lib/resolve-manager.js +148 -0
- package/lib/rule-registry.js +205 -0
- package/lib/scan-cache.js +171 -0
- package/lib/scan-context.js +312 -0
- package/lib/scan-schema.js +67 -0
- package/lib/scanner-full.js +681 -0
- package/lib/scanner-manual.js +348 -0
- package/lib/scanner-secrets.js +83 -0
- package/lib/scope.js +331 -0
- package/lib/store-verify.js +395 -0
- package/lib/store.js +310 -0
- package/lib/taint-register.js +196 -0
- package/lib/version-check.js +46 -0
- package/package.json +37 -0
- package/rules/rule-loader.js +324 -0
- package/rules/rules-aspx-cs.json +399 -0
- package/rules/rules-aspx.json +222 -0
- package/rules/rules-infra-leakage.json +434 -0
- package/rules/rules-js.json +664 -0
- package/rules/rules-php.json +521 -0
- package/rules/rules-python.json +466 -0
- package/rules/rules-secrets.json +99 -0
- package/rules/rules-sensitive-files.json +475 -0
- package/rules/rules-ts.json +76 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"rules": [
|
|
4
|
+
{
|
|
5
|
+
"id": "ENV-001",
|
|
6
|
+
"name": "Hardcoded secret in .env file committed to repository",
|
|
7
|
+
"severity": "CRITICAL",
|
|
8
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
9
|
+
"pattern": "^[A-Z0-9_]*(?:PASSWORD|PASSWD|_SECRET|_TOKEN|_KEY|API_KEY|API_SECRET|ACCESS_KEY|CLIENT_SECRET|PRIVATE_KEY|SIGNING_KEY|ENCRYPTION_KEY|STRIPE|TWILIO|SENDGRID|AWS_SECRET|AZURE_SECRET|GCP_KEY|GITHUB_TOKEN|GITLAB_TOKEN)[A-Z0-9_]*\\s*=\\s*\\S.{3,}",
|
|
10
|
+
"flags": "gim",
|
|
11
|
+
"antipattern": "=\\s*(?:your_|change_|replace_|example_|test_|<|>|\\$\\{|%\\(|placeholder)",
|
|
12
|
+
"antipattern_flags": "i",
|
|
13
|
+
"lookahead": 5,
|
|
14
|
+
"file_types": [
|
|
15
|
+
"env"
|
|
16
|
+
],
|
|
17
|
+
"why": ".env files are designed to keep secrets OUT of source code — but committing the .env file itself defeats that purpose entirely. Any secret in a committed .env file is as exposed as if it were hardcoded.",
|
|
18
|
+
"scenario": "Developer runs git add . and commits .env along with other files. The repository now stores DB_PASSWORD, API_KEY, and JWT_SECRET in its history forever — including after deletion.",
|
|
19
|
+
"fix": "Add .env to .gitignore immediately. Rotate all exposed secrets. Commit only .env.example with placeholder values. Use CI/CD secrets management (GitHub Secrets, Azure Key Vault) for deployment environments."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "ENV-002",
|
|
23
|
+
"name": "Production .env file with credentials (.env.production, .env.prod)",
|
|
24
|
+
"severity": "CRITICAL",
|
|
25
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
26
|
+
"match_mode": "filename",
|
|
27
|
+
"pattern": "\\.env\\.(?:prod(?:uction)?|staging|live|release|deploy)",
|
|
28
|
+
"flags": "i",
|
|
29
|
+
"file_types": [
|
|
30
|
+
"env"
|
|
31
|
+
],
|
|
32
|
+
"why": "Production environment files contain live credentials for databases, payment systems, and third-party APIs. A single exposure can result in immediate breach of production systems.",
|
|
33
|
+
"scenario": ".env.production is pushed to a public repository. Automated scrapers find it within minutes. Production database, payment API keys, and email service credentials are compromised.",
|
|
34
|
+
"fix": "Remove from repository immediately (use git filter-branch or BFG Repo Cleaner to purge history). Rotate all credentials. Use environment-specific secrets management — never store production secrets in files."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "SQL-001",
|
|
38
|
+
"name": "SQL dump file in repository or web-accessible location",
|
|
39
|
+
"severity": "CRITICAL",
|
|
40
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
41
|
+
"match_mode": "filename",
|
|
42
|
+
"pattern": "(?:dump|backup|export|full|prod|database|db).*\\.sql$|\\.sql\\.(?:gz|bak|zip)$",
|
|
43
|
+
"flags": "i",
|
|
44
|
+
"file_types": [
|
|
45
|
+
"sql"
|
|
46
|
+
],
|
|
47
|
+
"why": "SQL dump files contain the complete database schema and often production data including user records, hashed passwords, PII, and transaction history. They are frequently created for migration and left in accessible locations.",
|
|
48
|
+
"scenario": "Developer creates database.sql for migration, forgets to delete it. File is reachable at https://company.com/database.sql. Attacker downloads the entire production database including user emails and password hashes.",
|
|
49
|
+
"fix": "Remove SQL dumps from web-accessible directories and version control. Store backups outside the web root in access-controlled storage. Use .gitignore patterns: *.sql, *.dump."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "SQL-002",
|
|
53
|
+
"name": "Hardcoded credentials in SQL file (CREATE USER / ALTER USER)",
|
|
54
|
+
"severity": "CRITICAL",
|
|
55
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
56
|
+
"pattern": "(?:CREATE|ALTER)\\s+USER\\s+[^;]{0,100}IDENTIFIED\\s+BY\\s+['\"][^'\"]{1,80}['\"]",
|
|
57
|
+
"flags": "gi",
|
|
58
|
+
"file_types": [
|
|
59
|
+
"sql"
|
|
60
|
+
],
|
|
61
|
+
"why": "SQL scripts that create database users often include plaintext passwords for the initial setup. These scripts end up in version control and are rarely updated when passwords are rotated.",
|
|
62
|
+
"scenario": "setup.sql contains CREATE USER app_user IDENTIFIED BY \"ProductionPass123\". The script is committed to a private repository that later becomes public. The database account is compromised.",
|
|
63
|
+
"fix": "Never include passwords in SQL scripts. Use IDENTIFIED BY <EXTERNAL> and manage passwords separately, or use SQL scripts that read from environment variables via shell wrappers."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "SQL-003",
|
|
67
|
+
"name": "Personal data (PII) columns in SQL dump",
|
|
68
|
+
"severity": "HIGH",
|
|
69
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
70
|
+
"pattern": "INSERT\\s+INTO\\s+\\w+\\s*\\([^)]{0,200}(?:personnummer|ssn|social_security|passport|national_id|credit_card|card_number)[^)]{0,200}\\)",
|
|
71
|
+
"flags": "gi",
|
|
72
|
+
"file_types": [
|
|
73
|
+
"sql"
|
|
74
|
+
],
|
|
75
|
+
"why": "SQL dumps containing PII such as social security numbers, passport numbers or payment card data in version control or web-accessible locations constitute a data breach under GDPR and PCI-DSS.",
|
|
76
|
+
"scenario": "Developer exports users table for testing, commits users_export.sql. The file contains 50,000 records with personnummer (Swedish SSN) and email addresses. GDPR breach notification is now required.",
|
|
77
|
+
"fix": "Never commit real user data to version control. Use anonymized/synthetic test data. If a dump is required for debugging, strip PII first using a data masking tool."
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "YAML-001",
|
|
81
|
+
"name": "Hardcoded password in YAML config file",
|
|
82
|
+
"severity": "CRITICAL",
|
|
83
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
84
|
+
"pattern": "(?:password|passwd|pwd|secret|api_key|api_secret|token|private_key)\\s*:\\s*(?!.*\\$\\{)(?!.*\\$\\{\\{)['\"]?[^\\s'\"#\\n$%{]{6,}['\"]?",
|
|
85
|
+
"flags": "gi",
|
|
86
|
+
"antipattern": "\\$\\{|\\$\\{\\{|<[A-Z_]+>|YOUR_|CHANGE_ME|PLACEHOLDER|example|test123|\\bstring\\b|\\binteger\\b|\\bnumber\\b|\\bboolean\\b|\\bobject\\b|\\barray\\b|\\bnull\\b|\\btrue\\b|\\bfalse\\b|\\btype\\b|\\brequired\\b|\\bvalue\\b",
|
|
87
|
+
"antipattern_flags": "i",
|
|
88
|
+
"lookahead": 5,
|
|
89
|
+
"file_types": [
|
|
90
|
+
"yml",
|
|
91
|
+
"yaml"
|
|
92
|
+
],
|
|
93
|
+
"why": "YAML configuration files (Docker Compose, Kubernetes, CI/CD pipelines, application configs) are frequently committed to version control with hardcoded credentials that should be injected at runtime.",
|
|
94
|
+
"scenario": "docker-compose.yml contains POSTGRES_PASSWORD: \"ProductionPass\" and is committed. Anyone with repository access — including contractors, junior devs, and CI/CD systems — has the production database password.",
|
|
95
|
+
"fix": "Use environment variable references: password: ${DB_PASSWORD}. For Kubernetes: use Secrets objects. For GitHub Actions: use ${{ secrets.MY_SECRET }}. Never put literal credentials in YAML files."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"id": "YAML-002",
|
|
99
|
+
"name": "CI/CD pipeline with hardcoded secret (GitHub Actions / GitLab CI)",
|
|
100
|
+
"severity": "CRITICAL",
|
|
101
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
102
|
+
"pattern": "(?:env|environment)\\s*:\\s*\\n(?:\\s+[A-Z][A-Z0-9_]+\\s*:\\s*['\"]?[^\\s'\"#\\n$]{8,}['\"]?\\s*\\n){1,}",
|
|
103
|
+
"flags": "gm",
|
|
104
|
+
"antipattern": "\\$\\{\\{|\\$\\{|secrets\\.|vault\\.",
|
|
105
|
+
"antipattern_flags": "i",
|
|
106
|
+
"lookahead": 10,
|
|
107
|
+
"file_types": [
|
|
108
|
+
"yml",
|
|
109
|
+
"yaml"
|
|
110
|
+
],
|
|
111
|
+
"why": "CI/CD pipeline files (.github/workflows/*.yml, .gitlab-ci.yml) are version controlled. Hardcoded secrets in these files are exposed to everyone with repository read access, including forks.",
|
|
112
|
+
"scenario": "GitHub Actions workflow hardcodes AWS_SECRET_ACCESS_KEY. A contributor forks the repository for a PR. The fork now contains production AWS credentials with potential for cloud resource abuse.",
|
|
113
|
+
"fix": "Use secrets references exclusively: ${{ secrets.AWS_SECRET_KEY }}. Configure secrets in repository settings, not in workflow files. Audit pipeline files in code review for credential leaks."
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"id": "JSON-001",
|
|
117
|
+
"name": "Hardcoded API key or secret in JSON config file",
|
|
118
|
+
"severity": "CRITICAL",
|
|
119
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
120
|
+
"pattern": "\"(?:apiKey|api_key|apiSecret|api_secret|clientSecret|client_secret|accessToken|access_token|secretKey|secret_key|privateKey|private_key|authToken|auth_token)\"\\s*:\\s*\"([^\"]{8,})\"",
|
|
121
|
+
"flags": "gi",
|
|
122
|
+
"antipattern": "YOUR_|CHANGE|EXAMPLE|PLACEHOLDER|<[A-Z]|^:[a-zA-Z]",
|
|
123
|
+
"antipatterns": [
|
|
124
|
+
":\\s*[\"'][^\"']*\\s[^\"']*[\"']",
|
|
125
|
+
":\\s*[\"'](?:<[^>]+>|\\{\\{[^}]+\\}\\}|[A-Z][A-Z0-9_]{3,})[\"']",
|
|
126
|
+
":\\s*[\"'][a-zA-Z]{1,15}[\"']",
|
|
127
|
+
"%\\]"
|
|
128
|
+
],
|
|
129
|
+
"antipattern_flags": "i",
|
|
130
|
+
"lookahead": 5,
|
|
131
|
+
"file_types": [
|
|
132
|
+
"json"
|
|
133
|
+
],
|
|
134
|
+
"confidence_rules": [
|
|
135
|
+
{
|
|
136
|
+
"if_value_matches": "^(?:sk-|ghp_|ghs_|gho_|github_pat_|xoxb-|xoxp-|xoxa-|AKIA|AIza|ya29\\.|eyJ)",
|
|
137
|
+
"then": "HIGH"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"if_value_matches": "^[0-9a-f]{32,}$",
|
|
141
|
+
"then": "HIGH"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"if_value_matches": "^[A-Za-z0-9+/]{32,}={0,2}$",
|
|
145
|
+
"then": "HIGH"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"if_value_matches": "[^\\x00-\\x7F]|\\s",
|
|
149
|
+
"then": "LOW"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"if_value_matches": "^:",
|
|
153
|
+
"then": "LOW"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"if_value_not_matches": "\\d",
|
|
157
|
+
"then": "LOW"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"if_value_shorter_than": 12,
|
|
161
|
+
"then": "LOW"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"default": "MEDIUM"
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
"why": "JSON configuration files committed to version control expose API keys, OAuth secrets, and access tokens. These are particularly dangerous in frontend projects where appsettings or firebase configs contain service credentials.",
|
|
168
|
+
"scenario": "appsettings.json contains \"ConnectionString\": \"...Password=prod123...\" and is committed. Azure DevOps build log shows the file. Production database credentials are now in build history.",
|
|
169
|
+
"fix": "Use appsettings.{Environment}.json with environment-specific files excluded from git. For .NET: use User Secrets (dotnet user-secrets) for local dev and Key Vault for production. Never commit appsettings.Production.json."
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"id": "JSON-002",
|
|
173
|
+
"name": "Firebase / GCP service account key file",
|
|
174
|
+
"severity": "CRITICAL",
|
|
175
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
176
|
+
"pattern": "\"type\"\\s*:\\s*\"service_account\"[^}]{0,500}\"private_key\"\\s*:\\s*\"-----BEGIN",
|
|
177
|
+
"flags": "gi",
|
|
178
|
+
"file_types": [
|
|
179
|
+
"json"
|
|
180
|
+
],
|
|
181
|
+
"why": "GCP and Firebase service account JSON files contain private keys that grant programmatic access to cloud services. These files are frequently downloaded for local development and accidentally committed.",
|
|
182
|
+
"scenario": "firebase-service-account.json is committed to a repository. Automated scanners detect it within minutes. Attacker gains full Firebase admin access including reading all user data.",
|
|
183
|
+
"fix": "Add *service-account*.json and *-credentials.json to .gitignore. Use Workload Identity Federation or mounted secrets in production. For local dev, store outside the repository directory."
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"id": "XML-001",
|
|
187
|
+
"name": "Hardcoded credentials in XML config (Maven settings, Spring, NuGet)",
|
|
188
|
+
"severity": "CRITICAL",
|
|
189
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
190
|
+
"pattern": "<(?:password|passwd|secret|apiKey|api-key|accessKey|access-key|token)>(?!.*\\$\\{)[^<]{4,80}<\\/(?:password|passwd|secret|apiKey|api-key|accessKey|access-key|token)>",
|
|
191
|
+
"flags": "gi",
|
|
192
|
+
"antipattern": "\\$\\{|\\$ENV|CHANGE_ME|YOUR_PASSWORD|example",
|
|
193
|
+
"antipattern_flags": "i",
|
|
194
|
+
"lookahead": 5,
|
|
195
|
+
"file_types": [
|
|
196
|
+
"xml"
|
|
197
|
+
],
|
|
198
|
+
"why": "XML configuration files (Maven settings.xml, Spring applicationContext.xml, NuGet.config) are frequently committed with hardcoded credentials for package repositories, databases, and external services.",
|
|
199
|
+
"scenario": "Maven settings.xml contains <password>nexus-prod-pass</password> for the internal Nexus repository. Developer commits it. Attacker gains access to internal build artifacts and can inject malicious dependencies.",
|
|
200
|
+
"fix": "Use property references: <password>${env.NEXUS_PASSWORD}</password>. For Maven: use server credentials via CI/CD environment injection. For Spring: use @Value(\"${db.password}\") bound to environment variables."
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"id": "XML-002",
|
|
204
|
+
"name": "Connection string with password in XML config",
|
|
205
|
+
"severity": "CRITICAL",
|
|
206
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
207
|
+
"pattern": "connectionString\\s*=\\s*[\"'][^\"']{0,200}Password\\s*=[^;\"']{1,80}",
|
|
208
|
+
"flags": "gi",
|
|
209
|
+
"file_types": [
|
|
210
|
+
"xml",
|
|
211
|
+
"config"
|
|
212
|
+
],
|
|
213
|
+
"why": "Connection strings with embedded passwords in XML config files (Web.config, app.config, ApplicationHost.config) are a systemic issue in .NET applications. They are rarely encrypted and frequently committed.",
|
|
214
|
+
"scenario": "Web.config contains connectionString=\"...Password=SqlServerProd123\". The file is in the repository for all team members. It also exists on the server — readable by any user who gains file system access.",
|
|
215
|
+
"fix": "Encrypt Web.config connectionStrings section using aspnet_regiis -pe \"connectionStrings\". For new projects: use environment variables or Azure App Service connection string settings which override Web.config at runtime."
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "PROP-001",
|
|
219
|
+
"name": "Hardcoded password in .properties file",
|
|
220
|
+
"severity": "CRITICAL",
|
|
221
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
222
|
+
"pattern": "^[a-zA-Z][\\w.\\-]{2,60}(?:password|passwd|secret|api[_.]?key|token|credential)\\s*=\\s*(?!\\$\\{)(?!ENC\\()(?!your|change|example)[^\\s#\\n]{4,}",
|
|
223
|
+
"flags": "gim",
|
|
224
|
+
"antipattern": "=\\s*\\$\\{|=\\s*ENC\\(|#\\s*example|=\\s*<|=\\s*YOUR",
|
|
225
|
+
"antipattern_flags": "i",
|
|
226
|
+
"lookahead": 5,
|
|
227
|
+
"file_types": [
|
|
228
|
+
"properties"
|
|
229
|
+
],
|
|
230
|
+
"why": "Java .properties files (Spring Boot application.properties, Hibernate, JDBC config) are often committed with real credentials. Spring Boot's externalized configuration is designed to override these at runtime — but only if developers know to use it.",
|
|
231
|
+
"scenario": "spring.datasource.password=ProductionDBPass123 in application.properties. Committed to git. All 12 developers and the CI/CD system have the production database password.",
|
|
232
|
+
"fix": "Use Spring profiles: application-prod.properties excluded from git, loaded via SPRING_PROFILES_ACTIVE. Or use environment variable binding: spring.datasource.password=${DB_PASSWORD}. Jasypt can encrypt values: ENC(encryptedValue)."
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"id": "INI-001",
|
|
236
|
+
"name": "Hardcoded credentials in INI or CFG config file",
|
|
237
|
+
"severity": "HIGH",
|
|
238
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
239
|
+
"pattern": "^(?:password|passwd|pwd|secret|api_key|db_pass|db_password|token)\\s*=\\s*(?!\\$\\{)(?!your_|change_|example)[^\\s#\\n]{4,}",
|
|
240
|
+
"flags": "gim",
|
|
241
|
+
"antipattern": "^\\s*#|\\$\\{|<[A-Z]|your_password|change_me",
|
|
242
|
+
"antipattern_flags": "i",
|
|
243
|
+
"lookahead": 5,
|
|
244
|
+
"file_types": [
|
|
245
|
+
"ini",
|
|
246
|
+
"cfg",
|
|
247
|
+
"conf"
|
|
248
|
+
],
|
|
249
|
+
"why": "INI and CFG files are the classic configuration format for Python applications (Django, Flask via configparser), PHP (php.ini), and many legacy systems. They often contain database credentials and API keys committed alongside application code.",
|
|
250
|
+
"scenario": "config.cfg contains password = FlaskProdSecret committed by a junior developer who followed a tutorial. The tutorial used a real-looking password that the developer never changed.",
|
|
251
|
+
"fix": "Read sensitive values from environment variables in the config loader: password = %(DB_PASSWORD)s using configparser interpolation, or os.environ[\"DB_PASSWORD\"] in application startup code."
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"id": "SH-001",
|
|
255
|
+
"name": "Hardcoded secret in shell script (export or variable assignment)",
|
|
256
|
+
"severity": "HIGH",
|
|
257
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
258
|
+
"pattern": "(?:export\\s+)?[A-Z][A-Z0-9_]{2,40}(?:_KEY|_SECRET|_TOKEN|_PASSWORD|_PASS|_PWD|_API)\\s*=\\s*[\"'][^\"'\\n]{6,}[\"']",
|
|
259
|
+
"flags": "g",
|
|
260
|
+
"antipattern": "\\$\\{|\\$\\(|read\\s+-s|getpass|vault|secrets",
|
|
261
|
+
"antipattern_flags": "i",
|
|
262
|
+
"lookahead": 5,
|
|
263
|
+
"file_types": [
|
|
264
|
+
"sh",
|
|
265
|
+
"bash"
|
|
266
|
+
],
|
|
267
|
+
"why": "Shell scripts with hardcoded secrets are a common finding in DevOps and infrastructure repositories. They are often written as quick one-offs and then committed, or used in CI/CD pipelines where environment injection was not configured.",
|
|
268
|
+
"scenario": "deploy.sh contains export DB_PASSWORD=\"ProductionPass123\". It is committed as part of the deployment tooling. Every team member, contractor, and CI/CD system with repository access has the production password.",
|
|
269
|
+
"fix": "Read secrets from environment: DB_PASSWORD=\"${DB_PASSWORD:?DB_PASSWORD not set}\". The :? syntax causes the script to fail loudly if the variable is not set. For CI/CD, inject via pipeline secret management."
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"id": "SH-002",
|
|
273
|
+
"name": "Hardcoded credentials in curl or wget command",
|
|
274
|
+
"severity": "HIGH",
|
|
275
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
276
|
+
"pattern": "(?:curl|wget)[^#\\n]{0,200}(?:-H\\s+[\"']Authorization:\\s+(?:Bearer|Basic)\\s+[A-Za-z0-9+/=._-]{8,}|--user\\s+\\w+:[^\\s'\"]{4,}|--password\\s+[\"'][^\"'\\n]{4,}[\"'])",
|
|
277
|
+
"flags": "gi",
|
|
278
|
+
"antipattern": "\\$\\{|\\$[A-Z_]|read\\s+-s",
|
|
279
|
+
"antipattern_flags": "i",
|
|
280
|
+
"lookahead": 5,
|
|
281
|
+
"file_types": [
|
|
282
|
+
"sh",
|
|
283
|
+
"bash"
|
|
284
|
+
],
|
|
285
|
+
"why": "Shell scripts that make authenticated API calls often hardcode credentials in curl/wget commands. These commands may also appear in shell history, process listings, and log files.",
|
|
286
|
+
"scenario": "backup.sh uses curl with a hardcoded Bearer token for a cloud storage API. The token has write access. An attacker who reads this script can exfiltrate or overwrite all backed-up data.",
|
|
287
|
+
"fix": "Use environment variables: curl -H \"Authorization: Bearer ${API_TOKEN}\". Store the token in a secrets manager and inject it at runtime. Never pass credentials as literal strings in command-line arguments."
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"id": "PS-001",
|
|
291
|
+
"name": "Hardcoded credential in PowerShell script",
|
|
292
|
+
"severity": "HIGH",
|
|
293
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
294
|
+
"pattern": "\\$(?:password|passwd|apiKey|api_key|secret|token|credential|pass)\\s*=\\s*[\"'][^\"'\\n]{4,}[\"']|ConvertTo-SecureString\\s+[\"'][^\"'\\n]{4,}[\"']",
|
|
295
|
+
"flags": "gi",
|
|
296
|
+
"antipattern": "Read-Host|Get-Secret|ConvertTo-SecureString\\s+\\$|KeyVault|SecretManagement",
|
|
297
|
+
"antipattern_flags": "i",
|
|
298
|
+
"lookahead": 10,
|
|
299
|
+
"file_types": [
|
|
300
|
+
"ps1"
|
|
301
|
+
],
|
|
302
|
+
"why": "PowerShell scripts are common in Windows/.NET environments for deployment, database maintenance, and administration tasks. Hardcoded credentials in these scripts are a frequent finding in ASP.NET project repositories.",
|
|
303
|
+
"scenario": "$password = \"SqlAdmin123!\" is used to construct a SqlConnection in a database migration script. The script is in the repository. SQL Server admin credentials are now accessible to all developers.",
|
|
304
|
+
"fix": "Use Read-Host -AsSecureString for interactive scripts, or inject via environment: $password = $env:DB_PASSWORD. For automation, use the SecretManagement module or Azure Key Vault PowerShell module."
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"id": "BAT-001",
|
|
308
|
+
"name": "Hardcoded password in Windows batch script",
|
|
309
|
+
"severity": "HIGH",
|
|
310
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
311
|
+
"pattern": "SET\\s+(?:[A-Z][A-Z0-9_]{2,40})?(?:PASSWORD|PASSWD|API_KEY|SECRET|TOKEN|DB_PASS)\\s*=\\s*(?!%)[^\\s%\\n]{4,}",
|
|
312
|
+
"flags": "gi",
|
|
313
|
+
"antipattern": "SET\\s+\\w+=\\s*%\\w+%|SET\\s+\\/P",
|
|
314
|
+
"antipattern_flags": "i",
|
|
315
|
+
"lookahead": 5,
|
|
316
|
+
"file_types": [
|
|
317
|
+
"bat",
|
|
318
|
+
"cmd"
|
|
319
|
+
],
|
|
320
|
+
"why": "Windows batch scripts (.bat, .cmd) in ASP.NET and legacy Windows server environments frequently use SET to assign credentials for database connections, FTP uploads, and service calls.",
|
|
321
|
+
"scenario": "deploy.bat contains SET DB_PASSWORD=Prod@2024 for use in sqlcmd commands. The script is part of the release process and lives in the repository. Every team member has the production SQL Server password.",
|
|
322
|
+
"fix": "Read from environment: SET DB_PASSWORD=%DB_PASSWORD%. Inject the variable value via CI/CD pipeline variables (Azure DevOps pipeline variables, TeamCity parameters) which are never stored in source control."
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"id": "BAK-001",
|
|
326
|
+
"name": "Backup file in repository or web-accessible location",
|
|
327
|
+
"severity": "HIGH",
|
|
328
|
+
"category": "Security Misconfiguration (OWASP A05)",
|
|
329
|
+
"match_mode": "filename",
|
|
330
|
+
"pattern": "\\.(?:bak|old|orig|backup|tmp|temp|copy|swp|~)$|~$|\\.bak\\d*$",
|
|
331
|
+
"flags": "i",
|
|
332
|
+
"file_types": [
|
|
333
|
+
"bak",
|
|
334
|
+
"old",
|
|
335
|
+
"orig",
|
|
336
|
+
"tmp",
|
|
337
|
+
"swp"
|
|
338
|
+
],
|
|
339
|
+
"why": "Backup files are created by editors, deployment tools, and manual copy operations. They often contain older versions of configuration files with credentials, or copies of source files with sensitive logic exposed.",
|
|
340
|
+
"scenario": "Web.config.bak is created during a failed deployment and left in the web root. It is accessible at https://app.com/Web.config.bak and contains the production connection string with credentials.",
|
|
341
|
+
"fix": "Add backup file patterns to .gitignore: *.bak, *.old, *.orig, *.tmp, *~. Audit web root for these files. Configure web server to deny access to common backup extensions."
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"id": "BINSEC-001",
|
|
345
|
+
"name": "Private key file (.pem, .key) in repository",
|
|
346
|
+
"severity": "CRITICAL",
|
|
347
|
+
"category": "Cryptographic Failures (OWASP A02)",
|
|
348
|
+
"match_mode": "filename",
|
|
349
|
+
"pattern": "(?:^|[/\\\\])(?!.*(?:public|\\.pub$)).*\\.(?:pem|key|p8)$",
|
|
350
|
+
"flags": "i",
|
|
351
|
+
"file_types": [
|
|
352
|
+
"pem",
|
|
353
|
+
"key",
|
|
354
|
+
"p8"
|
|
355
|
+
],
|
|
356
|
+
"why": "PEM and KEY files contain private cryptographic material: SSL/TLS private keys, SSH private keys, code signing keys. A single committed private key can compromise an entire PKI infrastructure.",
|
|
357
|
+
"scenario": "ssl.pem is committed for convenience during development. The same certificate is used in production. Attacker clones the repository, extracts the private key, and can perform HTTPS man-in-the-middle attacks or decrypt recorded traffic.",
|
|
358
|
+
"fix": "Remove immediately using git filter-branch or BFG Repo Cleaner. Revoke and reissue the certificate. Add *.pem, *.key to .gitignore. Use certificate management services (Let's Encrypt, ACM) that never expose private keys."
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"id": "BINSEC-002",
|
|
362
|
+
"name": "PKCS#12 certificate bundle (.pfx, .p12) in repository",
|
|
363
|
+
"severity": "CRITICAL",
|
|
364
|
+
"category": "Cryptographic Failures (OWASP A02)",
|
|
365
|
+
"match_mode": "filename",
|
|
366
|
+
"pattern": "\\.(?:pfx|p12)$",
|
|
367
|
+
"flags": "i",
|
|
368
|
+
"file_types": [
|
|
369
|
+
"pfx",
|
|
370
|
+
"p12"
|
|
371
|
+
],
|
|
372
|
+
"why": "PFX/P12 files bundle a certificate with its private key, protected only by a password. If the file is committed, attackers need only crack or find the password (often in nearby config files) to extract the private key.",
|
|
373
|
+
"scenario": "app-cert.pfx is committed alongside its password in appsettings.json. Attacker extracts the private key, signs malicious code with the organization's code signing certificate, or decrypts TLS traffic.",
|
|
374
|
+
"fix": "Never commit PFX/P12 files. Store them in a hardware security module (HSM), Azure Key Vault, or a secrets manager. For local development use, store outside the repository and reference by absolute path."
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
"id": "BINSEC-003",
|
|
378
|
+
"name": "SQLite database file in repository",
|
|
379
|
+
"severity": "HIGH",
|
|
380
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
381
|
+
"match_mode": "filename",
|
|
382
|
+
"pattern": "\\.(?:sqlite|sqlite3|db|sdb)$",
|
|
383
|
+
"flags": "i",
|
|
384
|
+
"file_types": [
|
|
385
|
+
"sqlite",
|
|
386
|
+
"sqlite3",
|
|
387
|
+
"db",
|
|
388
|
+
"sdb"
|
|
389
|
+
],
|
|
390
|
+
"why": "SQLite database files committed to version control may contain user data, password hashes, session tokens, or application secrets stored as database records. The entire database is readable by anyone with repository access.",
|
|
391
|
+
"scenario": "app.db is committed as part of the initial project setup. It contains a seeded admin user with a bcrypt hash that is crackable offline. Attacker recovers the admin password and gains application access.",
|
|
392
|
+
"fix": "Add *.sqlite, *.sqlite3, *.db to .gitignore. Use migration scripts to define schema — never commit the database file itself. For test fixtures, use SQL seed scripts with synthetic data only."
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
"id": "LOG-001",
|
|
396
|
+
"name": "Connection string with credentials found in log file",
|
|
397
|
+
"severity": "CRITICAL",
|
|
398
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
399
|
+
"pattern": "(?:Server|Data Source|Host)\\s*=[^\\n]{0,200}(?:Password|PWD)\\s*=[^;\\n\"']{1,60}",
|
|
400
|
+
"flags": "gi",
|
|
401
|
+
"file_types": [
|
|
402
|
+
"log",
|
|
403
|
+
"txt"
|
|
404
|
+
],
|
|
405
|
+
"why": "Applications that log connection strings (during startup, connection errors, or debug output) expose database credentials in log files. Log files are often stored insecurely, forwarded to log aggregation services, or accessible to more people than intended.",
|
|
406
|
+
"scenario": "An application logs the full connection string when database connection fails. Error log is shipped to an ELK stack accessible to all developers. The production DB password is visible in Kibana to 40 people.",
|
|
407
|
+
"fix": "Never log connection strings. Use structured logging and explicitly exclude sensitive fields. Implement a log sanitizer that redacts patterns matching credentials before writing to log output."
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
"id": "LOG-002",
|
|
411
|
+
"name": "Password or secret value found in log file",
|
|
412
|
+
"severity": "HIGH",
|
|
413
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
414
|
+
"pattern": "(?:password|passwd|pwd|secret|api[_\\s]?key|token|credential)\\s*[=:]\\s*[\"']?[^\\s\"'#\\n]{6,}",
|
|
415
|
+
"flags": "gi",
|
|
416
|
+
"antipattern": "redacted|\\*{3,}|<hidden>|\\[FILTERED\\]|\\[REDACTED\\]",
|
|
417
|
+
"antipattern_flags": "i",
|
|
418
|
+
"lookahead": 10,
|
|
419
|
+
"file_types": [
|
|
420
|
+
"log",
|
|
421
|
+
"txt"
|
|
422
|
+
],
|
|
423
|
+
"why": "Log files that contain passwords or secrets represent a secondary exposure vector. Logs are often retained longer than necessary, backed up to insecure storage, or sent to external services without proper filtering.",
|
|
424
|
+
"scenario": "Login attempt logging includes the submitted password for debugging. Logs are shipped to a third-party log service. User passwords (including those reused from other services) are now stored by a third party.",
|
|
425
|
+
"fix": "Implement log sanitization that redacts sensitive parameter names before output. Never log request bodies that may contain passwords. Audit logging pipelines for credential leakage."
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
"id": "LOG-003",
|
|
429
|
+
"name": "Swedish personal identity number (personnummer) in log file",
|
|
430
|
+
"severity": "HIGH",
|
|
431
|
+
"category": "Sensitive Data Exposure / GDPR",
|
|
432
|
+
"pattern": "\\b(?:19|20)\\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\\d|3[01])-?\\d{4}\\b|\\b\\d{6}-\\d{4}\\b",
|
|
433
|
+
"flags": "g",
|
|
434
|
+
"file_types": [
|
|
435
|
+
"log",
|
|
436
|
+
"txt"
|
|
437
|
+
],
|
|
438
|
+
"why": "Swedish personnummer (personal identity numbers) in log files constitutes processing of sensitive personal data under GDPR Article 9 and the Swedish Personal Data Act. Logging PII without legal basis or proper safeguards triggers notification obligations.",
|
|
439
|
+
"scenario": "Application logs user identifiers as personnummer for debugging. Logs are retained for 2 years and accessible to operations staff. A log server compromise exposes 10,000 users' personal identity numbers — a notifiable GDPR breach.",
|
|
440
|
+
"fix": "Replace personnummer with internal user IDs in all log output. If logging is required for audit purposes, pseudonymize: log a hash of the personnummer, not the value itself. Review log retention policy."
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
"id": "LOG-004",
|
|
444
|
+
"name": "Email addresses in bulk in log file",
|
|
445
|
+
"severity": "MEDIUM",
|
|
446
|
+
"category": "Sensitive Data Exposure / GDPR",
|
|
447
|
+
"pattern": "[a-zA-Z0-9._%+\\-]{1,64}@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,10}",
|
|
448
|
+
"flags": "g",
|
|
449
|
+
"file_types": ["log"],
|
|
450
|
+
"why": "Log files containing many email addresses represent a bulk PII exposure. Email addresses are personal data under GDPR. Log files are often less protected than databases and more likely to be shared or forwarded.",
|
|
451
|
+
"scenario": "Application logs each user's email on login for audit purposes. Log file grows to 200MB with 85,000 unique email addresses. Developer copies the log file to their laptop for troubleshooting. GDPR breach.",
|
|
452
|
+
"fix": "Hash or truncate email addresses in logs: log user_id or a hash(email) instead. Review what constitutes necessary audit logging versus debug logging that should be disabled in production."
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"id": "EXPOSURE-001",
|
|
456
|
+
"name": "Exported email list in data file",
|
|
457
|
+
"severity": "HIGH",
|
|
458
|
+
"confidence": "MEDIUM",
|
|
459
|
+
"category": "Sensitive Data Exposure (OWASP A02)",
|
|
460
|
+
"file_types": ["csv"],
|
|
461
|
+
"pattern": "[a-zA-Z0-9._%+\\-]{1,64}@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,10}",
|
|
462
|
+
"flags": "gi",
|
|
463
|
+
"antipatterns": [
|
|
464
|
+
"Copyright|copyright|©|\\(C\\)|\\(c\\)",
|
|
465
|
+
"Author:|author:|Generated by|Generated on",
|
|
466
|
+
"LICENSE|license|LICENCE|licence",
|
|
467
|
+
"README|changelog|CHANGELOG|[Cc]ontributors",
|
|
468
|
+
"@example\\.com|@test\\.com|@domain\\.com|@email\\.com|noreply@|no-reply@"
|
|
469
|
+
],
|
|
470
|
+
"why": "A text or CSV file containing email addresses likely represents an exported user list or dataset committed to the repository. Email addresses are personal data (PII) subject to GDPR and similar regulations.",
|
|
471
|
+
"scenario": "A developer exports a user list to emails.txt for testing and commits it. The file contains real user email addresses.",
|
|
472
|
+
"fix": "Remove the file from the repository and purge it from git history (git filter-repo or BFG). Use anonymized or generated test data instead. Add *.csv and sensitive patterns to .gitignore."
|
|
473
|
+
}
|
|
474
|
+
]
|
|
475
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"rules": [
|
|
4
|
+
{
|
|
5
|
+
"id": "TS-TYPE-001",
|
|
6
|
+
"name": "Type assertion (as any) disables type safety on potentially unsafe value",
|
|
7
|
+
"severity": "MEDIUM",
|
|
8
|
+
"category": "Injection (OWASP A03)",
|
|
9
|
+
"pattern": "(?:req|request|ctx|context)\\s*(?:\\.\\s*\\w+)+\\s+as\\s+any\\b|as\\s+unknown\\s+as\\s+(?!never\\b)\\w+",
|
|
10
|
+
"flags": "gi",
|
|
11
|
+
"file_types": [
|
|
12
|
+
"ts",
|
|
13
|
+
"tsx"
|
|
14
|
+
],
|
|
15
|
+
"why": "Casting request-derived values to \"any\" silences the TypeScript compiler for all downstream usage. Any injection, traversal, or auth bypass that the type system would have caught is now invisible — both to the compiler and to code reviewers.",
|
|
16
|
+
"scenario": "AI generates const userId = req.params.id as any to resolve a type error quickly. userId flows into a SQL query. The compiler no longer warns when it is used in string concatenation, because \"any\" propagates silently through expressions.",
|
|
17
|
+
"fix": "Parse and validate at the boundary instead of asserting: const userId = z.string().uuid().parse(req.params.id). Use a validation library (Zod, Valibot, io-ts) and derive the type from the schema rather than asserting it."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "TS-TYPE-002",
|
|
21
|
+
"name": "Non-null assertion (!) applied directly to request input",
|
|
22
|
+
"severity": "MEDIUM",
|
|
23
|
+
"category": "Injection (OWASP A03)",
|
|
24
|
+
"pattern": "(?:req|request|ctx|context)\\s*\\.\\s*(?:body|params|query|headers)\\s*(?:\\.\\s*\\w+|\\[['\\\"]\\w+['\\\"]\\])\\s*!",
|
|
25
|
+
"flags": "gi",
|
|
26
|
+
"file_types": [
|
|
27
|
+
"ts",
|
|
28
|
+
"tsx"
|
|
29
|
+
],
|
|
30
|
+
"why": "The non-null assertion operator (!) removes null and undefined from the type without performing any runtime check. If the value is actually absent or malformed, the application throws at runtime — or worse, proceeds with undefined in a security-sensitive context.",
|
|
31
|
+
"scenario": "AI writes const token = req.headers.authorization!.split(\" \")[1] to satisfy the compiler. If Authorization is absent, this throws \"Cannot read properties of undefined\" — potentially triggering a different error handler that leaks stack trace details.",
|
|
32
|
+
"fix": "Check explicitly: const auth = req.headers.authorization; if (!auth) return res.status(401).json({ error: \"Unauthorized\" }); const token = auth.split(\" \")[1];"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "TS-SUPPRESS-001",
|
|
36
|
+
"name": "@ts-ignore or @ts-expect-error suppressing a type error in security-sensitive context",
|
|
37
|
+
"severity": "HIGH",
|
|
38
|
+
"category": "Security Misconfiguration (OWASP A05)",
|
|
39
|
+
"pattern": "\\/\\/\\s*@ts-(?:ignore|expect-error)[^\\n]*\\n[^\\n]{0,150}(?:query|exec|eval|password|token|secret|auth|cookie|session|sql|mongo|redis|fetch|axios|http)",
|
|
40
|
+
"flags": "gi",
|
|
41
|
+
"scan_comments": true,
|
|
42
|
+
"why": "@ts-ignore and @ts-expect-error tell the compiler to skip type checking for the next line. When placed before database calls, authentication logic, or HTTP requests, they hide exactly the type errors that would reveal unsafe data flow — often the same errors that signal an injection or auth bypass.",
|
|
43
|
+
"scenario": "AI adds // @ts-ignore above db.query(\"SELECT * FROM users WHERE id=\" + userId) to suppress \"Argument of type string | undefined is not assignable to parameter of type string\". The suppression hides that userId might be undefined or attacker-controlled.",
|
|
44
|
+
"fix": "Fix the underlying type error instead of suppressing it. If the suppression is hiding an \"X | undefined\" error, add a null check. If it is hiding an incompatible type, add validation. @ts-ignore should never appear near data access or authentication code."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "TS-SUPPRESS-002",
|
|
48
|
+
"name": "TypeScript strict mode disabled in tsconfig",
|
|
49
|
+
"severity": "MEDIUM",
|
|
50
|
+
"category": "Security Misconfiguration (OWASP A05)",
|
|
51
|
+
"pattern": "\"(?:strict|strictNullChecks|noImplicitAny|strictFunctionTypes)\"\\s*:\\s*false",
|
|
52
|
+
"flags": "gi",
|
|
53
|
+
"file_types": [
|
|
54
|
+
"ts"
|
|
55
|
+
],
|
|
56
|
+
"why": "Disabling strict mode (or its constituent checks) means the compiler no longer warns about implicit any types, unchecked null/undefined access, or unsafe function signatures. These are the exact checks that catch unsafe handling of user input at compile time.",
|
|
57
|
+
"scenario": "Project has \"strict\": false in tsconfig. AI generates code freely using implicit any throughout request handlers. A security review finds SQL injection in five places — all would have been type errors under strict mode, flagged before the code was ever committed.",
|
|
58
|
+
"fix": "Enable \"strict\": true in tsconfig.json and fix the resulting type errors. For existing codebases, enable checks incrementally: start with \"strictNullChecks\": true as it catches the most security-relevant issues."
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "TS-VALID-001",
|
|
62
|
+
"name": "Request body cast to typed interface without runtime validation",
|
|
63
|
+
"severity": "HIGH",
|
|
64
|
+
"category": "Injection (OWASP A03)",
|
|
65
|
+
"pattern": "(?:req|request|ctx)\\s*\\.\\s*body\\s+as\\s+(?!any\\b|unknown\\b)[A-Z]\\w+",
|
|
66
|
+
"flags": "gi",
|
|
67
|
+
"file_types": [
|
|
68
|
+
"ts",
|
|
69
|
+
"tsx"
|
|
70
|
+
],
|
|
71
|
+
"why": "TypeScript interfaces and type aliases are compile-time constructs only — they are completely erased in the emitted JavaScript. Casting req.body to an interface performs no runtime validation whatsoever. The actual request body can contain any shape, any types, any extra fields.",
|
|
72
|
+
"scenario": "AI generates: const input = req.body as CreateUserDto. The interface requires email: string, but the runtime value could be { email: { $gt: \"\" } } — a NoSQL injection payload. TypeScript is satisfied; the database is not.",
|
|
73
|
+
"fix": "Always validate at the runtime boundary using a schema library: const input = CreateUserSchema.parse(req.body). Zod, Valibot, and class-validator all provide both runtime validation and TypeScript type inference from the same schema definition."
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|