mcp-wordpress 2.11.13 → 3.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 (247) hide show
  1. package/README.md +14 -29
  2. package/dist/cache/CacheInvalidation.js.map +1 -1
  3. package/dist/cache/CacheManager.d.ts +7 -0
  4. package/dist/cache/CacheManager.d.ts.map +1 -1
  5. package/dist/cache/CacheManager.js +21 -7
  6. package/dist/cache/CacheManager.js.map +1 -1
  7. package/dist/cache/HttpCacheWrapper.js.map +1 -1
  8. package/dist/cache/SEOCacheManager.d.ts.map +1 -1
  9. package/dist/cache/SEOCacheManager.js +6 -1
  10. package/dist/cache/SEOCacheManager.js.map +1 -1
  11. package/dist/cache/index.d.ts.map +1 -1
  12. package/dist/cache/index.js.map +1 -1
  13. package/dist/client/CachedWordPressClient.d.ts.map +1 -1
  14. package/dist/client/CachedWordPressClient.js.map +1 -1
  15. package/dist/client/MockWordPressClient.d.ts.map +1 -1
  16. package/dist/client/MockWordPressClient.js.map +1 -1
  17. package/dist/client/SEOWordPressClient.d.ts.map +1 -1
  18. package/dist/client/SEOWordPressClient.js.map +1 -1
  19. package/dist/client/api.d.ts +11 -26
  20. package/dist/client/api.d.ts.map +1 -1
  21. package/dist/client/api.js +111 -203
  22. package/dist/client/api.js.map +1 -1
  23. package/dist/client/auth.d.ts.map +1 -1
  24. package/dist/client/auth.js.map +1 -1
  25. package/dist/client/managers/AuthManager.d.ts.map +1 -1
  26. package/dist/client/managers/RequestManager.d.ts.map +1 -1
  27. package/dist/client/managers/RequestManager.js +6 -5
  28. package/dist/client/managers/RequestManager.js.map +1 -1
  29. package/dist/client/managers/composed/MigrationAdapter.d.ts +3 -3
  30. package/dist/client/managers/composed/MigrationAdapter.d.ts.map +1 -1
  31. package/dist/client/managers/composed/MigrationAdapter.js +2 -2
  32. package/dist/client/managers/composed/MigrationAdapter.js.map +1 -1
  33. package/dist/client/managers/composed/index.d.ts +7 -7
  34. package/dist/client/managers/composed/index.d.ts.map +1 -1
  35. package/dist/client/managers/composed/index.js +6 -6
  36. package/dist/client/managers/composed/index.js.map +1 -1
  37. package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts +1 -1
  38. package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts.map +1 -1
  39. package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts +1 -1
  40. package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts.map +1 -1
  41. package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts +1 -1
  42. package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts.map +1 -1
  43. package/dist/client/operations/comments.d.ts +58 -0
  44. package/dist/client/operations/comments.d.ts.map +1 -0
  45. package/dist/client/operations/comments.js +74 -0
  46. package/dist/client/operations/comments.js.map +1 -0
  47. package/dist/client/operations/index.d.ts +12 -0
  48. package/dist/client/operations/index.d.ts.map +1 -0
  49. package/dist/client/operations/index.js +12 -0
  50. package/dist/client/operations/index.js.map +1 -0
  51. package/dist/client/operations/media.d.ts +55 -0
  52. package/dist/client/operations/media.d.ts.map +1 -0
  53. package/dist/client/operations/media.js +132 -0
  54. package/dist/client/operations/media.js.map +1 -0
  55. package/dist/client/operations/pages.d.ts +50 -0
  56. package/dist/client/operations/pages.d.ts.map +1 -0
  57. package/dist/client/operations/pages.js +56 -0
  58. package/dist/client/operations/pages.js.map +1 -0
  59. package/dist/client/operations/posts.d.ts +50 -0
  60. package/dist/client/operations/posts.d.ts.map +1 -0
  61. package/dist/client/operations/posts.js +53 -0
  62. package/dist/client/operations/posts.js.map +1 -0
  63. package/dist/client/operations/site.d.ts +60 -0
  64. package/dist/client/operations/site.d.ts.map +1 -0
  65. package/dist/client/operations/site.js +83 -0
  66. package/dist/client/operations/site.js.map +1 -0
  67. package/dist/client/operations/taxonomies.d.ts +69 -0
  68. package/dist/client/operations/taxonomies.d.ts.map +1 -0
  69. package/dist/client/operations/taxonomies.js +87 -0
  70. package/dist/client/operations/taxonomies.js.map +1 -0
  71. package/dist/client/operations/users.d.ts +50 -0
  72. package/dist/client/operations/users.d.ts.map +1 -0
  73. package/dist/client/operations/users.js +57 -0
  74. package/dist/client/operations/users.js.map +1 -0
  75. package/dist/config/ServerConfiguration.d.ts.map +1 -1
  76. package/dist/config/ServerConfiguration.js.map +1 -1
  77. package/dist/docs/DocumentationGenerator.js.map +1 -1
  78. package/dist/performance/MetricsCollector.d.ts.map +1 -1
  79. package/dist/performance/MetricsCollector.js.map +1 -1
  80. package/dist/performance/PerformanceMonitor.js.map +1 -1
  81. package/dist/security/AISecurityScanner.d.ts.map +1 -1
  82. package/dist/security/AISecurityScanner.js +3 -2
  83. package/dist/security/AISecurityScanner.js.map +1 -1
  84. package/dist/security/AutomatedRemediation.js.map +1 -1
  85. package/dist/security/InputValidator.d.ts.map +1 -1
  86. package/dist/security/InputValidator.js +30 -18
  87. package/dist/security/InputValidator.js.map +1 -1
  88. package/dist/security/SecurityCIPipeline.d.ts +19 -196
  89. package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
  90. package/dist/security/SecurityCIPipeline.js +95 -639
  91. package/dist/security/SecurityCIPipeline.js.map +1 -1
  92. package/dist/security/SecurityConfig.js.map +1 -1
  93. package/dist/security/SecurityConfigManager.js.map +1 -1
  94. package/dist/security/SecurityGateExecutor.d.ts +67 -0
  95. package/dist/security/SecurityGateExecutor.d.ts.map +1 -0
  96. package/dist/security/SecurityGateExecutor.js +363 -0
  97. package/dist/security/SecurityGateExecutor.js.map +1 -0
  98. package/dist/security/SecurityMonitoring.js.map +1 -1
  99. package/dist/security/SecurityReportGenerator.d.ts +65 -0
  100. package/dist/security/SecurityReportGenerator.d.ts.map +1 -0
  101. package/dist/security/SecurityReportGenerator.js +210 -0
  102. package/dist/security/SecurityReportGenerator.js.map +1 -0
  103. package/dist/security/SecurityReviewer.js.map +1 -1
  104. package/dist/security/SecurityTypes.d.ts +188 -0
  105. package/dist/security/SecurityTypes.d.ts.map +1 -0
  106. package/dist/security/SecurityTypes.js +6 -0
  107. package/dist/security/SecurityTypes.js.map +1 -0
  108. package/dist/security/index.d.ts +5 -28
  109. package/dist/security/index.d.ts.map +1 -1
  110. package/dist/security/index.js +4 -0
  111. package/dist/security/index.js.map +1 -1
  112. package/dist/server/ConnectionTester.d.ts.map +1 -1
  113. package/dist/server/ConnectionTester.js.map +1 -1
  114. package/dist/server/ToolRegistry.d.ts.map +1 -1
  115. package/dist/server/ToolRegistry.js.map +1 -1
  116. package/dist/tools/BaseToolManager.d.ts.map +1 -1
  117. package/dist/tools/BaseToolManager.js.map +1 -1
  118. package/dist/tools/auth.d.ts.map +1 -1
  119. package/dist/tools/auth.js.map +1 -1
  120. package/dist/tools/cache.d.ts.map +1 -1
  121. package/dist/tools/cache.js.map +1 -1
  122. package/dist/tools/comments.d.ts.map +1 -1
  123. package/dist/tools/comments.js.map +1 -1
  124. package/dist/tools/media.d.ts.map +1 -1
  125. package/dist/tools/media.js.map +1 -1
  126. package/dist/tools/pages.d.ts.map +1 -1
  127. package/dist/tools/pages.js.map +1 -1
  128. package/dist/tools/performance/PerformanceHelpers.d.ts +116 -0
  129. package/dist/tools/performance/PerformanceHelpers.d.ts.map +1 -0
  130. package/dist/tools/performance/PerformanceHelpers.js +298 -0
  131. package/dist/tools/performance/PerformanceHelpers.js.map +1 -0
  132. package/dist/tools/performance/PerformanceTools.d.ts +54 -0
  133. package/dist/tools/performance/PerformanceTools.d.ts.map +1 -0
  134. package/dist/tools/performance/PerformanceTools.js +687 -0
  135. package/dist/tools/performance/PerformanceTools.js.map +1 -0
  136. package/dist/tools/performance/index.d.ts +8 -0
  137. package/dist/tools/performance/index.d.ts.map +1 -0
  138. package/dist/tools/performance/index.js +8 -0
  139. package/dist/tools/performance/index.js.map +1 -0
  140. package/dist/tools/performance.d.ts +12 -69
  141. package/dist/tools/performance.d.ts.map +1 -1
  142. package/dist/tools/performance.js +12 -920
  143. package/dist/tools/performance.js.map +1 -1
  144. package/dist/tools/posts.d.ts.map +1 -1
  145. package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts.map +1 -1
  146. package/dist/tools/seo/analyzers/ContentAnalyzer.js +14 -3
  147. package/dist/tools/seo/analyzers/ContentAnalyzer.js.map +1 -1
  148. package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -1
  149. package/dist/tools/seo/auditors/SiteAuditor.js +12 -3
  150. package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -1
  151. package/dist/tools/seo/generators/MetaGenerator.d.ts.map +1 -1
  152. package/dist/tools/seo/generators/MetaGenerator.js +25 -8
  153. package/dist/tools/seo/generators/MetaGenerator.js.map +1 -1
  154. package/dist/tools/seo/generators/SchemaGenerator.d.ts.map +1 -1
  155. package/dist/tools/seo/generators/SchemaGenerator.js.map +1 -1
  156. package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts.map +1 -1
  157. package/dist/tools/seo/optimizers/InternalLinkingSuggester.js.map +1 -1
  158. package/dist/tools/site.d.ts.map +1 -1
  159. package/dist/tools/site.js.map +1 -1
  160. package/dist/tools/taxonomies.d.ts.map +1 -1
  161. package/dist/tools/taxonomies.js.map +1 -1
  162. package/dist/tools/users.d.ts.map +1 -1
  163. package/dist/tools/users.js.map +1 -1
  164. package/dist/utils/CircuitBreaker.d.ts +243 -0
  165. package/dist/utils/CircuitBreaker.d.ts.map +1 -0
  166. package/dist/utils/CircuitBreaker.js +456 -0
  167. package/dist/utils/CircuitBreaker.js.map +1 -0
  168. package/dist/utils/debug.d.ts.map +1 -1
  169. package/dist/utils/debug.js.map +1 -1
  170. package/dist/utils/error.js.map +1 -1
  171. package/dist/utils/index.d.ts +1 -0
  172. package/dist/utils/index.d.ts.map +1 -1
  173. package/dist/utils/index.js +2 -0
  174. package/dist/utils/index.js.map +1 -1
  175. package/dist/utils/logger.js.map +1 -1
  176. package/dist/utils/toolWrapper.d.ts.map +1 -1
  177. package/docs/DEPRECATIONS.md +157 -0
  178. package/package.json +2 -3
  179. package/src/cache/CacheInvalidation.ts +1 -1
  180. package/src/cache/CacheManager.ts +25 -8
  181. package/src/cache/HttpCacheWrapper.ts +1 -1
  182. package/src/cache/SEOCacheManager.ts +9 -3
  183. package/src/cache/index.ts +1 -1
  184. package/src/client/CachedWordPressClient.ts +6 -6
  185. package/src/client/MockWordPressClient.ts +3 -3
  186. package/src/client/SEOWordPressClient.ts +6 -6
  187. package/src/client/api.ts +129 -215
  188. package/src/client/auth.ts +3 -3
  189. package/src/client/managers/AuthManager.ts +1 -1
  190. package/src/client/managers/RequestManager.ts +6 -7
  191. package/src/client/managers/composed/MigrationAdapter.ts +4 -4
  192. package/src/client/managers/composed/index.ts +7 -7
  193. package/src/client/managers/implementations/ConfigurationProviderImpl.ts +1 -1
  194. package/src/client/managers/implementations/ErrorHandlerImpl.ts +1 -1
  195. package/src/client/managers/implementations/ParameterValidatorImpl.ts +1 -1
  196. package/src/client/operations/comments.ts +96 -0
  197. package/src/client/operations/index.ts +12 -0
  198. package/src/client/operations/media.ts +162 -0
  199. package/src/client/operations/pages.ts +71 -0
  200. package/src/client/operations/posts.ts +68 -0
  201. package/src/client/operations/site.ts +106 -0
  202. package/src/client/operations/taxonomies.ts +115 -0
  203. package/src/client/operations/users.ts +72 -0
  204. package/src/config/ServerConfiguration.ts +6 -6
  205. package/src/docs/DocumentationGenerator.ts +3 -3
  206. package/src/performance/MetricsCollector.ts +4 -4
  207. package/src/performance/PerformanceMonitor.ts +1 -1
  208. package/src/security/AISecurityScanner.ts +4 -3
  209. package/src/security/AutomatedRemediation.ts +1 -1
  210. package/src/security/InputValidator.ts +36 -19
  211. package/src/security/SecurityCIPipeline.ts +130 -953
  212. package/src/security/SecurityConfig.ts +1 -1
  213. package/src/security/SecurityConfigManager.ts +1 -1
  214. package/src/security/SecurityGateExecutor.ts +485 -0
  215. package/src/security/SecurityMonitoring.ts +1 -1
  216. package/src/security/SecurityReportGenerator.ts +272 -0
  217. package/src/security/SecurityReviewer.ts +1 -1
  218. package/src/security/SecurityTypes.ts +199 -0
  219. package/src/security/index.ts +6 -1
  220. package/src/server/ConnectionTester.ts +4 -4
  221. package/src/server/ToolRegistry.ts +6 -6
  222. package/src/tools/BaseToolManager.ts +2 -2
  223. package/src/tools/auth.ts +3 -3
  224. package/src/tools/cache.ts +3 -3
  225. package/src/tools/comments.ts +3 -3
  226. package/src/tools/media.ts +3 -3
  227. package/src/tools/pages.ts +3 -3
  228. package/src/tools/performance/PerformanceHelpers.ts +330 -0
  229. package/src/tools/performance/PerformanceTools.ts +854 -0
  230. package/src/tools/performance/index.ts +8 -0
  231. package/src/tools/performance.ts +12 -1073
  232. package/src/tools/posts.ts +1 -1
  233. package/src/tools/seo/analyzers/ContentAnalyzer.ts +21 -7
  234. package/src/tools/seo/auditors/SiteAuditor.ts +18 -7
  235. package/src/tools/seo/generators/MetaGenerator.ts +33 -12
  236. package/src/tools/seo/generators/SchemaGenerator.ts +3 -3
  237. package/src/tools/seo/optimizers/InternalLinkingSuggester.ts +4 -4
  238. package/src/tools/site.ts +3 -3
  239. package/src/tools/taxonomies.ts +3 -3
  240. package/src/tools/users.ts +4 -4
  241. package/src/utils/CircuitBreaker.ts +572 -0
  242. package/src/utils/debug.ts +3 -3
  243. package/src/utils/error.ts +1 -1
  244. package/src/utils/index.ts +3 -0
  245. package/src/utils/logger.ts +1 -1
  246. package/src/utils/toolWrapper.ts +2 -2
  247. package/docs/BRANCH_PROTECTION.md +0 -0
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * Security CI/CD Pipeline Integration
3
3
  * Provides security checks and gates for continuous integration and deployment
4
+ *
5
+ * This module orchestrates security gates using:
6
+ * - SecurityGateExecutor: Handles gate and check execution
7
+ * - SecurityReportGenerator: Handles report generation and statistics
8
+ * - SecurityTypes: Shared type definitions
4
9
  */
5
10
  import { AISecurityScanner } from "./AISecurityScanner.js";
6
11
  import { AutomatedRemediation } from "./AutomatedRemediation.js";
@@ -8,6 +13,8 @@ import { SecurityReviewer } from "./SecurityReviewer.js";
8
13
  import { SecurityConfigManager } from "./SecurityConfigManager.js";
9
14
  import { SecurityUtils } from "./SecurityConfig.js";
10
15
  import { LoggerFactory } from "../utils/logger.js";
16
+ import { SecurityGateExecutor } from "./SecurityGateExecutor.js";
17
+ import { SecurityReportGenerator } from "./SecurityReportGenerator.js";
11
18
  const logger = LoggerFactory.security();
12
19
  /**
13
20
  * Security CI/CD Pipeline Manager
@@ -20,7 +27,8 @@ export class SecurityCIPipeline {
20
27
  configManager;
21
28
  config;
22
29
  gates = new Map();
23
- reports = [];
30
+ gateExecutor;
31
+ reportGenerator;
24
32
  constructor(config = { projectPath: "/" }, deps) {
25
33
  // Basic validation expected by tests
26
34
  const configWithPath = config;
@@ -31,46 +39,51 @@ export class SecurityCIPipeline {
31
39
  throw new Error("Invalid scanner configuration");
32
40
  }
33
41
  this.config = config;
34
- // Instantiate components (tests provide vi.mocks which should replace constructors in the dist/ runtime)
35
42
  // Allow dependency injection for tests to provide mocked implementations
36
43
  this.scanner = (deps && deps.scanner) || new AISecurityScanner();
37
44
  this.remediation = (deps && deps.remediation) || new AutomatedRemediation();
38
45
  this.reviewer = (deps && deps.reviewer) || new SecurityReviewer();
39
46
  this.configManager = (deps && deps.configManager) || new SecurityConfigManager();
40
- // Small helper: ensure methods exist and are spyable under vitest without overwriting existing mocks
41
- const makeMockable = (obj, methods) => {
42
- const _objRecord = obj;
43
- // Only ensure missing methods exist. Never overwrite or wrap existing properties so test-provided
44
- // mocks are not replaced.
45
- const viRef = globalThis.vi;
46
- if (!obj)
47
- return;
48
- for (const m of methods) {
49
- try {
50
- const current = _objRecord[m];
51
- // If method already exists (mock or real), do not replace it.
52
- if (typeof current !== "undefined")
53
- continue;
54
- if (viRef && typeof viRef.fn === "function") {
55
- _objRecord[m] = viRef.fn();
56
- }
57
- else {
58
- // Provide a harmless default implementation when tests aren't present
59
- _objRecord[m] = () => Promise.resolve(null);
60
- }
47
+ // Ensure methods exist and are spyable under vitest
48
+ this.makeMockable(this.scanner, ["scanCodeForVulnerabilities", "performScan", "scanDependencies", "scanSecrets"]);
49
+ this.makeMockable(this.reviewer, ["reviewCode", "reviewDirectory", "reviewConfiguration"]);
50
+ this.makeMockable(this.remediation, ["autoFix", "generateRecommendations"]);
51
+ this.makeMockable(this.configManager, ["getSecurityConfig", "validateConfiguration", "initialize"]);
52
+ // Initialize executor and report generator
53
+ this.gateExecutor = new SecurityGateExecutor({
54
+ scanner: this.scanner,
55
+ reviewer: this.reviewer,
56
+ configManager: this.configManager,
57
+ });
58
+ this.reportGenerator = new SecurityReportGenerator();
59
+ this.initializeDefaultGates();
60
+ }
61
+ /**
62
+ * Helper to make methods mockable in tests
63
+ */
64
+ makeMockable(obj, methods) {
65
+ const _objRecord = obj;
66
+ const viRef = globalThis.vi;
67
+ if (!obj)
68
+ return;
69
+ for (const m of methods) {
70
+ try {
71
+ const current = _objRecord[m];
72
+ if (typeof current !== "undefined")
73
+ continue;
74
+ if (viRef && typeof viRef.fn === "function") {
75
+ _objRecord[m] = viRef.fn();
61
76
  }
62
- catch (_e) {
63
- // ignore and continue
77
+ else {
78
+ _objRecord[m] = () => Promise.resolve(null);
64
79
  }
65
80
  }
66
- };
67
- makeMockable(this.scanner, ["scanCodeForVulnerabilities", "performScan", "scanDependencies", "scanSecrets"]);
68
- makeMockable(this.reviewer, ["reviewCode", "reviewDirectory", "reviewConfiguration"]);
69
- makeMockable(this.remediation, ["autoFix", "generateRecommendations"]);
70
- makeMockable(this.configManager, ["getSecurityConfig", "validateConfiguration", "initialize"]);
71
- this.initializeDefaultGates();
81
+ catch (_e) {
82
+ // ignore and continue
83
+ }
84
+ }
72
85
  }
73
- // --- Public API expected by tests ---
86
+ // --- Public Gate Execution API ---
74
87
  async executePreCommitGate(options = {}) {
75
88
  const context = this.buildDefaultContext();
76
89
  return this.executeSecurityGates("pre-commit", context, options);
@@ -83,7 +96,7 @@ export class SecurityCIPipeline {
83
96
  const context = this.buildDefaultContext();
84
97
  return this.executeSecurityGates("pre-deploy", context, options);
85
98
  }
86
- // Convenience runners for individual checks used by tests
99
+ // --- Individual Check Runners ---
87
100
  async runVulnerabilityCheck(opts = {}) {
88
101
  const check = {
89
102
  id: "vulnerability-scan",
@@ -102,7 +115,7 @@ export class SecurityCIPipeline {
102
115
  try {
103
116
  const scanPromise = scanner.scanCodeForVulnerabilities
104
117
  ? scanner.scanCodeForVulnerabilities()
105
- : scanner.performScan?.() ?? Promise.resolve({ vulnerabilities: [], riskScore: 0 });
118
+ : (scanner.performScan?.() ?? Promise.resolve({ vulnerabilities: [], riskScore: 0 }));
106
119
  if (opts.timeout && opts.timeout > 0) {
107
120
  const res = await Promise.race([
108
121
  scanPromise,
@@ -111,17 +124,12 @@ export class SecurityCIPipeline {
111
124
  if (res && res.__timeout) {
112
125
  return { checkId: check.id, status: "timeout" };
113
126
  }
114
- // treat the scan result like a successful check
115
127
  return { checkId: check.id, status: "passed", result: res };
116
128
  }
117
129
  const res = await scanPromise;
118
- // Validate response: treat null/undefined as invalid, but accept objects that may not include
119
- // a vulnerabilities array (normalize to empty array). This keeps tests' mocked scanner
120
- // results compatible while still flagging truly malformed responses.
121
130
  if (res === null || typeof res === "undefined") {
122
131
  return { checkId: check.id, status: "failed", error: "Invalid response" };
123
132
  }
124
- // Normalize vulnerabilities field
125
133
  const resWithVulns = res;
126
134
  if (!Array.isArray(resWithVulns.vulnerabilities)) {
127
135
  resWithVulns.vulnerabilities = [];
@@ -132,7 +140,6 @@ export class SecurityCIPipeline {
132
140
  if (attempts >= maxAttempts) {
133
141
  return { checkId: check.id, status: "failed", error: String(err) };
134
142
  }
135
- // otherwise retry
136
143
  }
137
144
  }
138
145
  return { checkId: check.id, status: "failed", error: "Retries exhausted" };
@@ -148,13 +155,12 @@ export class SecurityCIPipeline {
148
155
  return { checkId: "secrets-scan", status: "passed", result: res };
149
156
  }
150
157
  async runCodeReviewCheck() {
151
- // Prefer reviewCode if present in mocked reviewer, else fallback
152
158
  const reviewer = this.reviewer;
153
159
  const reviewFn = reviewer.reviewCode ?? reviewer.reviewDirectory;
154
160
  const res = (await reviewFn?.("src/")) ?? { issues: [] };
155
161
  return { checkId: "code-review", status: "passed", result: res };
156
162
  }
157
- // Gate management
163
+ // --- Gate Management ---
158
164
  configureGate(gate) {
159
165
  if (!gate || !gate.id || !gate.stage) {
160
166
  throw new Error("Invalid gate configuration");
@@ -187,27 +193,41 @@ export class SecurityCIPipeline {
187
193
  const g = this.gates.get(gateId);
188
194
  return !!g && g.enabled;
189
195
  }
190
- // Reporting
196
+ getSecurityGate(gateId) {
197
+ return this.gates.get(gateId) || null;
198
+ }
199
+ updateSecurityGate(gateId, updates) {
200
+ const gate = this.gates.get(gateId);
201
+ if (!gate) {
202
+ return false;
203
+ }
204
+ const updatedGate = { ...gate, ...updates, id: gateId };
205
+ this.gates.set(gateId, updatedGate);
206
+ logger.info(`Updated security gate: ${updatedGate.name}`, { gateName: updatedGate.name });
207
+ return true;
208
+ }
209
+ // --- Reporting ---
191
210
  async generateReport() {
192
- if (this.reports.length > 0)
193
- return this.reports[this.reports.length - 1];
194
- return this.createEmptyReport(SecurityUtils.generateSecureToken(8), "summary", Date.now());
211
+ const latest = this.reportGenerator.getLatestReport();
212
+ if (latest)
213
+ return latest;
214
+ return this.reportGenerator.createEmptyReport(SecurityUtils.generateSecureToken(8), "summary", Date.now());
195
215
  }
196
216
  async exportReport(format) {
197
217
  const report = await this.generateReport();
198
- if (format === "html")
199
- return `<html><body>${JSON.stringify(report)}</body></html>`;
200
- if (format === "xml")
201
- return `<report>${JSON.stringify(report)}</report>`;
202
- return JSON.stringify(report);
218
+ return this.reportGenerator.exportReport(report, format);
203
219
  }
204
220
  async calculateSecurityMetrics() {
205
221
  const report = await this.generateReport();
206
- const overallScore = report.summary.securityScore ?? 100;
207
- const riskLevel = overallScore > 80 ? "low" : overallScore > 50 ? "medium" : "high";
208
- return { overallScore, riskLevel, complianceStatus: report.summary.compliance };
222
+ return this.reportGenerator.calculateSecurityMetrics(report);
223
+ }
224
+ getReports(options = {}) {
225
+ return this.reportGenerator.getReports(options);
209
226
  }
210
- // Automated remediation
227
+ getStatistics() {
228
+ return this.reportGenerator.getStatistics();
229
+ }
230
+ // --- Remediation ---
211
231
  async executeAutoRemediation() {
212
232
  try {
213
233
  const remediation = this.remediation;
@@ -222,7 +242,7 @@ export class SecurityCIPipeline {
222
242
  const remediation = this.remediation;
223
243
  return remediation.generateRecommendations?.() ?? [];
224
244
  }
225
- // Full pipeline orchestration
245
+ // --- Full Pipeline Orchestration ---
226
246
  async executeFullPipeline() {
227
247
  const start = Date.now();
228
248
  const stages = [];
@@ -252,7 +272,7 @@ export class SecurityCIPipeline {
252
272
  }
253
273
  return { stages, overallStatus, duration: Math.max(1, Date.now() - start) };
254
274
  }
255
- // Notifications
275
+ // --- Notifications ---
256
276
  sendNotification(payload) {
257
277
  logger.info("Sending notification", { payload });
258
278
  }
@@ -261,45 +281,26 @@ export class SecurityCIPipeline {
261
281
  const body = `Stage: ${data.stage}\nStatus: ${data.status}\ncritical: ${data.criticalIssues ?? 0}`;
262
282
  return { subject, body };
263
283
  }
264
- // Configuration reloading
284
+ // --- Configuration ---
265
285
  reloadConfiguration(newConfig) {
266
286
  if (!newConfig || newConfig.projectPath == null)
267
287
  throw new Error("Invalid configuration");
268
- // preserve gate enabled/disabled states
269
288
  const state = {};
270
289
  for (const [id, g] of this.gates.entries())
271
290
  state[id] = g.enabled;
272
291
  this.config = newConfig;
273
- // reapply states
274
292
  for (const [id, enabled] of Object.entries(state)) {
275
293
  const g = this.gates.get(id);
276
294
  if (g)
277
295
  g.enabled = enabled;
278
296
  }
279
297
  }
280
- // Helper to build a default pipeline context
281
- buildDefaultContext() {
282
- return {
283
- repositoryUrl: this.config.repositoryUrl ?? "",
284
- branch: this.config.branch ?? "main",
285
- commit: this.config.commitHash ?? "",
286
- author: this.config.author ?? "",
287
- environment: this.config.environment ?? "test",
288
- buildNumber: this.config.buildNumber ?? "0",
289
- artifacts: this.config.artifacts ?? [],
290
- };
291
- }
292
- /**
293
- * Initialize the security pipeline
294
- */
295
298
  async initialize() {
296
299
  logger.info("Initializing security CI/CD pipeline");
297
300
  await this.configManager.initialize();
298
301
  logger.info("Security pipeline ready");
299
302
  }
300
- /**
301
- * Execute security gates for a pipeline stage
302
- */
303
+ // --- Core Gate Execution ---
303
304
  async executeSecurityGates(stage, context, options = {}) {
304
305
  const reportId = SecurityUtils.generateSecureToken(16);
305
306
  const startTime = Date.now();
@@ -311,23 +312,22 @@ export class SecurityCIPipeline {
311
312
  const applicableGates = Array.from(this.gates.values()).filter((gate) => gate.stage === stage && gate.enabled);
312
313
  if (applicableGates.length === 0) {
313
314
  logger.warn(`No security gates configured for stage: ${stage}`, { stage });
314
- return this.createEmptyReport(reportId, stage, startTime);
315
+ return this.reportGenerator.createEmptyReport(reportId, stage, startTime);
315
316
  }
316
317
  const gateResults = [];
317
318
  let overallStatus = "passed";
318
319
  for (const gate of applicableGates) {
319
320
  logger.info(`Executing gate: ${gate.name}`, { gateName: gate.name });
320
321
  try {
321
- const gateResult = await this.executeSecurityGate(gate, context, options);
322
+ const gateResult = await this.gateExecutor.executeGate(gate, context, options);
322
323
  gateResults.push(gateResult);
323
- // Update overall status
324
324
  if (gateResult.status === "failed" && gate.blocking) {
325
325
  overallStatus = "failed";
326
326
  }
327
327
  else if (gateResult.status === "warning" && overallStatus === "passed") {
328
328
  overallStatus = "warning";
329
329
  }
330
- // Send notification for failed gates (tests expect notification on failure)
330
+ // Send notification for failed gates
331
331
  try {
332
332
  if (gateResult.status === "failed") {
333
333
  const criticalCount = gateResult.checks
@@ -337,16 +337,15 @@ export class SecurityCIPipeline {
337
337
  }
338
338
  }
339
339
  catch (_e) {
340
- // ignore notification errors during test runs
340
+ // ignore notification errors
341
341
  }
342
- // Stop on blocking failure unless continuing on failure
343
342
  if (gateResult.status === "failed" && gate.blocking && !options.continueOnFailure) {
344
343
  logger.error(`Stopping pipeline due to blocking gate failure: ${gate.name}`, { gateName: gate.name });
345
344
  break;
346
345
  }
347
346
  }
348
347
  catch (_error) {
349
- logger.error(`Gate execution _error: ${gate.name}`, { gateName: gate.name, _error });
348
+ logger.error(`Gate execution error: ${gate.name}`, { gateName: gate.name, _error });
350
349
  const errorResult = {
351
350
  gateId: gate.id,
352
351
  gateName: gate.name,
@@ -363,476 +362,23 @@ export class SecurityCIPipeline {
363
362
  }
364
363
  }
365
364
  }
366
- const report = this.generatePipelineReport(reportId, stage, startTime, overallStatus, gateResults, context);
367
- this.reports.push(report);
365
+ const report = this.reportGenerator.generateReport(reportId, stage, startTime, overallStatus, gateResults, context);
366
+ this.reportGenerator.storeReport(report);
368
367
  logger.info(`${stage} gates completed`, { stage, status: overallStatus });
369
368
  return report;
370
369
  }
371
- /**
372
- * Execute a single security gate
373
- */
374
- async executeSecurityGate(gate, context, options = {}) {
375
- const startTime = Date.now();
376
- const checkResults = [];
377
- for (const check of gate.checks) {
378
- if (!check.enabled) {
379
- continue;
380
- }
381
- logger.info(`Running check: ${check.name}`, { checkName: check.name });
382
- try {
383
- const checkResult = await this.executeSecurityCheck(check, context, options);
384
- checkResults.push(checkResult);
385
- }
386
- catch (_error) {
387
- logger.error(`Check execution _error: ${check.name}`, { checkName: check.name, _error });
388
- checkResults.push({
389
- checkId: check.id,
390
- checkName: check.name,
391
- status: "error",
392
- duration: Date.now() - startTime,
393
- findings: [],
394
- details: `Check execution failed: ${_error instanceof Error ? _error.message : String(_error)}`,
395
- score: 0,
396
- });
397
- }
398
- }
399
- // Evaluate gate status based on check results and thresholds
400
- const gateStatus = this.evaluateGateStatus(gate, checkResults);
401
- return {
402
- gateId: gate.id,
403
- gateName: gate.id,
404
- status: gateStatus.status,
405
- duration: Date.now() - startTime,
406
- checks: checkResults,
407
- blocking: gate.blocking,
408
- message: gateStatus.message,
409
- };
410
- }
411
- /**
412
- * Execute a single security check
413
- */
414
- async executeSecurityCheck(check, context, options = {}) {
415
- const startTime = Date.now();
416
- const findings = [];
417
- let score = 100; // Initialize with safe default
418
- let details = "";
419
- if (options.dryRun) {
420
- return {
421
- checkId: check.id,
422
- checkName: check.name,
423
- status: "passed",
424
- duration: Date.now() - startTime,
425
- findings: [],
426
- details: "Dry run - no actual checks performed",
427
- score: 100,
428
- };
429
- }
430
- try {
431
- switch (check.type) {
432
- case "scan":
433
- const scanResults = await this.executeScanCheck(check, context);
434
- findings.push(...scanResults.findings);
435
- score = scanResults.score;
436
- details = scanResults.details;
437
- break;
438
- case "review":
439
- const reviewResults = await this.executeReviewCheck(check, context);
440
- findings.push(...reviewResults.findings);
441
- score = reviewResults.score;
442
- details = reviewResults.details;
443
- break;
444
- case "dependency":
445
- const depResults = await this.executeDependencyCheck(check, context);
446
- findings.push(...depResults.findings);
447
- score = depResults.score;
448
- details = depResults.details;
449
- break;
450
- case "configuration":
451
- const configResults = await this.executeConfigurationCheck(check, context);
452
- findings.push(...configResults.findings);
453
- score = configResults.score;
454
- details = configResults.details;
455
- break;
456
- case "secrets":
457
- const secretResults = await this.executeSecretsCheck(check, context);
458
- findings.push(...secretResults.findings);
459
- score = secretResults.score;
460
- details = secretResults.details;
461
- break;
462
- case "compliance":
463
- const complianceResults = await this.executeComplianceCheck(check, context);
464
- findings.push(...complianceResults.findings);
465
- score = complianceResults.score;
466
- details = complianceResults.details;
467
- break;
468
- default:
469
- throw new Error(`Unknown check type: ${check.type}`);
470
- }
471
- // Determine check status based on findings
472
- const criticalCount = findings.filter((f) => f.severity === "critical").length;
473
- const highCount = findings.filter((f) => f.severity === "high").length;
474
- let status;
475
- if (criticalCount > 0) {
476
- status = "failed";
477
- }
478
- else if (highCount > 0) {
479
- status = "warning";
480
- }
481
- else {
482
- status = "passed";
483
- }
484
- return {
485
- checkId: check.id,
486
- checkName: check.name,
487
- status,
488
- duration: Date.now() - startTime,
489
- findings,
490
- details,
491
- score,
492
- };
493
- }
494
- catch (_error) {
495
- // Log the original error for observability while converting to warning
496
- logger.warn(`Security check ${check.name} encountered error (converting to warning)`, {
497
- checkId: check.id,
498
- checkName: check.name,
499
- error: String(_error),
500
- errorStack: _error instanceof Error ? _error.stack : undefined,
501
- });
502
- // Instead of throwing (which produced 'error' status externally and failed gates),
503
- // convert this into a non-blocking warning result so default gates pass unless
504
- // tests intentionally inject critical/high findings.
505
- return {
506
- checkId: check.id,
507
- checkName: check.name,
508
- status: "warning",
509
- duration: Date.now() - startTime,
510
- findings: [],
511
- details: `Check execution issue treated as warning: ${String(_error)}`,
512
- // Use 90 as neutral score to indicate uncertainty (lower than perfect 100 but still passing)
513
- score: Math.min(score ?? 100, 90),
514
- };
515
- }
516
- }
517
- /**
518
- * Execute security scan check
519
- */
520
- async executeScanCheck(check, context) {
521
- const scanParams = check.parameters;
522
- // Prefer explicit scanner APIs when present (tests mock these). Fall back to performScan when needed.
523
- const scannerAny = this.scanner;
524
- let scanResult;
525
- if (typeof scannerAny.scanCodeForVulnerabilities === "function") {
526
- scanResult = await scannerAny.scanCodeForVulnerabilities();
527
- }
528
- else if (typeof scannerAny.performScan === "function") {
529
- scanResult = await scannerAny.performScan({
530
- targets: scanParams.targets ?? ["src/"],
531
- depth: scanParams.depth ?? "deep",
532
- includeRuntime: scanParams.includeRuntime ?? false,
533
- includeFileSystem: scanParams.includeFileSystem ?? true,
534
- });
535
- }
536
- else {
537
- scanResult = { vulnerabilities: [], summary: { total: 0, critical: 0, high: 0, medium: 0 } };
538
- }
539
- // Normalize scanResult shape if mocks provide only vulnerabilities without summary
540
- const scanResultTyped = scanResult;
541
- const vulns = Array.isArray(scanResultTyped?.vulnerabilities) ? scanResultTyped.vulnerabilities : [];
542
- const summary = scanResultTyped?.summary
543
- ? scanResultTyped.summary
544
- : {
545
- total: vulns.length,
546
- critical: vulns.filter((v) => v?.severity === "critical").length,
547
- high: vulns.filter((v) => v?.severity === "high").length,
548
- medium: vulns.filter((v) => v?.severity === "medium").length,
549
- };
550
- const findings = (vulns || []).map((vuln) => {
551
- const v = vuln;
552
- return {
553
- id: v.id || "unknown",
554
- severity: v.severity || "medium",
555
- type: v.type || "vulnerability",
556
- description: v.description || "No description",
557
- file: v.location?.file,
558
- line: v.location?.line,
559
- remediation: v.remediation?.suggested,
560
- };
561
- });
562
- const summaryTyped = summary;
563
- const score = Math.max(0, 100 - ((summaryTyped.critical || 0) * 10 + (summaryTyped.high || 0) * 5 + (summaryTyped.medium || 0) * 2));
564
- return {
565
- findings,
566
- score,
567
- details: `Scanned codebase: ${summary.total} vulnerabilities found`,
568
- };
569
- }
570
- /**
571
- * Execute code review check
572
- */
573
- async executeReviewCheck(check, context) {
574
- const reviewParams = check.parameters;
575
- // Support either reviewer.reviewDirectory (returns array) or reviewer.reviewCode (returns single summary)
576
- const reviewerAny = this.reviewer;
577
- const raw = typeof reviewerAny.reviewDirectory === "function"
578
- ? await reviewerAny.reviewDirectory("src/", {
579
- recursive: true,
580
- rules: reviewParams.rules ?? [],
581
- excludeRules: reviewParams.excludeRules ?? [],
582
- aiAnalysis: reviewParams.aiAnalysis ?? false,
583
- })
584
- : typeof reviewerAny.reviewCode === "function"
585
- ? await reviewerAny.reviewCode("src/", {
586
- rules: reviewParams.rules ?? [],
587
- aiAnalysis: reviewParams.aiAnalysis ?? false,
588
- })
589
- : [];
590
- const reviewResults = Array.isArray(raw) ? raw : raw ? [raw] : [];
591
- const allFindings = [];
592
- let totalScore = 0;
593
- for (const result of reviewResults) {
594
- const resultTyped = result;
595
- const resultFindings = (resultTyped.findings || []).map((finding) => {
596
- const f = finding;
597
- return {
598
- id: f.id || "unknown",
599
- severity: f.severity || "medium",
600
- type: f.category || f.type || "review",
601
- description: f.message || f.description || "No description",
602
- file: resultTyped.file,
603
- line: f.line,
604
- remediation: f.recommendation || f.remediation,
605
- };
606
- });
607
- allFindings.push(...resultFindings);
608
- totalScore += this.calculateFileScore(result.findings || []);
609
- }
610
- const averageScore = reviewResults.length > 0 ? totalScore / reviewResults.length : 100;
611
- return {
612
- findings: allFindings,
613
- score: averageScore,
614
- details: `Reviewed ${reviewResults.length} files: ${allFindings.length} security issues found`,
615
- };
616
- }
617
- /**
618
- * Execute dependency check
619
- */
620
- async executeDependencyCheck(check, context) {
621
- // This would integrate with npm audit, Snyk, or similar tools
622
- logger.info("Dependency check - integration with external tools required");
623
- return {
624
- findings: [],
625
- score: 100,
626
- details: "Dependency check completed - no vulnerabilities found",
627
- };
628
- }
629
- /**
630
- * Execute configuration check
631
- */
632
- async executeConfigurationCheck(check, context) {
633
- // Some test mocks provide validateCompliance, others may not. Fall back to compliant=true when unavailable.
634
- let compliance = { compliant: true, violations: [] };
635
- try {
636
- const configManager = this.configManager;
637
- if (typeof configManager.validateCompliance === "function") {
638
- compliance = await configManager.validateCompliance(context.environment);
639
- }
640
- else if (typeof configManager.getSecurityConfig === "function") {
641
- // derive basic compliance from config when validateCompliance is not available
642
- const cfg = configManager.getSecurityConfig() || {};
643
- compliance = { compliant: !!cfg.compliant, violations: cfg.violations ?? [] };
644
- }
645
- }
646
- catch (_e) {
647
- compliance = { compliant: true, violations: [] };
648
- }
649
- const findings = compliance.violations.map((violation, index) => ({
650
- id: `config-${index}`,
651
- severity: "medium",
652
- type: "Configuration",
653
- description: violation,
654
- remediation: "Review security configuration",
655
- }));
656
- const score = compliance.compliant ? 100 : Math.max(0, 100 - compliance.violations.length * 10);
657
- return {
658
- findings,
659
- score,
660
- details: `Configuration compliance: ${compliance.compliant ? "compliant" : "non-compliant"}`,
661
- };
662
- }
663
- /**
664
- * Execute secrets check
665
- */
666
- async executeSecretsCheck(check, context) {
667
- // This would integrate with tools like TruffleHog, GitLeaks, etc.
668
- logger.info("Secrets check - integration with secret scanning tools required");
669
- return {
670
- findings: [],
671
- score: 100,
672
- details: "Secrets scan completed - no exposed secrets found",
673
- };
674
- }
675
- /**
676
- * Execute compliance check
677
- */
678
- async executeComplianceCheck(check, context) {
679
- const complianceParams = check.parameters;
680
- const frameworks = complianceParams.frameworks ?? ["OWASP", "CWE"];
681
- const findings = [];
682
- // Check for compliance with security frameworks
683
- for (const framework of frameworks) {
684
- // This would integrate with compliance checking tools
685
- logger.info(`Checking ${framework} compliance`, { framework });
686
- }
687
- return {
688
- findings,
689
- score: 100,
690
- details: `Compliance check completed for frameworks: ${frameworks.join(", ")}`,
691
- };
692
- }
693
- /**
694
- * Calculate security score for file findings
695
- */
696
- calculateFileScore(findings) {
697
- const severityWeights = { critical: 20, high: 10, medium: 5, low: 2, info: 1 };
698
- const penalty = findings.reduce((sum, finding) => {
699
- return sum + (severityWeights[finding.severity] || 0);
700
- }, 0);
701
- return Math.max(0, 100 - penalty);
702
- }
703
- /**
704
- * Evaluate gate status based on check results and thresholds
705
- */
706
- evaluateGateStatus(gate, checkResults) {
707
- const allFindings = checkResults.flatMap((result) => result.findings);
708
- const criticalCount = allFindings.filter((f) => f.severity === "critical").length;
709
- const highCount = allFindings.filter((f) => f.severity === "high").length;
710
- const mediumCount = allFindings.filter((f) => f.severity === "medium").length;
711
- // Exclude error checks from average score calculation
712
- const validChecks = checkResults.filter((result) => result.status !== "error");
713
- const averageScore = validChecks.length > 0 ? validChecks.reduce((sum, result) => sum + result.score, 0) / validChecks.length : 100;
714
- // Check thresholds
715
- if (criticalCount > gate.thresholds.maxCritical) {
716
- return {
717
- status: "failed",
718
- message: `Critical vulnerabilities (${criticalCount}) exceed threshold (${gate.thresholds.maxCritical})`,
719
- };
720
- }
721
- if (highCount > gate.thresholds.maxHigh) {
722
- return {
723
- status: "failed",
724
- message: `High-severity vulnerabilities (${highCount}) exceed threshold (${gate.thresholds.maxHigh})`,
725
- };
726
- }
727
- if (averageScore < gate.thresholds.minSecurityScore) {
728
- return {
729
- status: "failed",
730
- message: `Security score (${averageScore.toFixed(1)}) below threshold (${gate.thresholds.minSecurityScore})`,
731
- };
732
- }
733
- if (mediumCount > gate.thresholds.maxMedium) {
734
- return {
735
- status: "warning",
736
- message: `Medium-severity vulnerabilities (${mediumCount}) exceed threshold (${gate.thresholds.maxMedium})`,
737
- };
738
- }
739
- return {
740
- status: "passed",
741
- message: "All security checks passed",
742
- };
743
- }
744
- /**
745
- * Generate pipeline security report
746
- */
747
- generatePipelineReport(reportId, stage, startTime, status, gateResults, context) {
748
- const allFindings = gateResults.flatMap((gate) => gate.checks.flatMap((check) => check.findings));
749
- const summary = {
750
- totalIssues: allFindings.length,
751
- criticalIssues: allFindings.filter((f) => f.severity === "critical").length,
752
- highIssues: allFindings.filter((f) => f.severity === "high").length,
753
- mediumIssues: allFindings.filter((f) => f.severity === "medium").length,
754
- lowIssues: allFindings.filter((f) => f.severity === "low").length,
755
- securityScore: this.calculateOverallSecurityScore(gateResults),
756
- compliance: status === "passed",
757
- };
758
- const recommendations = this.generateRecommendations(gateResults, summary);
759
- return {
760
- reportId,
761
- timestamp: new Date(),
762
- stage,
763
- status,
764
- duration: Date.now() - startTime,
765
- gates: gateResults,
766
- summary,
767
- recommendations,
768
- artifacts: this.generateArtifacts(reportId, gateResults),
769
- };
770
- }
771
- /**
772
- * Calculate overall security score
773
- */
774
- calculateOverallSecurityScore(gateResults) {
775
- const allChecks = gateResults.flatMap((gate) => gate.checks);
776
- if (allChecks.length === 0) {
777
- return 100;
778
- }
779
- const totalScore = allChecks.reduce((sum, check) => sum + check.score, 0);
780
- return totalScore / allChecks.length;
781
- }
782
- /**
783
- * Generate recommendations based on results
784
- */
785
- generateRecommendations(gateResults, summary) {
786
- const recommendations = [];
787
- if (summary.criticalIssues > 0) {
788
- recommendations.push("Address critical security vulnerabilities immediately before deployment");
789
- }
790
- if (summary.highIssues > 5) {
791
- recommendations.push("Review and remediate high-severity security issues");
792
- }
793
- if (summary.securityScore < 80) {
794
- recommendations.push("Improve overall security posture through code review and security training");
795
- }
796
- const failedGates = gateResults.filter((gate) => gate.status === "failed");
797
- if (failedGates.length > 0) {
798
- recommendations.push(`Review failed security gates: ${failedGates.map((g) => g.gateName).join(", ")}`);
799
- }
800
- return recommendations;
801
- }
802
- /**
803
- * Generate artifacts for the security report
804
- */
805
- generateArtifacts(reportId, gateResults) {
806
- // In a real implementation, this would generate SARIF files, security reports, etc.
807
- return [`security-report-${reportId}.json`, `security-findings-${reportId}.sarif`];
808
- }
809
- /**
810
- * Create empty report for stages with no gates
811
- */
812
- createEmptyReport(reportId, stage, startTime) {
370
+ // --- Private Helpers ---
371
+ buildDefaultContext() {
813
372
  return {
814
- reportId,
815
- timestamp: new Date(),
816
- stage,
817
- status: "passed",
818
- duration: Date.now() - startTime,
819
- gates: [],
820
- summary: {
821
- totalIssues: 0,
822
- criticalIssues: 0,
823
- highIssues: 0,
824
- mediumIssues: 0,
825
- lowIssues: 0,
826
- securityScore: 100,
827
- compliance: true,
828
- },
829
- recommendations: [],
830
- artifacts: [],
373
+ repositoryUrl: this.config.repositoryUrl ?? "",
374
+ branch: this.config.branch ?? "main",
375
+ commit: this.config.commitHash ?? "",
376
+ author: this.config.author ?? "",
377
+ environment: this.config.environment ?? "test",
378
+ buildNumber: this.config.buildNumber ?? "0",
379
+ artifacts: this.config.artifacts ?? [],
831
380
  };
832
381
  }
833
- /**
834
- * Initialize default security gates
835
- */
836
382
  initializeDefaultGates() {
837
383
  // Pre-commit gate
838
384
  this.gates.set("pre-commit", {
@@ -958,95 +504,5 @@ export class SecurityCIPipeline {
958
504
  exceptions: [],
959
505
  });
960
506
  }
961
- /**
962
- * Get security gate configuration
963
- */
964
- getSecurityGate(gateId) {
965
- return this.gates.get(gateId) || null;
966
- }
967
- /**
968
- * Update security gate configuration
969
- */
970
- updateSecurityGate(gateId, updates) {
971
- const gate = this.gates.get(gateId);
972
- if (!gate) {
973
- return false;
974
- }
975
- const updatedGate = { ...gate, ...updates, id: gateId };
976
- this.gates.set(gateId, updatedGate);
977
- logger.info(`Updated security gate: ${updatedGate.name}`, { gateName: updatedGate.name });
978
- return true;
979
- }
980
- /**
981
- * Get pipeline reports
982
- */
983
- getReports(options = {}) {
984
- let reports = [...this.reports];
985
- if (options.stage) {
986
- reports = reports.filter((r) => r.stage === options.stage);
987
- }
988
- if (options.status) {
989
- reports = reports.filter((r) => r.status === options.status);
990
- }
991
- if (options.since) {
992
- reports = reports.filter((r) => r.timestamp >= options.since);
993
- }
994
- // Sort by timestamp (newest first)
995
- reports.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
996
- if (options.limit) {
997
- reports = reports.slice(0, options.limit);
998
- }
999
- return reports;
1000
- }
1001
- /**
1002
- * Get pipeline statistics
1003
- */
1004
- getStatistics() {
1005
- const totalReports = this.reports.length;
1006
- const passedReports = this.reports.filter((r) => r.status === "passed").length;
1007
- const passRate = totalReports > 0 ? passedReports / totalReports : 1;
1008
- const averageSecurityScore = totalReports > 0 ? this.reports.reduce((sum, r) => sum + r.summary.securityScore, 0) / totalReports : 100;
1009
- // Count issue types
1010
- const issueTypes = {};
1011
- this.reports.forEach((report) => {
1012
- report.gates.forEach((gate) => {
1013
- gate.checks.forEach((check) => {
1014
- check.findings.forEach((finding) => {
1015
- issueTypes[finding.type] = (issueTypes[finding.type] || 0) + 1;
1016
- });
1017
- });
1018
- });
1019
- });
1020
- const mostCommonIssues = Object.entries(issueTypes)
1021
- .map(([type, count]) => ({ type, count }))
1022
- .sort((a, b) => b.count - a.count)
1023
- .slice(0, 5);
1024
- // Calculate gate performance
1025
- const gateStats = {};
1026
- this.reports.forEach((report) => {
1027
- report.gates.forEach((gate) => {
1028
- if (!gateStats[gate.gateId]) {
1029
- gateStats[gate.gateId] = { total: 0, passed: 0, totalDuration: 0 };
1030
- }
1031
- gateStats[gate.gateId].total++;
1032
- gateStats[gate.gateId].totalDuration += gate.duration;
1033
- if (gate.status === "passed") {
1034
- gateStats[gate.gateId].passed++;
1035
- }
1036
- });
1037
- });
1038
- const gatePerformance = Object.entries(gateStats).map(([gateId, stats]) => ({
1039
- gateId,
1040
- successRate: stats.total > 0 ? stats.passed / stats.total : 0,
1041
- averageDuration: stats.total > 0 ? stats.totalDuration / stats.total : 0,
1042
- }));
1043
- return {
1044
- totalReports,
1045
- passRate,
1046
- averageSecurityScore,
1047
- mostCommonIssues,
1048
- gatePerformance,
1049
- };
1050
- }
1051
507
  }
1052
508
  //# sourceMappingURL=SecurityCIPipeline.js.map