getdoorman 1.0.9 → 1.1.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/bin/doorman.js +45 -41
- package/bin/getdoorman.js +45 -41
- package/package.json +1 -1
- package/src/rules/bugs/general.js +3 -2
- package/src/rules/compliance/healthcare.js +1 -1
- package/src/rules/compliance/index.js +24 -24
- package/src/rules/data/index.js +26 -26
- package/src/rules/dependencies/index.js +5 -5
- package/src/rules/deployment/index.js +7 -7
- package/src/rules/infrastructure/index.js +28 -28
- package/src/rules/performance/index.js +1 -1
- package/src/rules/reliability/index.js +1 -1
- package/src/rules/security/ai-api.js +1 -0
- package/src/rules/security/auth.js +2 -2
- package/src/rules/security/cors.js +2 -2
- package/src/rules/security/crypto.js +2 -2
- package/src/rules/security/csharp.js +3 -3
- package/src/rules/security/csrf.js +1 -1
- package/src/rules/security/file-upload.js +1 -1
- package/src/rules/security/go.js +3 -3
- package/src/rules/security/misconfiguration.js +4 -4
- package/src/rules/security/oauth-jwt.js +5 -2
- package/src/rules/security/prototype-pollution.js +2 -2
- package/src/rules/security/rate-limiting.js +1 -1
- package/src/rules/security/rust.js +3 -3
- package/src/rules/security/ssrf.js +8 -4
- package/src/rules/security/supply-chain-advanced.js +2 -2
- package/src/rules/security/xss.js +2 -2
- package/src/simple-checks.js +257 -0
- package/src/simple-reporter.js +60 -0
package/src/rules/data/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const rules = [
|
|
|
6
6
|
{
|
|
7
7
|
id: 'DATA-001',
|
|
8
8
|
category: 'data',
|
|
9
|
-
severity: '
|
|
9
|
+
severity: 'high',
|
|
10
10
|
confidence: 'definite',
|
|
11
11
|
title: 'Weak Password Hashing',
|
|
12
12
|
check({ files }) {
|
|
@@ -162,7 +162,7 @@ const rules = [
|
|
|
162
162
|
},
|
|
163
163
|
|
|
164
164
|
// DATA-ENC-002: ECB mode for AES
|
|
165
|
-
{ id: 'DATA-ENC-002', category: 'data', severity: '
|
|
165
|
+
{ id: 'DATA-ENC-002', category: 'data', severity: 'high', confidence: 'definite', title: 'AES-ECB Mode — Insecure Encryption',
|
|
166
166
|
check({ files }) {
|
|
167
167
|
const findings = [];
|
|
168
168
|
for (const [fp, c] of files) {
|
|
@@ -180,7 +180,7 @@ const rules = [
|
|
|
180
180
|
},
|
|
181
181
|
|
|
182
182
|
// DATA-ENC-003: Hardcoded IV/nonce
|
|
183
|
-
{ id: 'DATA-ENC-003', category: 'data', severity: '
|
|
183
|
+
{ id: 'DATA-ENC-003', category: 'data', severity: 'high', confidence: 'definite', title: 'Hardcoded IV or Nonce',
|
|
184
184
|
check({ files }) {
|
|
185
185
|
const findings = [];
|
|
186
186
|
for (const [fp, c] of files) {
|
|
@@ -237,7 +237,7 @@ const rules = [
|
|
|
237
237
|
},
|
|
238
238
|
|
|
239
239
|
// DATA-PII-001: SSN pattern in code
|
|
240
|
-
{ id: 'DATA-PII-001', category: 'data', severity: '
|
|
240
|
+
{ id: 'DATA-PII-001', category: 'data', severity: 'high', confidence: 'definite', title: 'Social Security Number Handling',
|
|
241
241
|
check({ files }) {
|
|
242
242
|
const findings = [];
|
|
243
243
|
for (const [fp, c] of files) {
|
|
@@ -342,7 +342,7 @@ const rules = [
|
|
|
342
342
|
},
|
|
343
343
|
|
|
344
344
|
// DATA-STORE-001: S3 bucket with public read
|
|
345
|
-
{ id: 'DATA-STORE-001', category: 'data', severity: '
|
|
345
|
+
{ id: 'DATA-STORE-001', category: 'data', severity: 'high', confidence: 'definite', title: 'S3 Bucket With Public Read Access',
|
|
346
346
|
check({ files }) {
|
|
347
347
|
const findings = [];
|
|
348
348
|
for (const [fp, c] of files) {
|
|
@@ -499,7 +499,7 @@ const rules = [
|
|
|
499
499
|
},
|
|
500
500
|
|
|
501
501
|
// DATA-ENC-009: Weak password hashing (MD5/SHA1)
|
|
502
|
-
{ id: 'DATA-ENC-009', category: 'data', severity: '
|
|
502
|
+
{ id: 'DATA-ENC-009', category: 'data', severity: 'high', confidence: 'definite', title: 'Weak Password Hashing Algorithm',
|
|
503
503
|
check({ files }) {
|
|
504
504
|
const findings = [];
|
|
505
505
|
for (const [fp, c] of files) {
|
|
@@ -601,7 +601,7 @@ const rules = [
|
|
|
601
601
|
},
|
|
602
602
|
|
|
603
603
|
// DATA-PII-008: Credit card data in logs
|
|
604
|
-
{ id: 'DATA-PII-008', category: 'data', severity: '
|
|
604
|
+
{ id: 'DATA-PII-008', category: 'data', severity: 'high', confidence: 'definite', title: 'Payment Card Data in Logs',
|
|
605
605
|
check({ files }) {
|
|
606
606
|
const findings = [];
|
|
607
607
|
for (const [fp, c] of files) {
|
|
@@ -685,7 +685,7 @@ const rules = [
|
|
|
685
685
|
},
|
|
686
686
|
|
|
687
687
|
// DATA-STORE-005: Database connection string hardcoded
|
|
688
|
-
{ id: 'DATA-STORE-005', category: 'data', severity: '
|
|
688
|
+
{ id: 'DATA-STORE-005', category: 'data', severity: 'high', confidence: 'definite', title: 'Database Connection String Hardcoded',
|
|
689
689
|
check({ files }) {
|
|
690
690
|
const findings = [];
|
|
691
691
|
for (const [fp, c] of files) {
|
|
@@ -729,7 +729,7 @@ const rules = [
|
|
|
729
729
|
},
|
|
730
730
|
|
|
731
731
|
// DATA-STORE-008: MongoDB without authentication
|
|
732
|
-
{ id: 'DATA-STORE-008', category: 'data', severity: '
|
|
732
|
+
{ id: 'DATA-STORE-008', category: 'data', severity: 'high', confidence: 'definite', title: 'MongoDB Connection Without Authentication',
|
|
733
733
|
check({ files }) {
|
|
734
734
|
const findings = [];
|
|
735
735
|
for (const [fp, c] of files) {
|
|
@@ -763,7 +763,7 @@ const rules = [
|
|
|
763
763
|
},
|
|
764
764
|
|
|
765
765
|
// DATA-STORE-010: Path traversal risk in file operations
|
|
766
|
-
{ id: 'DATA-STORE-010', category: 'data', severity: '
|
|
766
|
+
{ id: 'DATA-STORE-010', category: 'data', severity: 'high', confidence: 'definite', title: 'Path Traversal Risk in File Operations',
|
|
767
767
|
check({ files }) {
|
|
768
768
|
const findings = [];
|
|
769
769
|
for (const [fp, c] of files) {
|
|
@@ -979,7 +979,7 @@ const rules = [
|
|
|
979
979
|
},
|
|
980
980
|
|
|
981
981
|
// DATA-PII-009: Health data stored without consent
|
|
982
|
-
{ id: 'DATA-PII-009', category: 'data', severity: '
|
|
982
|
+
{ id: 'DATA-PII-009', category: 'data', severity: 'high', confidence: 'definite', title: 'Health Data Collected Without Explicit Consent',
|
|
983
983
|
check({ files }) {
|
|
984
984
|
const findings = [];
|
|
985
985
|
for (const [fp, c] of files) {
|
|
@@ -1015,7 +1015,7 @@ const rules = [
|
|
|
1015
1015
|
},
|
|
1016
1016
|
|
|
1017
1017
|
// DATA-STORE-012: Storing secrets in database as plain text
|
|
1018
|
-
{ id: 'DATA-STORE-012', category: 'data', severity: '
|
|
1018
|
+
{ id: 'DATA-STORE-012', category: 'data', severity: 'high', confidence: 'definite', title: 'Secrets Stored as Plain Text in Database',
|
|
1019
1019
|
check({ files }) {
|
|
1020
1020
|
const findings = [];
|
|
1021
1021
|
for (const [fp, c] of files) {
|
|
@@ -1078,7 +1078,7 @@ const rules = [
|
|
|
1078
1078
|
},
|
|
1079
1079
|
|
|
1080
1080
|
// DATA-ENC-012: JWT with none algorithm
|
|
1081
|
-
{ id: 'DATA-ENC-012', category: 'data', severity: '
|
|
1081
|
+
{ id: 'DATA-ENC-012', category: 'data', severity: 'high', confidence: 'definite', title: 'JWT Algorithm Set to None',
|
|
1082
1082
|
check({ files }) {
|
|
1083
1083
|
const findings = [];
|
|
1084
1084
|
for (const [fp, c] of files) {
|
|
@@ -1095,7 +1095,7 @@ const rules = [
|
|
|
1095
1095
|
},
|
|
1096
1096
|
|
|
1097
1097
|
// DATA-STORE-013: Using eval() to parse untrusted JSON
|
|
1098
|
-
{ id: 'DATA-STORE-013', category: 'data', severity: '
|
|
1098
|
+
{ id: 'DATA-STORE-013', category: 'data', severity: 'high', confidence: 'definite', title: 'eval() Used to Parse JSON',
|
|
1099
1099
|
check({ files }) {
|
|
1100
1100
|
const findings = [];
|
|
1101
1101
|
for (const [fp, c] of files) {
|
|
@@ -1233,7 +1233,7 @@ const rules = [
|
|
|
1233
1233
|
},
|
|
1234
1234
|
},
|
|
1235
1235
|
// DATA-STORE-015: Storing passwords as password_hash with MD5/SHA1
|
|
1236
|
-
{ id: 'DATA-STORE-015', category: 'data', severity: '
|
|
1236
|
+
{ id: 'DATA-STORE-015', category: 'data', severity: 'high', confidence: 'definite', title: 'Weak Password Hashing Algorithm',
|
|
1237
1237
|
check({ files }) {
|
|
1238
1238
|
const findings = [];
|
|
1239
1239
|
for (const [fp, c] of files) {
|
|
@@ -1449,7 +1449,7 @@ const rules = [
|
|
|
1449
1449
|
},
|
|
1450
1450
|
},
|
|
1451
1451
|
// DATA-STORE-019: Using eval to deserialize data from storage
|
|
1452
|
-
{ id: 'DATA-STORE-019', category: 'data', severity: '
|
|
1452
|
+
{ id: 'DATA-STORE-019', category: 'data', severity: 'high', confidence: 'definite', title: 'eval() Used to Deserialize Stored Data',
|
|
1453
1453
|
check({ files }) {
|
|
1454
1454
|
const findings = [];
|
|
1455
1455
|
for (const [fp, c] of files) {
|
|
@@ -1468,7 +1468,7 @@ const rules = [
|
|
|
1468
1468
|
},
|
|
1469
1469
|
},
|
|
1470
1470
|
// DATA-ENC-017: Hardcoded initialization vector (IV)
|
|
1471
|
-
{ id: 'DATA-ENC-017', category: 'data', severity: '
|
|
1471
|
+
{ id: 'DATA-ENC-017', category: 'data', severity: 'high', confidence: 'definite', title: 'Hardcoded IV/Nonce for Encryption',
|
|
1472
1472
|
check({ files }) {
|
|
1473
1473
|
const findings = [];
|
|
1474
1474
|
for (const [fp, c] of files) {
|
|
@@ -1532,7 +1532,7 @@ const rules = [
|
|
|
1532
1532
|
},
|
|
1533
1533
|
},
|
|
1534
1534
|
// DATA-ENC-018: Using DES or 3DES encryption
|
|
1535
|
-
{ id: 'DATA-ENC-018', category: 'data', severity: '
|
|
1535
|
+
{ id: 'DATA-ENC-018', category: 'data', severity: 'high', confidence: 'definite', title: 'DES or 3DES Encryption Algorithm Used',
|
|
1536
1536
|
check({ files }) {
|
|
1537
1537
|
const findings = [];
|
|
1538
1538
|
for (const [fp, c] of files) {
|
|
@@ -1608,7 +1608,7 @@ const rules = [
|
|
|
1608
1608
|
},
|
|
1609
1609
|
},
|
|
1610
1610
|
// DATA-PII-014: SSN or date-of-birth stored in plain text
|
|
1611
|
-
{ id: 'DATA-PII-014', category: 'data', severity: '
|
|
1611
|
+
{ id: 'DATA-PII-014', category: 'data', severity: 'high', confidence: 'definite', title: 'SSN or Date of Birth Stored in Plain Text',
|
|
1612
1612
|
check({ files }) {
|
|
1613
1613
|
const findings = [];
|
|
1614
1614
|
for (const [fp, c] of files) {
|
|
@@ -1668,7 +1668,7 @@ rules.push({
|
|
|
1668
1668
|
|
|
1669
1669
|
// DATA-017: SSN returned in API response
|
|
1670
1670
|
rules.push({
|
|
1671
|
-
id: 'DATA-017', category: 'data', severity: '
|
|
1671
|
+
id: 'DATA-017', category: 'data', severity: 'high', confidence: 'definite', title: 'Full SSN returned in API response — data exposure',
|
|
1672
1672
|
check({ files }) {
|
|
1673
1673
|
const findings = [];
|
|
1674
1674
|
const p = /(?:res\.json|res\.send|return.*json)\s*\([^)]*(?:ssn|social_security|socialSecurity)/i;
|
|
@@ -1688,7 +1688,7 @@ rules.push({
|
|
|
1688
1688
|
|
|
1689
1689
|
// DATA-018: Full card number in API response
|
|
1690
1690
|
rules.push({
|
|
1691
|
-
id: 'DATA-018', category: 'data', severity: '
|
|
1691
|
+
id: 'DATA-018', category: 'data', severity: 'high', confidence: 'definite', title: 'Full card number returned in API response',
|
|
1692
1692
|
check({ files }) {
|
|
1693
1693
|
const findings = [];
|
|
1694
1694
|
const p = /(?:res\.json|res\.send|return.*json)\s*\([^)]*(?:cardNumber|card_number|pan|creditCard)/i;
|
|
@@ -1897,7 +1897,7 @@ rules.push({
|
|
|
1897
1897
|
|
|
1898
1898
|
// DATA-028: Password stored with reversible encryption
|
|
1899
1899
|
rules.push({
|
|
1900
|
-
id: 'DATA-028', category: 'data', severity: '
|
|
1900
|
+
id: 'DATA-028', category: 'data', severity: 'high', confidence: 'definite', title: 'Password stored with reversible encryption instead of one-way hash',
|
|
1901
1901
|
check({ files }) {
|
|
1902
1902
|
const findings = [];
|
|
1903
1903
|
const encryptPassword = /(?:encrypt|cipher|aes|des)\s*\([^)]*password|password\s*[:=]\s*(?:encrypt|cipher)/i;
|
|
@@ -1919,7 +1919,7 @@ rules.push({
|
|
|
1919
1919
|
|
|
1920
1920
|
// DATA-029: PII transmitted over HTTP
|
|
1921
1921
|
rules.push({
|
|
1922
|
-
id: 'DATA-029', category: 'data', severity: '
|
|
1922
|
+
id: 'DATA-029', category: 'data', severity: 'high', confidence: 'definite', title: 'PII data transmitted over insecure HTTP',
|
|
1923
1923
|
check({ files }) {
|
|
1924
1924
|
const findings = [];
|
|
1925
1925
|
for (const [fp, c] of files) {
|
|
@@ -2180,7 +2180,7 @@ rules.push({
|
|
|
2180
2180
|
|
|
2181
2181
|
// DATA-043: Unmasked PAN in API response
|
|
2182
2182
|
rules.push({
|
|
2183
|
-
id: 'DATA-043', category: 'data', severity: '
|
|
2183
|
+
id: 'DATA-043', category: 'data', severity: 'high', confidence: 'definite', title: 'Credit card PAN returned in API response without masking',
|
|
2184
2184
|
check({ files }) {
|
|
2185
2185
|
const findings = [];
|
|
2186
2186
|
for (const [fp, c] of files) {
|
|
@@ -2216,7 +2216,7 @@ rules.push({
|
|
|
2216
2216
|
|
|
2217
2217
|
// DATA-045: Passwords stored with reversible hashing
|
|
2218
2218
|
rules.push({
|
|
2219
|
-
id: 'DATA-045', category: 'data', severity: '
|
|
2219
|
+
id: 'DATA-045', category: 'data', severity: 'high', confidence: 'definite', title: 'Password hashed with reversible algorithm (MD5/SHA1)',
|
|
2220
2220
|
check({ files }) {
|
|
2221
2221
|
const findings = [];
|
|
2222
2222
|
for (const [fp, c] of files) {
|
|
@@ -2453,7 +2453,7 @@ rules.push({
|
|
|
2453
2453
|
|
|
2454
2454
|
// DATA-059: Biometric data without special category treatment
|
|
2455
2455
|
rules.push({
|
|
2456
|
-
id: 'DATA-059', category: 'data', severity: '
|
|
2456
|
+
id: 'DATA-059', category: 'data', severity: 'high', confidence: 'definite', title: 'Biometric data processed without GDPR special category controls',
|
|
2457
2457
|
check({ files }) {
|
|
2458
2458
|
const findings = [];
|
|
2459
2459
|
for (const [fp, c] of files) {
|
|
@@ -317,7 +317,7 @@ const rules = [
|
|
|
317
317
|
},
|
|
318
318
|
|
|
319
319
|
// DEPS-016: Package with known typosquatting name
|
|
320
|
-
{ id: 'DEPS-016', category: 'dependencies', severity: '
|
|
320
|
+
{ id: 'DEPS-016', category: 'dependencies', severity: 'high', confidence: 'definite', title: 'Suspected Typosquatting Package',
|
|
321
321
|
check({ stack }) {
|
|
322
322
|
const findings = [];
|
|
323
323
|
const typosquats = { 'crossenv': 'cross-env', 'lodash-express': 'lodash', 'expres': 'express', 'requets': 'request', 'coloers': 'colors', 'mongoos': 'mongoose', 'exress': 'express', 'node-uuid': 'uuid', 'nodemon-alternative': 'nodemon', 'react-devtools-core2': 'react-devtools-core' };
|
|
@@ -703,7 +703,7 @@ const rules = [
|
|
|
703
703
|
},
|
|
704
704
|
|
|
705
705
|
// DEPS-040: High severity npm audit findings
|
|
706
|
-
{ id: 'DEPS-040', category: 'dependencies', severity: '
|
|
706
|
+
{ id: 'DEPS-040', category: 'dependencies', severity: 'high', confidence: 'definite', title: 'Known High Severity CVE in Dependency',
|
|
707
707
|
check({ stack }) {
|
|
708
708
|
const findings = [];
|
|
709
709
|
const knownVulnerable = {
|
|
@@ -740,7 +740,7 @@ const rules = [
|
|
|
740
740
|
},
|
|
741
741
|
|
|
742
742
|
// DEPS-042: Production app without lockfile
|
|
743
|
-
{ id: 'DEPS-042', category: 'dependencies', severity: '
|
|
743
|
+
{ id: 'DEPS-042', category: 'dependencies', severity: 'high', confidence: 'definite', title: 'No Lockfile in Repository',
|
|
744
744
|
check({ files }) {
|
|
745
745
|
const findings = [];
|
|
746
746
|
const hasPackageJson = [...files.keys()].some(f => f.endsWith('package.json') && !f.includes('node_modules'));
|
|
@@ -1592,7 +1592,7 @@ rules.push({
|
|
|
1592
1592
|
|
|
1593
1593
|
// DEPS-095: vm2 package (abandoned, RCE vulnerabilities)
|
|
1594
1594
|
rules.push({
|
|
1595
|
-
id: 'DEPS-095', category: 'dependencies', severity: '
|
|
1595
|
+
id: 'DEPS-095', category: 'dependencies', severity: 'high', confidence: 'definite', title: 'vm2 package is abandoned and has multiple RCE vulnerabilities',
|
|
1596
1596
|
check({ files, stack }) {
|
|
1597
1597
|
const findings = [];
|
|
1598
1598
|
if (stack.dependencies?.['vm2']) {
|
|
@@ -1605,7 +1605,7 @@ rules.push({
|
|
|
1605
1605
|
|
|
1606
1606
|
// DEPS-096: jsonwebtoken < 9.0.0
|
|
1607
1607
|
rules.push({
|
|
1608
|
-
id: 'DEPS-096', category: 'dependencies', severity: '
|
|
1608
|
+
id: 'DEPS-096', category: 'dependencies', severity: 'high', confidence: 'definite', title: 'jsonwebtoken < 9.0.0 — CVE-2022-23529 vulnerability',
|
|
1609
1609
|
check({ files, stack }) {
|
|
1610
1610
|
const findings = [];
|
|
1611
1611
|
const ver = stack.dependencies?.['jsonwebtoken'];
|
|
@@ -152,7 +152,7 @@ const rules = [
|
|
|
152
152
|
{
|
|
153
153
|
id: 'DEPLOY-005',
|
|
154
154
|
category: 'deployment',
|
|
155
|
-
severity: '
|
|
155
|
+
severity: 'high',
|
|
156
156
|
confidence: 'definite',
|
|
157
157
|
title: 'Hardcoded Secrets in CI Config',
|
|
158
158
|
check({ files }) {
|
|
@@ -420,7 +420,7 @@ const rules = [
|
|
|
420
420
|
},
|
|
421
421
|
|
|
422
422
|
// DEPLOY-SEC-002: pull_request_target with checkout
|
|
423
|
-
{ id: 'DEPLOY-SEC-002', category: 'deployment', severity: '
|
|
423
|
+
{ id: 'DEPLOY-SEC-002', category: 'deployment', severity: 'high', confidence: 'definite', title: 'pull_request_target With Untrusted Code Checkout',
|
|
424
424
|
check({ files }) {
|
|
425
425
|
const findings = [];
|
|
426
426
|
for (const [fp, c] of files) {
|
|
@@ -436,7 +436,7 @@ const rules = [
|
|
|
436
436
|
},
|
|
437
437
|
|
|
438
438
|
// DEPLOY-SEC-003: Workflow injection via PR title/body
|
|
439
|
-
{ id: 'DEPLOY-SEC-003', category: 'deployment', severity: '
|
|
439
|
+
{ id: 'DEPLOY-SEC-003', category: 'deployment', severity: 'high', confidence: 'definite', title: 'Workflow Injection via PR Title/Body',
|
|
440
440
|
check({ files }) {
|
|
441
441
|
const findings = [];
|
|
442
442
|
for (const [fp, c] of files) {
|
|
@@ -453,7 +453,7 @@ const rules = [
|
|
|
453
453
|
},
|
|
454
454
|
|
|
455
455
|
// DEPLOY-SEC-004: Secrets in CI logs
|
|
456
|
-
{ id: 'DEPLOY-SEC-004', category: 'deployment', severity: '
|
|
456
|
+
{ id: 'DEPLOY-SEC-004', category: 'deployment', severity: 'high', confidence: 'definite', title: 'Secrets Potentially Exposed in CI Logs',
|
|
457
457
|
check({ files }) {
|
|
458
458
|
const findings = [];
|
|
459
459
|
for (const [fp, c] of files) {
|
|
@@ -566,7 +566,7 @@ const rules = [
|
|
|
566
566
|
},
|
|
567
567
|
|
|
568
568
|
// DEPLOY-ENV-001: Shared database between environments
|
|
569
|
-
{ id: 'DEPLOY-ENV-001', category: 'deployment', severity: '
|
|
569
|
+
{ id: 'DEPLOY-ENV-001', category: 'deployment', severity: 'high', confidence: 'definite', title: 'Shared Database Between Environments',
|
|
570
570
|
check({ files }) {
|
|
571
571
|
const findings = [];
|
|
572
572
|
const prodDbUrl = [...files.values()].some(c => c.match(/DATABASE_URL.*prod|production.*DATABASE_URL/i));
|
|
@@ -1206,7 +1206,7 @@ const rules = [
|
|
|
1206
1206
|
},
|
|
1207
1207
|
},
|
|
1208
1208
|
// DEPLOY-PROC-033: Using curl | sh pattern in CI
|
|
1209
|
-
{ id: 'DEPLOY-PROC-033', category: 'deployment', severity: '
|
|
1209
|
+
{ id: 'DEPLOY-PROC-033', category: 'deployment', severity: 'high', confidence: 'definite', title: 'Downloading and Executing Scripts via curl | sh',
|
|
1210
1210
|
check({ files }) {
|
|
1211
1211
|
const findings = [];
|
|
1212
1212
|
for (const [fp, c] of files) {
|
|
@@ -1548,7 +1548,7 @@ rules.push({
|
|
|
1548
1548
|
|
|
1549
1549
|
// DEPLOY-055: Environment variables hardcoded in CI config
|
|
1550
1550
|
rules.push({
|
|
1551
|
-
id: 'DEPLOY-055', category: 'deployment', severity: '
|
|
1551
|
+
id: 'DEPLOY-055', category: 'deployment', severity: 'high', confidence: 'definite', title: 'Hardcoded secret in CI/CD configuration file',
|
|
1552
1552
|
check({ files }) {
|
|
1553
1553
|
const findings = [];
|
|
1554
1554
|
for (const [fp, c] of files) {
|
|
@@ -183,7 +183,7 @@ const rules = [
|
|
|
183
183
|
{
|
|
184
184
|
id: 'INFRA-007',
|
|
185
185
|
category: 'infrastructure',
|
|
186
|
-
severity: '
|
|
186
|
+
severity: 'high',
|
|
187
187
|
confidence: 'definite',
|
|
188
188
|
title: 'Sensitive Host Path Mounted in Docker Compose',
|
|
189
189
|
check({ files }) {
|
|
@@ -245,7 +245,7 @@ const rules = [
|
|
|
245
245
|
{
|
|
246
246
|
id: 'INFRA-009',
|
|
247
247
|
category: 'infrastructure',
|
|
248
|
-
severity: '
|
|
248
|
+
severity: 'high',
|
|
249
249
|
confidence: 'definite',
|
|
250
250
|
title: 'Privileged Mode in Docker Compose',
|
|
251
251
|
check({ files }) {
|
|
@@ -296,7 +296,7 @@ const rules = [
|
|
|
296
296
|
},
|
|
297
297
|
|
|
298
298
|
// INFRA-DOCKER-001: Secrets in ENV instruction
|
|
299
|
-
{ id: 'INFRA-DOCKER-001', category: 'infrastructure', severity: '
|
|
299
|
+
{ id: 'INFRA-DOCKER-001', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Secrets in Dockerfile ENV Instruction',
|
|
300
300
|
check({ files }) {
|
|
301
301
|
const findings = [];
|
|
302
302
|
for (const [fp, c] of files) {
|
|
@@ -315,7 +315,7 @@ const rules = [
|
|
|
315
315
|
},
|
|
316
316
|
|
|
317
317
|
// INFRA-DOCKER-002: curl|bash pattern
|
|
318
|
-
{ id: 'INFRA-DOCKER-002', category: 'infrastructure', severity: '
|
|
318
|
+
{ id: 'INFRA-DOCKER-002', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Remote Script Execution in Dockerfile',
|
|
319
319
|
check({ files }) {
|
|
320
320
|
const findings = [];
|
|
321
321
|
for (const [fp, c] of files) {
|
|
@@ -398,7 +398,7 @@ const rules = [
|
|
|
398
398
|
},
|
|
399
399
|
|
|
400
400
|
// INFRA-K8S-003: Secrets in ConfigMap
|
|
401
|
-
{ id: 'INFRA-K8S-003', category: 'infrastructure', severity: '
|
|
401
|
+
{ id: 'INFRA-K8S-003', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Secrets in Kubernetes ConfigMap',
|
|
402
402
|
check({ files }) {
|
|
403
403
|
const findings = [];
|
|
404
404
|
for (const [fp, c] of files) {
|
|
@@ -428,7 +428,7 @@ const rules = [
|
|
|
428
428
|
},
|
|
429
429
|
|
|
430
430
|
// INFRA-CLOUD-001: SSH open to all IPs
|
|
431
|
-
{ id: 'INFRA-CLOUD-001', category: 'infrastructure', severity: '
|
|
431
|
+
{ id: 'INFRA-CLOUD-001', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'SSH Port Open to Internet',
|
|
432
432
|
check({ files }) {
|
|
433
433
|
const findings = [];
|
|
434
434
|
for (const [fp, c] of files) {
|
|
@@ -444,7 +444,7 @@ const rules = [
|
|
|
444
444
|
},
|
|
445
445
|
|
|
446
446
|
// INFRA-CLOUD-002: Database publicly accessible
|
|
447
|
-
{ id: 'INFRA-CLOUD-002', category: 'infrastructure', severity: '
|
|
447
|
+
{ id: 'INFRA-CLOUD-002', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Database Publicly Accessible',
|
|
448
448
|
check({ files }) {
|
|
449
449
|
const findings = [];
|
|
450
450
|
for (const [fp, c] of files) {
|
|
@@ -460,7 +460,7 @@ const rules = [
|
|
|
460
460
|
},
|
|
461
461
|
|
|
462
462
|
// INFRA-CLOUD-003: Hardcoded creds in Terraform
|
|
463
|
-
{ id: 'INFRA-CLOUD-003', category: 'infrastructure', severity: '
|
|
463
|
+
{ id: 'INFRA-CLOUD-003', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Hardcoded Credentials in Terraform',
|
|
464
464
|
check({ files }) {
|
|
465
465
|
const findings = [];
|
|
466
466
|
for (const [fp, c] of files) {
|
|
@@ -493,7 +493,7 @@ const rules = [
|
|
|
493
493
|
},
|
|
494
494
|
|
|
495
495
|
// INFRA-CLOUD-005: IAM admin policy
|
|
496
|
-
{ id: 'INFRA-CLOUD-005', category: 'infrastructure', severity: '
|
|
496
|
+
{ id: 'INFRA-CLOUD-005', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'IAM Policy Grants Full Admin Access',
|
|
497
497
|
check({ files }) {
|
|
498
498
|
const findings = [];
|
|
499
499
|
for (const [fp, c] of files) {
|
|
@@ -572,7 +572,7 @@ const rules = [
|
|
|
572
572
|
},
|
|
573
573
|
|
|
574
574
|
// INFRA-K8S-010: Privileged container
|
|
575
|
-
{ id: 'INFRA-K8S-010', category: 'infrastructure', severity: '
|
|
575
|
+
{ id: 'INFRA-K8S-010', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Kubernetes Privileged Container',
|
|
576
576
|
check({ files }) {
|
|
577
577
|
const findings = [];
|
|
578
578
|
for (const [fp, c] of files) {
|
|
@@ -837,7 +837,7 @@ const rules = [
|
|
|
837
837
|
},
|
|
838
838
|
|
|
839
839
|
// INFRA-CLOUD-011: RDS instance publicly accessible
|
|
840
|
-
{ id: 'INFRA-CLOUD-011', category: 'infrastructure', severity: '
|
|
840
|
+
{ id: 'INFRA-CLOUD-011', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'RDS Instance Publicly Accessible',
|
|
841
841
|
check({ files }) {
|
|
842
842
|
const findings = [];
|
|
843
843
|
for (const [fp, c] of files) {
|
|
@@ -1274,7 +1274,7 @@ const rules = [
|
|
|
1274
1274
|
},
|
|
1275
1275
|
},
|
|
1276
1276
|
// INFRA-TF-009: terraform.tfvars with secrets committed
|
|
1277
|
-
{ id: 'INFRA-TF-009', category: 'infrastructure', severity: '
|
|
1277
|
+
{ id: 'INFRA-TF-009', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Terraform Variables File With Secrets',
|
|
1278
1278
|
check({ files }) {
|
|
1279
1279
|
const findings = [];
|
|
1280
1280
|
for (const [fp, c] of files) {
|
|
@@ -1290,7 +1290,7 @@ const rules = [
|
|
|
1290
1290
|
},
|
|
1291
1291
|
},
|
|
1292
1292
|
// INFRA-NET-004: Security group ingress from 0.0.0.0 on database port
|
|
1293
|
-
{ id: 'INFRA-NET-004', category: 'infrastructure', severity: '
|
|
1293
|
+
{ id: 'INFRA-NET-004', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Database Port Open to Internet',
|
|
1294
1294
|
check({ files }) {
|
|
1295
1295
|
const findings = [];
|
|
1296
1296
|
for (const [fp, c] of files) {
|
|
@@ -1516,7 +1516,7 @@ const rules = [
|
|
|
1516
1516
|
},
|
|
1517
1517
|
},
|
|
1518
1518
|
// INFRA-NET-006: Security group allows unrestricted database port
|
|
1519
|
-
{ id: 'INFRA-NET-006', category: 'infrastructure', severity: '
|
|
1519
|
+
{ id: 'INFRA-NET-006', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Security Group Exposes Database Port Publicly',
|
|
1520
1520
|
check({ files }) {
|
|
1521
1521
|
const findings = [];
|
|
1522
1522
|
for (const [fp, c] of files) {
|
|
@@ -1595,7 +1595,7 @@ const rules = [
|
|
|
1595
1595
|
},
|
|
1596
1596
|
},
|
|
1597
1597
|
// INFRA-CLOUD-033: IAM role with AdministratorAccess
|
|
1598
|
-
{ id: 'INFRA-CLOUD-033', category: 'infrastructure', severity: '
|
|
1598
|
+
{ id: 'INFRA-CLOUD-033', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'IAM Role with AdministratorAccess Policy',
|
|
1599
1599
|
check({ files }) {
|
|
1600
1600
|
const findings = [];
|
|
1601
1601
|
for (const [fp, c] of files) {
|
|
@@ -1758,7 +1758,7 @@ rules.push({
|
|
|
1758
1758
|
|
|
1759
1759
|
// INFRA-K8S-028: Privileged container
|
|
1760
1760
|
rules.push({
|
|
1761
|
-
id: 'INFRA-K8S-028', category: 'infrastructure', severity: '
|
|
1761
|
+
id: 'INFRA-K8S-028', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Kubernetes container running as privileged',
|
|
1762
1762
|
check({ files }) {
|
|
1763
1763
|
const findings = [];
|
|
1764
1764
|
for (const [fp, c] of files) {
|
|
@@ -1832,7 +1832,7 @@ rules.push({
|
|
|
1832
1832
|
|
|
1833
1833
|
// INFRA-DC-027: Secrets in Docker Compose environment
|
|
1834
1834
|
rules.push({
|
|
1835
|
-
id: 'INFRA-DC-027', category: 'infrastructure', severity: '
|
|
1835
|
+
id: 'INFRA-DC-027', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Hardcoded secret in Docker Compose environment section',
|
|
1836
1836
|
check({ files }) {
|
|
1837
1837
|
const findings = [];
|
|
1838
1838
|
for (const [fp, c] of files) {
|
|
@@ -1897,7 +1897,7 @@ rules.push({
|
|
|
1897
1897
|
|
|
1898
1898
|
// INFRA-TF-028: Open security group (0.0.0.0/0 ingress)
|
|
1899
1899
|
rules.push({
|
|
1900
|
-
id: 'INFRA-TF-028', category: 'infrastructure', severity: '
|
|
1900
|
+
id: 'INFRA-TF-028', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Terraform security group with unrestricted ingress (0.0.0.0/0)',
|
|
1901
1901
|
check({ files }) {
|
|
1902
1902
|
const findings = [];
|
|
1903
1903
|
for (const [fp, c] of files) {
|
|
@@ -1962,7 +1962,7 @@ rules.push({
|
|
|
1962
1962
|
|
|
1963
1963
|
// INFRA-K8S-033: Sensitive hostPath mount
|
|
1964
1964
|
rules.push({
|
|
1965
|
-
id: 'INFRA-K8S-033', category: 'infrastructure', severity: '
|
|
1965
|
+
id: 'INFRA-K8S-033', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Kubernetes pod mounts sensitive host path',
|
|
1966
1966
|
check({ files }) {
|
|
1967
1967
|
const findings = [];
|
|
1968
1968
|
for (const [fp, c] of files) {
|
|
@@ -2101,7 +2101,7 @@ rules.push({
|
|
|
2101
2101
|
|
|
2102
2102
|
// INFRA-TF-036: RDS not in private subnet
|
|
2103
2103
|
rules.push({
|
|
2104
|
-
id: 'INFRA-TF-036', category: 'infrastructure', severity: '
|
|
2104
|
+
id: 'INFRA-TF-036', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'RDS instance publicly accessible',
|
|
2105
2105
|
check({ files }) {
|
|
2106
2106
|
const findings = [];
|
|
2107
2107
|
for (const [fp, c] of files) {
|
|
@@ -2178,7 +2178,7 @@ rules.push({
|
|
|
2178
2178
|
|
|
2179
2179
|
// INFRA-K8S-037: ConfigMap used for secrets
|
|
2180
2180
|
rules.push({
|
|
2181
|
-
id: 'INFRA-K8S-037', category: 'infrastructure', severity: '
|
|
2181
|
+
id: 'INFRA-K8S-037', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Kubernetes ConfigMap storing secret values',
|
|
2182
2182
|
check({ files }) {
|
|
2183
2183
|
const findings = [];
|
|
2184
2184
|
for (const [fp, c] of files) {
|
|
@@ -2212,7 +2212,7 @@ rules.push({
|
|
|
2212
2212
|
|
|
2213
2213
|
// INFRA-TF-040: AWS account root user access key
|
|
2214
2214
|
rules.push({
|
|
2215
|
-
id: 'INFRA-TF-040', category: 'infrastructure', severity: '
|
|
2215
|
+
id: 'INFRA-TF-040', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'AWS root account access key configured',
|
|
2216
2216
|
check({ files }) {
|
|
2217
2217
|
const findings = [];
|
|
2218
2218
|
for (const [fp, c] of files) {
|
|
@@ -2307,7 +2307,7 @@ rules.push({
|
|
|
2307
2307
|
|
|
2308
2308
|
// INFRA-K8S-040: Container with all capabilities
|
|
2309
2309
|
rules.push({
|
|
2310
|
-
id: 'INFRA-K8S-040', category: 'infrastructure', severity: '
|
|
2310
|
+
id: 'INFRA-K8S-040', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Kubernetes container with all capabilities added',
|
|
2311
2311
|
check({ files }) {
|
|
2312
2312
|
const findings = [];
|
|
2313
2313
|
for (const [fp, c] of files) {
|
|
@@ -2474,7 +2474,7 @@ rules.push({
|
|
|
2474
2474
|
|
|
2475
2475
|
// INFRA-K8S-044: Service account with cluster-admin role
|
|
2476
2476
|
rules.push({
|
|
2477
|
-
id: 'INFRA-K8S-044', category: 'infrastructure', severity: '
|
|
2477
|
+
id: 'INFRA-K8S-044', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Kubernetes service account bound to cluster-admin role',
|
|
2478
2478
|
check({ files }) {
|
|
2479
2479
|
const findings = [];
|
|
2480
2480
|
for (const [fp, c] of files) {
|
|
@@ -2576,7 +2576,7 @@ rules.push({
|
|
|
2576
2576
|
|
|
2577
2577
|
// INFRA-057: Kubernetes container running as privileged
|
|
2578
2578
|
rules.push({
|
|
2579
|
-
id: 'INFRA-057', category: 'infrastructure', severity: '
|
|
2579
|
+
id: 'INFRA-057', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Kubernetes privileged container',
|
|
2580
2580
|
check({ files }) {
|
|
2581
2581
|
const findings = [];
|
|
2582
2582
|
for (const [fp, c] of files) {
|
|
@@ -2692,7 +2692,7 @@ rules.push({
|
|
|
2692
2692
|
|
|
2693
2693
|
// INFRA-066: Database publicly accessible
|
|
2694
2694
|
rules.push({
|
|
2695
|
-
id: 'INFRA-066', category: 'infrastructure', severity: '
|
|
2695
|
+
id: 'INFRA-066', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'RDS instance publicly accessible',
|
|
2696
2696
|
check({ files }) {
|
|
2697
2697
|
const findings = [];
|
|
2698
2698
|
for (const [fp, c] of files) {
|
|
@@ -2717,7 +2717,7 @@ rules.push({
|
|
|
2717
2717
|
|
|
2718
2718
|
// INFRA-068: Security group with 0.0.0.0/0 SSH
|
|
2719
2719
|
rules.push({
|
|
2720
|
-
id: 'INFRA-068', category: 'infrastructure', severity: '
|
|
2720
|
+
id: 'INFRA-068', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'SSH open to the world',
|
|
2721
2721
|
check({ files }) {
|
|
2722
2722
|
const findings = [];
|
|
2723
2723
|
for (const [fp, c] of files) {
|
|
@@ -2857,7 +2857,7 @@ rules.push({
|
|
|
2857
2857
|
|
|
2858
2858
|
// INFRA-079: Docker socket mounted
|
|
2859
2859
|
rules.push({
|
|
2860
|
-
id: 'INFRA-079', category: 'infrastructure', severity: '
|
|
2860
|
+
id: 'INFRA-079', category: 'infrastructure', severity: 'high', confidence: 'definite', title: 'Docker socket mounted in container',
|
|
2861
2861
|
check({ files }) {
|
|
2862
2862
|
const findings = [];
|
|
2863
2863
|
for (const [fp, c] of files) {
|
|
@@ -2350,7 +2350,7 @@ const rules = [
|
|
|
2350
2350
|
},
|
|
2351
2351
|
},
|
|
2352
2352
|
// PERF-CACHE-010: Redis KEYS command in production
|
|
2353
|
-
{ id: 'PERF-CACHE-010', category: 'performance', severity: '
|
|
2353
|
+
{ id: 'PERF-CACHE-010', category: 'performance', severity: 'high', confidence: 'definite', title: 'Redis KEYS Command Used in Production',
|
|
2354
2354
|
check({ files }) {
|
|
2355
2355
|
const findings = [];
|
|
2356
2356
|
for (const [fp, c] of files) {
|
|
@@ -329,6 +329,7 @@ const rules = [
|
|
|
329
329
|
for (const [path, content] of files) {
|
|
330
330
|
if (SKIP_PATH.test(path)) continue;
|
|
331
331
|
if (!path.endsWith('.env') && !path.match(/\.env\.\w+$/)) continue;
|
|
332
|
+
if (/\.env\.(example|sample|template|defaults|dev\.example)$/i.test(path)) continue;
|
|
332
333
|
findings.push(...scanLines(content, envKeyPattern, path, this));
|
|
333
334
|
}
|
|
334
335
|
return findings;
|
|
@@ -309,7 +309,7 @@ const rules = [
|
|
|
309
309
|
{
|
|
310
310
|
id: 'SEC-AUTH-007',
|
|
311
311
|
category: 'security',
|
|
312
|
-
severity: '
|
|
312
|
+
severity: 'high',
|
|
313
313
|
confidence: 'suggestion',
|
|
314
314
|
title: 'API route missing authentication middleware',
|
|
315
315
|
check({ files }) {
|
|
@@ -366,7 +366,7 @@ const rules = [
|
|
|
366
366
|
{
|
|
367
367
|
id: 'SEC-AUTH-008',
|
|
368
368
|
category: 'security',
|
|
369
|
-
severity: '
|
|
369
|
+
severity: 'high',
|
|
370
370
|
confidence: 'suggestion',
|
|
371
371
|
title: 'Admin route missing role/permission check',
|
|
372
372
|
check({ files }) {
|
|
@@ -32,7 +32,7 @@ const rules = [
|
|
|
32
32
|
|
|
33
33
|
// SEC-CORS-002
|
|
34
34
|
{
|
|
35
|
-
id: 'SEC-CORS-002', category: 'security', severity: '
|
|
35
|
+
id: 'SEC-CORS-002', category: 'security', severity: 'high', confidence: 'definite',
|
|
36
36
|
title: 'CORS reflects Origin header',
|
|
37
37
|
check({ files }) {
|
|
38
38
|
const findings = [];
|
|
@@ -56,7 +56,7 @@ const rules = [
|
|
|
56
56
|
|
|
57
57
|
// SEC-CORS-003
|
|
58
58
|
{
|
|
59
|
-
id: 'SEC-CORS-003', category: 'security', severity: '
|
|
59
|
+
id: 'SEC-CORS-003', category: 'security', severity: 'high', confidence: 'definite',
|
|
60
60
|
title: 'CORS allows credentials with wildcard origin',
|
|
61
61
|
check({ files }) {
|
|
62
62
|
const findings = [];
|