getdoorman 1.0.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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +181 -0
  3. package/bin/doorman.js +444 -0
  4. package/package.json +74 -0
  5. package/src/ai-fixer.js +559 -0
  6. package/src/ast-scanner.js +434 -0
  7. package/src/auth.js +149 -0
  8. package/src/baseline.js +48 -0
  9. package/src/compliance.js +539 -0
  10. package/src/config.js +466 -0
  11. package/src/custom-rules.js +32 -0
  12. package/src/dashboard.js +202 -0
  13. package/src/detector.js +142 -0
  14. package/src/fix-engine.js +48 -0
  15. package/src/fix-registry-extra.js +95 -0
  16. package/src/fix-registry-go-rust.js +77 -0
  17. package/src/fix-registry-java-csharp.js +77 -0
  18. package/src/fix-registry-js.js +99 -0
  19. package/src/fix-registry-mcp-ai.js +57 -0
  20. package/src/fix-registry-python.js +87 -0
  21. package/src/fixer-ruby-php.js +608 -0
  22. package/src/fixer.js +2113 -0
  23. package/src/hooks.js +115 -0
  24. package/src/ignore.js +176 -0
  25. package/src/index.js +384 -0
  26. package/src/metrics.js +126 -0
  27. package/src/monorepo.js +65 -0
  28. package/src/presets.js +54 -0
  29. package/src/reporter.js +975 -0
  30. package/src/rule-worker.js +36 -0
  31. package/src/rules/ast-rules.js +756 -0
  32. package/src/rules/bugs/accessibility.js +235 -0
  33. package/src/rules/bugs/ai-codegen-fixable.js +172 -0
  34. package/src/rules/bugs/ai-codegen.js +365 -0
  35. package/src/rules/bugs/code-smell-bugs.js +247 -0
  36. package/src/rules/bugs/crypto-bugs.js +195 -0
  37. package/src/rules/bugs/docker-bugs.js +158 -0
  38. package/src/rules/bugs/general.js +361 -0
  39. package/src/rules/bugs/go-bugs.js +279 -0
  40. package/src/rules/bugs/index.js +73 -0
  41. package/src/rules/bugs/js-api.js +257 -0
  42. package/src/rules/bugs/js-array-object.js +210 -0
  43. package/src/rules/bugs/js-async-fixable.js +223 -0
  44. package/src/rules/bugs/js-async.js +211 -0
  45. package/src/rules/bugs/js-closure-scope.js +182 -0
  46. package/src/rules/bugs/js-database.js +203 -0
  47. package/src/rules/bugs/js-error-handling.js +148 -0
  48. package/src/rules/bugs/js-logic.js +261 -0
  49. package/src/rules/bugs/js-memory.js +214 -0
  50. package/src/rules/bugs/js-node.js +361 -0
  51. package/src/rules/bugs/js-react.js +373 -0
  52. package/src/rules/bugs/js-regex.js +200 -0
  53. package/src/rules/bugs/js-state.js +272 -0
  54. package/src/rules/bugs/js-type-coercion.js +318 -0
  55. package/src/rules/bugs/nextjs-bugs.js +242 -0
  56. package/src/rules/bugs/nextjs-fixable.js +120 -0
  57. package/src/rules/bugs/node-fixable.js +178 -0
  58. package/src/rules/bugs/python-advanced.js +245 -0
  59. package/src/rules/bugs/python-fixable.js +98 -0
  60. package/src/rules/bugs/python.js +284 -0
  61. package/src/rules/bugs/react-fixable.js +207 -0
  62. package/src/rules/bugs/ruby-bugs.js +182 -0
  63. package/src/rules/bugs/shell-bugs.js +181 -0
  64. package/src/rules/bugs/silent-failures.js +261 -0
  65. package/src/rules/bugs/ts-bugs.js +235 -0
  66. package/src/rules/bugs/unused-vars.js +65 -0
  67. package/src/rules/compliance/accessibility-ext.js +468 -0
  68. package/src/rules/compliance/education.js +322 -0
  69. package/src/rules/compliance/financial.js +421 -0
  70. package/src/rules/compliance/frameworks.js +507 -0
  71. package/src/rules/compliance/healthcare.js +520 -0
  72. package/src/rules/compliance/index.js +2714 -0
  73. package/src/rules/compliance/regional-eu.js +480 -0
  74. package/src/rules/compliance/regional-international.js +903 -0
  75. package/src/rules/cost/index.js +1993 -0
  76. package/src/rules/data/index.js +2503 -0
  77. package/src/rules/dependencies/index.js +1684 -0
  78. package/src/rules/deployment/index.js +2050 -0
  79. package/src/rules/index.js +71 -0
  80. package/src/rules/infrastructure/index.js +3048 -0
  81. package/src/rules/performance/index.js +3455 -0
  82. package/src/rules/quality/index.js +3175 -0
  83. package/src/rules/reliability/index.js +3040 -0
  84. package/src/rules/scope-rules.js +815 -0
  85. package/src/rules/security/ai-api.js +1177 -0
  86. package/src/rules/security/auth.js +1328 -0
  87. package/src/rules/security/cors.js +127 -0
  88. package/src/rules/security/crypto.js +527 -0
  89. package/src/rules/security/csharp.js +862 -0
  90. package/src/rules/security/csrf.js +193 -0
  91. package/src/rules/security/dart.js +835 -0
  92. package/src/rules/security/deserialization.js +291 -0
  93. package/src/rules/security/file-upload.js +187 -0
  94. package/src/rules/security/go.js +850 -0
  95. package/src/rules/security/headers.js +235 -0
  96. package/src/rules/security/index.js +65 -0
  97. package/src/rules/security/injection.js +1639 -0
  98. package/src/rules/security/mcp-server.js +71 -0
  99. package/src/rules/security/misconfiguration.js +660 -0
  100. package/src/rules/security/oauth-jwt.js +329 -0
  101. package/src/rules/security/path-traversal.js +295 -0
  102. package/src/rules/security/php.js +1054 -0
  103. package/src/rules/security/prototype-pollution.js +283 -0
  104. package/src/rules/security/rate-limiting.js +208 -0
  105. package/src/rules/security/ruby.js +1061 -0
  106. package/src/rules/security/rust.js +693 -0
  107. package/src/rules/security/secrets.js +747 -0
  108. package/src/rules/security/shell.js +647 -0
  109. package/src/rules/security/ssrf.js +298 -0
  110. package/src/rules/security/supply-chain-advanced.js +393 -0
  111. package/src/rules/security/supply-chain.js +734 -0
  112. package/src/rules/security/swift.js +835 -0
  113. package/src/rules/security/taint.js +27 -0
  114. package/src/rules/security/xss.js +520 -0
  115. package/src/scan-cache.js +71 -0
  116. package/src/scanner.js +710 -0
  117. package/src/scope-analyzer.js +685 -0
  118. package/src/share.js +88 -0
  119. package/src/taint.js +300 -0
  120. package/src/telemetry.js +183 -0
  121. package/src/tracer.js +190 -0
  122. package/src/upload.js +35 -0
  123. package/src/worker.js +31 -0
package/src/metrics.js ADDED
@@ -0,0 +1,126 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ const METRICS_DIR = '.doorman';
5
+ const METRICS_FILE = 'metrics.json';
6
+
7
+ function getMetricsPath(targetPath) {
8
+ return join(targetPath, METRICS_DIR, METRICS_FILE);
9
+ }
10
+
11
+ function ensureDir(targetPath) {
12
+ const dir = join(targetPath, METRICS_DIR);
13
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
14
+ }
15
+
16
+ export function loadMetrics(targetPath) {
17
+ const path = getMetricsPath(targetPath);
18
+ if (!existsSync(path)) {
19
+ return { scans: [], fixes: [], scores: [] };
20
+ }
21
+ return JSON.parse(readFileSync(path, 'utf-8'));
22
+ }
23
+
24
+ export function recordScan(targetPath, { score, findings, stack }) {
25
+ ensureDir(targetPath);
26
+ const metrics = loadMetrics(targetPath);
27
+
28
+ const scan = {
29
+ timestamp: new Date().toISOString(),
30
+ score,
31
+ totalFindings: findings.length,
32
+ bySeverity: {
33
+ critical: findings.filter(f => f.severity === 'critical').length,
34
+ high: findings.filter(f => f.severity === 'high').length,
35
+ medium: findings.filter(f => f.severity === 'medium').length,
36
+ low: findings.filter(f => f.severity === 'low').length,
37
+ },
38
+ byCategory: {},
39
+ fixable: findings.filter(f => f.fix).length,
40
+ stack: stack.framework || stack.language || 'unknown',
41
+ };
42
+
43
+ // Count by category
44
+ for (const f of findings) {
45
+ scan.byCategory[f.category] = (scan.byCategory[f.category] || 0) + 1;
46
+ }
47
+
48
+ metrics.scans.push(scan);
49
+
50
+ // Keep last 100 scans
51
+ if (metrics.scans.length > 100) metrics.scans = metrics.scans.slice(-100);
52
+
53
+ // Track score history
54
+ metrics.scores.push({ timestamp: scan.timestamp, score });
55
+ if (metrics.scores.length > 100) metrics.scores = metrics.scores.slice(-100);
56
+
57
+ writeFileSync(getMetricsPath(targetPath), JSON.stringify(metrics, null, 2));
58
+ return scan;
59
+ }
60
+
61
+ export function recordFix(targetPath, { applied, offered, accepted }) {
62
+ ensureDir(targetPath);
63
+ const metrics = loadMetrics(targetPath);
64
+
65
+ metrics.fixes.push({
66
+ timestamp: new Date().toISOString(),
67
+ offered,
68
+ applied,
69
+ accepted,
70
+ rate: offered > 0 ? Math.round((accepted / offered) * 100) : 0,
71
+ });
72
+
73
+ if (metrics.fixes.length > 100) metrics.fixes = metrics.fixes.slice(-100);
74
+
75
+ writeFileSync(getMetricsPath(targetPath), JSON.stringify(metrics, null, 2));
76
+ }
77
+
78
+ export function getScoreHistory(targetPath) {
79
+ const metrics = loadMetrics(targetPath);
80
+ return metrics.scores;
81
+ }
82
+
83
+ export function getFixAcceptanceRate(targetPath) {
84
+ const metrics = loadMetrics(targetPath);
85
+ if (metrics.fixes.length === 0) return null;
86
+
87
+ const totalOffered = metrics.fixes.reduce((sum, f) => sum + f.offered, 0);
88
+ const totalAccepted = metrics.fixes.reduce((sum, f) => sum + f.accepted, 0);
89
+
90
+ return {
91
+ totalOffered,
92
+ totalAccepted,
93
+ rate: totalOffered > 0 ? Math.round((totalAccepted / totalOffered) * 100) : 0,
94
+ history: metrics.fixes,
95
+ };
96
+ }
97
+
98
+ export function printMetricsSummary(targetPath) {
99
+ const metrics = loadMetrics(targetPath);
100
+
101
+ if (metrics.scans.length === 0) {
102
+ console.log(' No scan history yet. Run `doorman check` first.');
103
+ return;
104
+ }
105
+
106
+ const latest = metrics.scans[metrics.scans.length - 1];
107
+ const first = metrics.scans[0];
108
+ const scoreDelta = latest.score - first.score;
109
+
110
+ console.log('');
111
+ console.log(' Doorman Metrics');
112
+ console.log(' ═══════════════════');
113
+ console.log(` Total scans: ${metrics.scans.length}`);
114
+ console.log(` Current score: ${latest.score}/100`);
115
+ if (metrics.scans.length > 1) {
116
+ console.log(` Score trend: ${scoreDelta >= 0 ? '+' : ''}${scoreDelta} since first scan`);
117
+ }
118
+ console.log(` Total findings: ${latest.totalFindings}`);
119
+ console.log(` Auto-fixable: ${latest.fixable}`);
120
+
121
+ if (metrics.fixes.length > 0) {
122
+ const fixRate = getFixAcceptanceRate(targetPath);
123
+ console.log(` Fix acceptance rate: ${fixRate.rate}%`);
124
+ }
125
+ console.log('');
126
+ }
@@ -0,0 +1,65 @@
1
+ // Monorepo detection and workspace root discovery
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import { join, resolve } from 'path';
4
+
5
+ /**
6
+ * Detect monorepo workspaces and return workspace roots.
7
+ * Supports: npm workspaces, yarn, pnpm, turborepo, nx, lerna
8
+ */
9
+ export function detectWorkspaces(targetPath) {
10
+ const workspaces = [];
11
+
12
+ // npm/yarn workspaces
13
+ const pkgPath = join(targetPath, 'package.json');
14
+ if (existsSync(pkgPath)) {
15
+ try {
16
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
17
+ const ws = pkg.workspaces;
18
+ if (Array.isArray(ws)) {
19
+ workspaces.push(...ws);
20
+ } else if (ws && Array.isArray(ws.packages)) {
21
+ workspaces.push(...ws.packages);
22
+ }
23
+ } catch {}
24
+ }
25
+
26
+ // pnpm workspaces
27
+ const pnpmWs = join(targetPath, 'pnpm-workspace.yaml');
28
+ if (existsSync(pnpmWs)) {
29
+ try {
30
+ const content = readFileSync(pnpmWs, 'utf-8');
31
+ const matches = content.match(/- ['"]?([^'":\n]+)['"]?/g);
32
+ if (matches) {
33
+ workspaces.push(...matches.map(m => m.replace(/^- ['"]?/, '').replace(/['"]?$/, '')));
34
+ }
35
+ } catch {}
36
+ }
37
+
38
+ // turborepo
39
+ if (existsSync(join(targetPath, 'turbo.json'))) {
40
+ // turbo uses package.json workspaces — already handled above
41
+ }
42
+
43
+ // nx
44
+ if (existsSync(join(targetPath, 'nx.json'))) {
45
+ if (workspaces.length === 0) {
46
+ // Default nx layout
47
+ if (existsSync(join(targetPath, 'apps'))) workspaces.push('apps/*');
48
+ if (existsSync(join(targetPath, 'libs'))) workspaces.push('libs/*');
49
+ if (existsSync(join(targetPath, 'packages'))) workspaces.push('packages/*');
50
+ }
51
+ }
52
+
53
+ // lerna
54
+ const lernaPath = join(targetPath, 'lerna.json');
55
+ if (existsSync(lernaPath) && workspaces.length === 0) {
56
+ try {
57
+ const lerna = JSON.parse(readFileSync(lernaPath, 'utf-8'));
58
+ if (Array.isArray(lerna.packages)) {
59
+ workspaces.push(...lerna.packages);
60
+ }
61
+ } catch {}
62
+ }
63
+
64
+ return { isMonorepo: workspaces.length > 0, workspaces };
65
+ }
package/src/presets.js ADDED
@@ -0,0 +1,54 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Built-in configuration presets for Doorman
3
+ // ---------------------------------------------------------------------------
4
+
5
+ /**
6
+ * "recommended" — sensible defaults for most projects.
7
+ * All categories, medium+ severity, common ignore patterns.
8
+ */
9
+ const recommended = {
10
+ rules: {},
11
+ categories: [], // empty = all categories
12
+ severity: 'medium',
13
+ ignore: ['test/**', 'vendor/**', '*.min.js'],
14
+ };
15
+
16
+ /**
17
+ * "strict" — everything, no mercy.
18
+ * All categories, all severities, no extra ignore patterns.
19
+ */
20
+ const strict = {
21
+ rules: {},
22
+ categories: [], // all categories
23
+ severity: 'low',
24
+ ignore: [],
25
+ };
26
+
27
+ /**
28
+ * "minimal" — only the critical stuff.
29
+ * Security + reliability only, high+ severity.
30
+ */
31
+ const minimal = {
32
+ rules: {},
33
+ categories: ['security', 'reliability'],
34
+ severity: 'high',
35
+ ignore: ['test/**', 'vendor/**', '*.min.js'],
36
+ };
37
+
38
+ const PRESETS = { recommended, strict, minimal };
39
+
40
+ /**
41
+ * Resolve a preset by name. Returns a shallow copy or null if unknown.
42
+ */
43
+ export function getPreset(name) {
44
+ const preset = PRESETS[name];
45
+ if (!preset) return null;
46
+ return {
47
+ ...preset,
48
+ rules: { ...preset.rules },
49
+ categories: [...preset.categories],
50
+ ignore: [...preset.ignore],
51
+ };
52
+ }
53
+
54
+ export { PRESETS };