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
 
6
11
  import { AISecurityScanner } from "./AISecurityScanner.js";
@@ -8,102 +13,24 @@ import { AutomatedRemediation } from "./AutomatedRemediation.js";
8
13
  import { SecurityReviewer } from "./SecurityReviewer.js";
9
14
  import { SecurityConfigManager } from "./SecurityConfigManager.js";
10
15
  import { SecurityUtils } from "./SecurityConfig.js";
11
- import { LoggerFactory } from "../utils/logger.js";
16
+ import { LoggerFactory } from "@/utils/logger.js";
17
+ import { SecurityGateExecutor } from "./SecurityGateExecutor.js";
18
+ import { SecurityReportGenerator } from "./SecurityReportGenerator.js";
19
+ import type {
20
+ SecurityGate,
21
+ SecurityCheck,
22
+ PipelineSecurityReport,
23
+ PipelineContext,
24
+ GateExecutionOptions,
25
+ PipelineStatistics,
26
+ ReportFilterOptions,
27
+ } from "./SecurityTypes.js";
28
+
29
+ // Re-export types for backward compatibility
30
+ export type { PipelineSecurityReport } from "./SecurityTypes.js";
12
31
 
13
32
  const logger = LoggerFactory.security();
14
33
 
15
- interface SecurityGate {
16
- id: string;
17
- name: string;
18
- stage: "pre-commit" | "pre-build" | "pre-deploy" | "post-deploy";
19
- enabled: boolean;
20
- blocking: boolean;
21
- checks: SecurityCheck[];
22
- thresholds: {
23
- maxCritical: number;
24
- maxHigh: number;
25
- maxMedium: number;
26
- minSecurityScore: number;
27
- };
28
- exceptions: string[];
29
- }
30
-
31
- interface SecurityCheck {
32
- id: string;
33
- name: string;
34
- type: "scan" | "review" | "dependency" | "configuration" | "secrets" | "compliance";
35
- enabled: boolean;
36
- timeout: number;
37
- retries: number;
38
- parameters: Record<string, unknown>;
39
- }
40
-
41
- export interface PipelineSecurityReport {
42
- reportId: string;
43
- timestamp: Date;
44
- stage: string;
45
- status: "passed" | "failed" | "warning";
46
- duration: number;
47
- gates: GateResult[];
48
- summary: {
49
- totalIssues: number;
50
- criticalIssues: number;
51
- highIssues: number;
52
- mediumIssues: number;
53
- lowIssues: number;
54
- securityScore: number;
55
- compliance: boolean;
56
- };
57
- recommendations: string[];
58
- artifacts: string[];
59
- }
60
-
61
- interface GateResult {
62
- gateId: string;
63
- gateName: string;
64
- status: "passed" | "failed" | "warning" | "skipped";
65
- duration: number;
66
- checks: CheckResult[];
67
- blocking: boolean;
68
- message: string;
69
- }
70
-
71
- interface CheckResult {
72
- checkId: string;
73
- checkName: string;
74
- status: "passed" | "failed" | "warning" | "error";
75
- duration: number;
76
- findings: SecurityFinding[];
77
- details: string;
78
- score: number;
79
- }
80
-
81
- interface SecurityFinding {
82
- id: string;
83
- severity: "critical" | "high" | "medium" | "low" | "info";
84
- type: string;
85
- description: string;
86
- file?: string | undefined;
87
- line?: number | undefined;
88
- remediation?: string | undefined;
89
- }
90
-
91
- interface PipelineContext {
92
- repositoryUrl: string;
93
- branch: string;
94
- commit: string;
95
- author: string;
96
- pullRequest?: {
97
- id: string;
98
- title: string;
99
- source: string;
100
- target: string;
101
- };
102
- environment: string;
103
- buildNumber: string;
104
- artifacts: string[];
105
- }
106
-
107
34
  /**
108
35
  * Security CI/CD Pipeline Manager
109
36
  */
@@ -114,8 +41,10 @@ export class SecurityCIPipeline {
114
41
  public reviewer: SecurityReviewer;
115
42
  public configManager: SecurityConfigManager;
116
43
  public config: Record<string, unknown>;
44
+
117
45
  private gates: Map<string, SecurityGate> = new Map();
118
- private reports: PipelineSecurityReport[] = [];
46
+ private gateExecutor: SecurityGateExecutor;
47
+ private reportGenerator: SecurityReportGenerator;
119
48
 
120
49
  constructor(
121
50
  config: Record<string, unknown> = { projectPath: "/" },
@@ -140,77 +69,72 @@ export class SecurityCIPipeline {
140
69
 
141
70
  this.config = config;
142
71
 
143
- // Instantiate components (tests provide vi.mocks which should replace constructors in the dist/ runtime)
144
72
  // Allow dependency injection for tests to provide mocked implementations
145
73
  this.scanner = (deps && deps.scanner) || new AISecurityScanner();
146
74
  this.remediation = (deps && deps.remediation) || new AutomatedRemediation();
147
75
  this.reviewer = (deps && deps.reviewer) || new SecurityReviewer();
148
76
  this.configManager = (deps && deps.configManager) || new SecurityConfigManager();
149
77
 
150
- // Small helper: ensure methods exist and are spyable under vitest without overwriting existing mocks
151
- const makeMockable = (obj: unknown, methods: string[]) => {
152
- const _objRecord = obj as Record<string, unknown>;
153
- // Only ensure missing methods exist. Never overwrite or wrap existing properties so test-provided
154
- // mocks are not replaced.
155
- const viRef = (globalThis as Record<string, unknown> & { vi?: Record<string, unknown> }).vi;
156
- if (!obj) return;
78
+ // Ensure methods exist and are spyable under vitest
79
+ this.makeMockable(this.scanner, ["scanCodeForVulnerabilities", "performScan", "scanDependencies", "scanSecrets"]);
80
+ this.makeMockable(this.reviewer, ["reviewCode", "reviewDirectory", "reviewConfiguration"]);
81
+ this.makeMockable(this.remediation, ["autoFix", "generateRecommendations"]);
82
+ this.makeMockable(this.configManager, ["getSecurityConfig", "validateConfiguration", "initialize"]);
83
+
84
+ // Initialize executor and report generator
85
+ this.gateExecutor = new SecurityGateExecutor({
86
+ scanner: this.scanner,
87
+ reviewer: this.reviewer,
88
+ configManager: this.configManager,
89
+ });
90
+ this.reportGenerator = new SecurityReportGenerator();
157
91
 
158
- for (const m of methods) {
159
- try {
160
- const current = _objRecord[m];
161
- // If method already exists (mock or real), do not replace it.
162
- if (typeof current !== "undefined") continue;
163
-
164
- if (viRef && typeof viRef.fn === "function") {
165
- _objRecord[m] = (viRef.fn as () => unknown)();
166
- } else {
167
- // Provide a harmless default implementation when tests aren't present
168
- _objRecord[m] = () => Promise.resolve(null);
169
- }
170
- } catch (_e) {
171
- // ignore and continue
172
- }
173
- }
174
- };
92
+ this.initializeDefaultGates();
93
+ }
94
+
95
+ /**
96
+ * Helper to make methods mockable in tests
97
+ */
98
+ private makeMockable(obj: unknown, methods: string[]): void {
99
+ const _objRecord = obj as Record<string, unknown>;
100
+ const viRef = (globalThis as Record<string, unknown> & { vi?: Record<string, unknown> }).vi;
101
+ if (!obj) return;
175
102
 
176
- makeMockable(this.scanner, ["scanCodeForVulnerabilities", "performScan", "scanDependencies", "scanSecrets"]);
177
- makeMockable(this.reviewer, ["reviewCode", "reviewDirectory", "reviewConfiguration"]);
178
- makeMockable(this.remediation, ["autoFix", "generateRecommendations"]);
179
- makeMockable(this.configManager, ["getSecurityConfig", "validateConfiguration", "initialize"]);
103
+ for (const m of methods) {
104
+ try {
105
+ const current = _objRecord[m];
106
+ if (typeof current !== "undefined") continue;
180
107
 
181
- this.initializeDefaultGates();
108
+ if (viRef && typeof viRef.fn === "function") {
109
+ _objRecord[m] = (viRef.fn as () => unknown)();
110
+ } else {
111
+ _objRecord[m] = () => Promise.resolve(null);
112
+ }
113
+ } catch (_e) {
114
+ // ignore and continue
115
+ }
116
+ }
182
117
  }
183
118
 
184
- // --- Public API expected by tests ---
119
+ // --- Public Gate Execution API ---
185
120
 
186
121
  async executePreCommitGate(options: Record<string, unknown> = {}): Promise<PipelineSecurityReport> {
187
122
  const context = this.buildDefaultContext();
188
- return this.executeSecurityGates(
189
- "pre-commit",
190
- context,
191
- options as { skipNonBlocking?: boolean; continueOnFailure?: boolean; dryRun?: boolean },
192
- );
123
+ return this.executeSecurityGates("pre-commit", context, options as GateExecutionOptions);
193
124
  }
194
125
 
195
126
  async executePreBuildGate(options: Record<string, unknown> = {}): Promise<PipelineSecurityReport> {
196
127
  const context = this.buildDefaultContext();
197
- return this.executeSecurityGates(
198
- "pre-build",
199
- context,
200
- options as { skipNonBlocking?: boolean; continueOnFailure?: boolean; dryRun?: boolean },
201
- );
128
+ return this.executeSecurityGates("pre-build", context, options as GateExecutionOptions);
202
129
  }
203
130
 
204
131
  async executePreDeployGate(options: Record<string, unknown> = {}): Promise<PipelineSecurityReport> {
205
132
  const context = this.buildDefaultContext();
206
- return this.executeSecurityGates(
207
- "pre-deploy",
208
- context,
209
- options as { skipNonBlocking?: boolean; continueOnFailure?: boolean; dryRun?: boolean },
210
- );
133
+ return this.executeSecurityGates("pre-deploy", context, options as GateExecutionOptions);
211
134
  }
212
135
 
213
- // Convenience runners for individual checks used by tests
136
+ // --- Individual Check Runners ---
137
+
214
138
  async runVulnerabilityCheck(opts: { timeout?: number; retries?: number } = {}): Promise<Record<string, unknown>> {
215
139
  const check: SecurityCheck = {
216
140
  id: "vulnerability-scan",
@@ -237,7 +161,7 @@ export class SecurityCIPipeline {
237
161
  try {
238
162
  const scanPromise = scanner.scanCodeForVulnerabilities
239
163
  ? scanner.scanCodeForVulnerabilities()
240
- : scanner.performScan?.() ?? Promise.resolve({ vulnerabilities: [], riskScore: 0 });
164
+ : (scanner.performScan?.() ?? Promise.resolve({ vulnerabilities: [], riskScore: 0 }));
241
165
 
242
166
  if (opts.timeout && opts.timeout > 0) {
243
167
  const res = await Promise.race([
@@ -249,19 +173,14 @@ export class SecurityCIPipeline {
249
173
  return { checkId: check.id, status: "timeout" };
250
174
  }
251
175
 
252
- // treat the scan result like a successful check
253
176
  return { checkId: check.id, status: "passed", result: res } as Record<string, unknown>;
254
177
  }
255
178
 
256
179
  const res = await scanPromise;
257
- // Validate response: treat null/undefined as invalid, but accept objects that may not include
258
- // a vulnerabilities array (normalize to empty array). This keeps tests' mocked scanner
259
- // results compatible while still flagging truly malformed responses.
260
180
  if (res === null || typeof res === "undefined") {
261
181
  return { checkId: check.id, status: "failed", error: "Invalid response" } as Record<string, unknown>;
262
182
  }
263
183
 
264
- // Normalize vulnerabilities field
265
184
  const resWithVulns = res as Record<string, unknown> & { vulnerabilities?: unknown[] };
266
185
  if (!Array.isArray(resWithVulns.vulnerabilities)) {
267
186
  resWithVulns.vulnerabilities = [];
@@ -272,7 +191,6 @@ export class SecurityCIPipeline {
272
191
  if (attempts >= maxAttempts) {
273
192
  return { checkId: check.id, status: "failed", error: String(err) } as Record<string, unknown>;
274
193
  }
275
- // otherwise retry
276
194
  }
277
195
  }
278
196
  return { checkId: check.id, status: "failed", error: "Retries exhausted" } as Record<string, unknown>;
@@ -291,7 +209,6 @@ export class SecurityCIPipeline {
291
209
  }
292
210
 
293
211
  async runCodeReviewCheck(): Promise<Record<string, unknown>> {
294
- // Prefer reviewCode if present in mocked reviewer, else fallback
295
212
  const reviewer = this.reviewer as unknown as {
296
213
  reviewCode?: (path: string) => Promise<{ issues: unknown[] }>;
297
214
  reviewDirectory?: (path: string) => Promise<{ issues: unknown[] }>;
@@ -301,7 +218,8 @@ export class SecurityCIPipeline {
301
218
  return { checkId: "code-review", status: "passed", result: res };
302
219
  }
303
220
 
304
- // Gate management
221
+ // --- Gate Management ---
222
+
305
223
  configureGate(gate: Partial<SecurityGate>): void {
306
224
  if (!gate || !gate.id || !gate.stage) {
307
225
  throw new Error("Invalid gate configuration");
@@ -337,27 +255,51 @@ export class SecurityCIPipeline {
337
255
  return !!g && g.enabled;
338
256
  }
339
257
 
340
- // Reporting
258
+ getSecurityGate(gateId: string): SecurityGate | null {
259
+ return this.gates.get(gateId) || null;
260
+ }
261
+
262
+ updateSecurityGate(gateId: string, updates: Partial<SecurityGate>): boolean {
263
+ const gate = this.gates.get(gateId);
264
+ if (!gate) {
265
+ return false;
266
+ }
267
+
268
+ const updatedGate = { ...gate, ...updates, id: gateId };
269
+ this.gates.set(gateId, updatedGate);
270
+
271
+ logger.info(`Updated security gate: ${updatedGate.name}`, { gateName: updatedGate.name });
272
+ return true;
273
+ }
274
+
275
+ // --- Reporting ---
276
+
341
277
  async generateReport(): Promise<PipelineSecurityReport> {
342
- if (this.reports.length > 0) return this.reports[this.reports.length - 1];
343
- return this.createEmptyReport(SecurityUtils.generateSecureToken(8), "summary", Date.now());
278
+ const latest = this.reportGenerator.getLatestReport();
279
+ if (latest) return latest;
280
+ return this.reportGenerator.createEmptyReport(SecurityUtils.generateSecureToken(8), "summary", Date.now());
344
281
  }
345
282
 
346
283
  async exportReport(format: string): Promise<string> {
347
284
  const report = await this.generateReport();
348
- if (format === "html") return `<html><body>${JSON.stringify(report)}</body></html>`;
349
- if (format === "xml") return `<report>${JSON.stringify(report)}</report>`;
350
- return JSON.stringify(report);
285
+ return this.reportGenerator.exportReport(report, format);
351
286
  }
352
287
 
353
288
  async calculateSecurityMetrics(): Promise<{ overallScore: number; riskLevel: string; complianceStatus: boolean }> {
354
289
  const report = await this.generateReport();
355
- const overallScore = report.summary.securityScore ?? 100;
356
- const riskLevel = overallScore > 80 ? "low" : overallScore > 50 ? "medium" : "high";
357
- return { overallScore, riskLevel, complianceStatus: report.summary.compliance };
290
+ return this.reportGenerator.calculateSecurityMetrics(report);
291
+ }
292
+
293
+ getReports(options: ReportFilterOptions = {}): PipelineSecurityReport[] {
294
+ return this.reportGenerator.getReports(options);
358
295
  }
359
296
 
360
- // Automated remediation
297
+ getStatistics(): PipelineStatistics {
298
+ return this.reportGenerator.getStatistics();
299
+ }
300
+
301
+ // --- Remediation ---
302
+
361
303
  async executeAutoRemediation(): Promise<Record<string, unknown>> {
362
304
  try {
363
305
  const remediation = this.remediation as unknown as Record<string, unknown> & { autoFix?: () => Promise<unknown> };
@@ -375,7 +317,8 @@ export class SecurityCIPipeline {
375
317
  return remediation.generateRecommendations?.() ?? [];
376
318
  }
377
319
 
378
- // Full pipeline orchestration
320
+ // --- Full Pipeline Orchestration ---
321
+
379
322
  async executeFullPipeline(): Promise<Record<string, unknown>> {
380
323
  const start = Date.now();
381
324
  const stages: PipelineSecurityReport[] = [];
@@ -411,7 +354,8 @@ export class SecurityCIPipeline {
411
354
  return { stages, overallStatus, duration: Math.max(1, Date.now() - start) };
412
355
  }
413
356
 
414
- // Notifications
357
+ // --- Notifications ---
358
+
415
359
  sendNotification(payload: Record<string, unknown>): void {
416
360
  logger.info("Sending notification", { payload });
417
361
  }
@@ -422,55 +366,33 @@ export class SecurityCIPipeline {
422
366
  return { subject, body };
423
367
  }
424
368
 
425
- // Configuration reloading
369
+ // --- Configuration ---
370
+
426
371
  reloadConfiguration(newConfig: Record<string, unknown>): void {
427
372
  if (!newConfig || newConfig.projectPath == null) throw new Error("Invalid configuration");
428
- // preserve gate enabled/disabled states
429
373
  const state: Record<string, boolean> = {};
430
374
  for (const [id, g] of this.gates.entries()) state[id] = g.enabled;
431
375
 
432
376
  this.config = newConfig;
433
377
 
434
- // reapply states
435
378
  for (const [id, enabled] of Object.entries(state)) {
436
379
  const g = this.gates.get(id);
437
380
  if (g) g.enabled = enabled;
438
381
  }
439
382
  }
440
383
 
441
- // Helper to build a default pipeline context
442
- private buildDefaultContext(): PipelineContext {
443
- return {
444
- repositoryUrl: this.config.repositoryUrl ?? "",
445
- branch: this.config.branch ?? "main",
446
- commit: this.config.commitHash ?? "",
447
- author: this.config.author ?? "",
448
- environment: this.config.environment ?? "test",
449
- buildNumber: this.config.buildNumber ?? "0",
450
- artifacts: this.config.artifacts ?? [],
451
- } as PipelineContext;
452
- }
453
-
454
- /**
455
- * Initialize the security pipeline
456
- */
457
384
  async initialize(): Promise<void> {
458
385
  logger.info("Initializing security CI/CD pipeline");
459
386
  await this.configManager.initialize();
460
387
  logger.info("Security pipeline ready");
461
388
  }
462
389
 
463
- /**
464
- * Execute security gates for a pipeline stage
465
- */
390
+ // --- Core Gate Execution ---
391
+
466
392
  async executeSecurityGates(
467
393
  stage: SecurityGate["stage"],
468
394
  context: PipelineContext,
469
- options: {
470
- skipNonBlocking?: boolean;
471
- continueOnFailure?: boolean;
472
- dryRun?: boolean;
473
- } = {},
395
+ options: GateExecutionOptions = {},
474
396
  ): Promise<PipelineSecurityReport> {
475
397
  const reportId = SecurityUtils.generateSecureToken(16);
476
398
  const startTime = Date.now();
@@ -485,27 +407,26 @@ export class SecurityCIPipeline {
485
407
 
486
408
  if (applicableGates.length === 0) {
487
409
  logger.warn(`No security gates configured for stage: ${stage}`, { stage });
488
- return this.createEmptyReport(reportId, stage, startTime);
410
+ return this.reportGenerator.createEmptyReport(reportId, stage, startTime);
489
411
  }
490
412
 
491
- const gateResults: GateResult[] = [];
413
+ const gateResults: import("./SecurityTypes.js").GateResult[] = [];
492
414
  let overallStatus: "passed" | "failed" | "warning" = "passed";
493
415
 
494
416
  for (const gate of applicableGates) {
495
417
  logger.info(`Executing gate: ${gate.name}`, { gateName: gate.name });
496
418
 
497
419
  try {
498
- const gateResult = await this.executeSecurityGate(gate, context, options);
420
+ const gateResult = await this.gateExecutor.executeGate(gate, context, options);
499
421
  gateResults.push(gateResult);
500
422
 
501
- // Update overall status
502
423
  if (gateResult.status === "failed" && gate.blocking) {
503
424
  overallStatus = "failed";
504
425
  } else if (gateResult.status === "warning" && overallStatus === "passed") {
505
426
  overallStatus = "warning";
506
427
  }
507
428
 
508
- // Send notification for failed gates (tests expect notification on failure)
429
+ // Send notification for failed gates
509
430
  try {
510
431
  if (gateResult.status === "failed") {
511
432
  const criticalCount = gateResult.checks
@@ -516,18 +437,17 @@ export class SecurityCIPipeline {
516
437
  );
517
438
  }
518
439
  } catch (_e) {
519
- // ignore notification errors during test runs
440
+ // ignore notification errors
520
441
  }
521
442
 
522
- // Stop on blocking failure unless continuing on failure
523
443
  if (gateResult.status === "failed" && gate.blocking && !options.continueOnFailure) {
524
444
  logger.error(`Stopping pipeline due to blocking gate failure: ${gate.name}`, { gateName: gate.name });
525
445
  break;
526
446
  }
527
447
  } catch (_error) {
528
- logger.error(`Gate execution _error: ${gate.name}`, { gateName: gate.name, _error });
448
+ logger.error(`Gate execution error: ${gate.name}`, { gateName: gate.name, _error });
529
449
 
530
- const errorResult: GateResult = {
450
+ const errorResult: import("./SecurityTypes.js").GateResult = {
531
451
  gateId: gate.id,
532
452
  gateName: gate.name,
533
453
  status: "failed",
@@ -546,647 +466,29 @@ export class SecurityCIPipeline {
546
466
  }
547
467
  }
548
468
 
549
- const report = this.generatePipelineReport(reportId, stage, startTime, overallStatus, gateResults, context);
469
+ const report = this.reportGenerator.generateReport(reportId, stage, startTime, overallStatus, gateResults, context);
550
470
 
551
- this.reports.push(report);
471
+ this.reportGenerator.storeReport(report);
552
472
 
553
473
  logger.info(`${stage} gates completed`, { stage, status: overallStatus });
554
474
 
555
475
  return report;
556
476
  }
557
477
 
558
- /**
559
- * Execute a single security gate
560
- */
561
- private async executeSecurityGate(
562
- gate: SecurityGate,
563
- context: PipelineContext,
564
- options: { dryRun?: boolean } = {},
565
- ): Promise<GateResult> {
566
- const startTime = Date.now();
567
- const checkResults: CheckResult[] = [];
568
-
569
- for (const check of gate.checks) {
570
- if (!check.enabled) {
571
- continue;
572
- }
573
-
574
- logger.info(`Running check: ${check.name}`, { checkName: check.name });
575
-
576
- try {
577
- const checkResult = await this.executeSecurityCheck(check, context, options);
578
- checkResults.push(checkResult);
579
- } catch (_error) {
580
- logger.error(`Check execution _error: ${check.name}`, { checkName: check.name, _error });
581
-
582
- checkResults.push({
583
- checkId: check.id,
584
- checkName: check.name,
585
- status: "error",
586
- duration: Date.now() - startTime,
587
- findings: [],
588
- details: `Check execution failed: ${_error instanceof Error ? _error.message : String(_error)}`,
589
- score: 0,
590
- });
591
- }
592
- }
593
-
594
- // Evaluate gate status based on check results and thresholds
595
- const gateStatus = this.evaluateGateStatus(gate, checkResults);
596
-
597
- return {
598
- gateId: gate.id,
599
- gateName: gate.id,
600
- status: gateStatus.status,
601
- duration: Date.now() - startTime,
602
- checks: checkResults,
603
- blocking: gate.blocking,
604
- message: gateStatus.message,
605
- };
606
- }
607
-
608
- /**
609
- * Execute a single security check
610
- */
611
- private async executeSecurityCheck(
612
- check: SecurityCheck,
613
- context: PipelineContext,
614
- options: { dryRun?: boolean } = {},
615
- ): Promise<CheckResult> {
616
- const startTime = Date.now();
617
- const findings: SecurityFinding[] = [];
618
- let score: number = 100; // Initialize with safe default
619
- let details = "";
620
-
621
- if (options.dryRun) {
622
- return {
623
- checkId: check.id,
624
- checkName: check.name,
625
- status: "passed",
626
- duration: Date.now() - startTime,
627
- findings: [],
628
- details: "Dry run - no actual checks performed",
629
- score: 100,
630
- };
631
- }
632
-
633
- try {
634
- switch (check.type) {
635
- case "scan":
636
- const scanResults = await this.executeScanCheck(check, context);
637
- findings.push(...scanResults.findings);
638
- score = scanResults.score;
639
- details = scanResults.details;
640
- break;
641
-
642
- case "review":
643
- const reviewResults = await this.executeReviewCheck(check, context);
644
- findings.push(...reviewResults.findings);
645
- score = reviewResults.score;
646
- details = reviewResults.details;
647
- break;
648
-
649
- case "dependency":
650
- const depResults = await this.executeDependencyCheck(check, context);
651
- findings.push(...depResults.findings);
652
- score = depResults.score;
653
- details = depResults.details;
654
- break;
655
-
656
- case "configuration":
657
- const configResults = await this.executeConfigurationCheck(check, context);
658
- findings.push(...configResults.findings);
659
- score = configResults.score;
660
- details = configResults.details;
661
- break;
662
-
663
- case "secrets":
664
- const secretResults = await this.executeSecretsCheck(check, context);
665
- findings.push(...secretResults.findings);
666
- score = secretResults.score;
667
- details = secretResults.details;
668
- break;
669
-
670
- case "compliance":
671
- const complianceResults = await this.executeComplianceCheck(check, context);
672
- findings.push(...complianceResults.findings);
673
- score = complianceResults.score;
674
- details = complianceResults.details;
675
- break;
676
-
677
- default:
678
- throw new Error(`Unknown check type: ${check.type}`);
679
- }
680
-
681
- // Determine check status based on findings
682
- const criticalCount = findings.filter((f) => f.severity === "critical").length;
683
- const highCount = findings.filter((f) => f.severity === "high").length;
684
-
685
- let status: CheckResult["status"];
686
- if (criticalCount > 0) {
687
- status = "failed";
688
- } else if (highCount > 0) {
689
- status = "warning";
690
- } else {
691
- status = "passed";
692
- }
693
-
694
- return {
695
- checkId: check.id,
696
- checkName: check.name,
697
- status,
698
- duration: Date.now() - startTime,
699
- findings,
700
- details,
701
- score,
702
- };
703
- } catch (_error) {
704
- // Log the original error for observability while converting to warning
705
- logger.warn(`Security check ${check.name} encountered error (converting to warning)`, {
706
- checkId: check.id,
707
- checkName: check.name,
708
- error: String(_error),
709
- errorStack: _error instanceof Error ? _error.stack : undefined,
710
- });
711
-
712
- // Instead of throwing (which produced 'error' status externally and failed gates),
713
- // convert this into a non-blocking warning result so default gates pass unless
714
- // tests intentionally inject critical/high findings.
715
- return {
716
- checkId: check.id,
717
- checkName: check.name,
718
- status: "warning",
719
- duration: Date.now() - startTime,
720
- findings: [],
721
- details: `Check execution issue treated as warning: ${String(_error)}`,
722
- // Use 90 as neutral score to indicate uncertainty (lower than perfect 100 but still passing)
723
- score: Math.min(score ?? 100, 90),
724
- };
725
- }
726
- }
727
-
728
- /**
729
- * Execute security scan check
730
- */
731
- private async executeScanCheck(
732
- check: SecurityCheck,
733
- context: PipelineContext,
734
- ): Promise<{
735
- findings: SecurityFinding[];
736
- score: number;
737
- details: string;
738
- }> {
739
- const scanParams = check.parameters as {
740
- targets?: string[];
741
- depth?: "shallow" | "deep" | "comprehensive";
742
- includeRuntime?: boolean;
743
- includeFileSystem?: boolean;
744
- };
745
- // Prefer explicit scanner APIs when present (tests mock these). Fall back to performScan when needed.
746
- const scannerAny = this.scanner as unknown as {
747
- scanCodeForVulnerabilities?: () => Promise<unknown>;
748
- performScan?: (opts?: unknown) => Promise<unknown>;
749
- };
750
-
751
- let scanResult: unknown;
752
- if (typeof scannerAny.scanCodeForVulnerabilities === "function") {
753
- scanResult = await scannerAny.scanCodeForVulnerabilities();
754
- } else if (typeof scannerAny.performScan === "function") {
755
- scanResult = await scannerAny.performScan({
756
- targets: scanParams.targets ?? ["src/"],
757
- depth: scanParams.depth ?? "deep",
758
- includeRuntime: scanParams.includeRuntime ?? false,
759
- includeFileSystem: scanParams.includeFileSystem ?? true,
760
- });
761
- } else {
762
- scanResult = { vulnerabilities: [], summary: { total: 0, critical: 0, high: 0, medium: 0 } };
763
- }
764
-
765
- // Normalize scanResult shape if mocks provide only vulnerabilities without summary
766
- const scanResultTyped = scanResult as
767
- | { vulnerabilities?: unknown[]; summary?: Record<string, unknown> }
768
- | null
769
- | undefined;
770
- const vulns = Array.isArray(scanResultTyped?.vulnerabilities) ? scanResultTyped.vulnerabilities : [];
771
- const summary = scanResultTyped?.summary
772
- ? scanResultTyped.summary
773
- : {
774
- total: vulns.length,
775
- critical: vulns.filter((v: unknown) => (v as { severity?: string })?.severity === "critical").length,
776
- high: vulns.filter((v: unknown) => (v as { severity?: string })?.severity === "high").length,
777
- medium: vulns.filter((v: unknown) => (v as { severity?: string })?.severity === "medium").length,
778
- };
779
-
780
- const findings: SecurityFinding[] = (vulns || []).map((vuln: unknown) => {
781
- const v = vuln as {
782
- id?: string;
783
- severity?: string;
784
- type?: string;
785
- description?: string;
786
- location?: { file?: string; line?: number };
787
- remediation?: { suggested?: string };
788
- };
789
- return {
790
- id: v.id || "unknown",
791
- severity: (v.severity as SecurityFinding["severity"]) || "medium",
792
- type: v.type || "vulnerability",
793
- description: v.description || "No description",
794
- file: v.location?.file,
795
- line: v.location?.line,
796
- remediation: v.remediation?.suggested,
797
- };
798
- });
799
- const summaryTyped = summary as { critical?: number; high?: number; medium?: number };
800
- const score = Math.max(
801
- 0,
802
- 100 - ((summaryTyped.critical || 0) * 10 + (summaryTyped.high || 0) * 5 + (summaryTyped.medium || 0) * 2),
803
- );
804
-
805
- return {
806
- findings,
807
- score,
808
- details: `Scanned codebase: ${summary.total} vulnerabilities found`,
809
- };
810
- }
811
-
812
- /**
813
- * Execute code review check
814
- */
815
- private async executeReviewCheck(
816
- check: SecurityCheck,
817
- context: PipelineContext,
818
- ): Promise<{
819
- findings: SecurityFinding[];
820
- score: number;
821
- details: string;
822
- }> {
823
- const reviewParams = check.parameters as { rules?: string[]; excludeRules?: string[]; aiAnalysis?: boolean };
824
-
825
- // Support either reviewer.reviewDirectory (returns array) or reviewer.reviewCode (returns single summary)
826
- const reviewerAny = this.reviewer as unknown as {
827
- reviewDirectory?: (path: string, opts?: unknown) => Promise<unknown>;
828
- reviewCode?: (path: string, opts?: unknown) => Promise<unknown>;
829
- };
830
-
831
- const raw =
832
- typeof reviewerAny.reviewDirectory === "function"
833
- ? await reviewerAny.reviewDirectory("src/", {
834
- recursive: true,
835
- rules: reviewParams.rules ?? [],
836
- excludeRules: reviewParams.excludeRules ?? [],
837
- aiAnalysis: reviewParams.aiAnalysis ?? false,
838
- })
839
- : typeof reviewerAny.reviewCode === "function"
840
- ? await reviewerAny.reviewCode("src/", {
841
- rules: reviewParams.rules ?? [],
842
- aiAnalysis: reviewParams.aiAnalysis ?? false,
843
- })
844
- : [];
845
-
846
- const reviewResults = Array.isArray(raw) ? raw : raw ? [raw] : [];
847
-
848
- const allFindings: SecurityFinding[] = [];
849
- let totalScore = 0;
850
-
851
- for (const result of reviewResults) {
852
- const resultTyped = result as { findings?: unknown[]; file?: string };
853
- const resultFindings = (resultTyped.findings || []).map((finding: unknown) => {
854
- const f = finding as {
855
- id?: string;
856
- severity?: string;
857
- category?: string;
858
- type?: string;
859
- message?: string;
860
- description?: string;
861
- line?: number;
862
- recommendation?: string;
863
- remediation?: string;
864
- };
865
- return {
866
- id: f.id || "unknown",
867
- severity: (f.severity as SecurityFinding["severity"]) || "medium",
868
- type: f.category || f.type || "review",
869
- description: f.message || f.description || "No description",
870
- file: resultTyped.file,
871
- line: f.line,
872
- remediation: f.recommendation || f.remediation,
873
- };
874
- });
875
-
876
- allFindings.push(...resultFindings);
877
- totalScore += this.calculateFileScore(result.findings || []);
878
- }
879
-
880
- const averageScore = reviewResults.length > 0 ? totalScore / reviewResults.length : 100;
881
-
882
- return {
883
- findings: allFindings,
884
- score: averageScore,
885
- details: `Reviewed ${reviewResults.length} files: ${allFindings.length} security issues found`,
886
- };
887
- }
888
-
889
- /**
890
- * Execute dependency check
891
- */
892
- private async executeDependencyCheck(
893
- check: SecurityCheck,
894
- context: PipelineContext,
895
- ): Promise<{
896
- findings: SecurityFinding[];
897
- score: number;
898
- details: string;
899
- }> {
900
- // This would integrate with npm audit, Snyk, or similar tools
901
- logger.info("Dependency check - integration with external tools required");
902
-
903
- return {
904
- findings: [],
905
- score: 100,
906
- details: "Dependency check completed - no vulnerabilities found",
907
- };
908
- }
909
-
910
- /**
911
- * Execute configuration check
912
- */
913
- private async executeConfigurationCheck(
914
- check: SecurityCheck,
915
- context: PipelineContext,
916
- ): Promise<{
917
- findings: SecurityFinding[];
918
- score: number;
919
- details: string;
920
- }> {
921
- // Some test mocks provide validateCompliance, others may not. Fall back to compliant=true when unavailable.
922
- let compliance: { compliant: boolean; violations: string[] } = { compliant: true, violations: [] };
923
- try {
924
- const configManager = this.configManager as unknown as Record<string, unknown> & {
925
- validateCompliance?: (env: string) => Promise<{ compliant: boolean; violations: string[] }>;
926
- getSecurityConfig?: () => Record<string, unknown> & { compliant?: boolean; violations?: string[] };
927
- };
928
-
929
- if (typeof configManager.validateCompliance === "function") {
930
- compliance = await configManager.validateCompliance(context.environment);
931
- } else if (typeof configManager.getSecurityConfig === "function") {
932
- // derive basic compliance from config when validateCompliance is not available
933
- const cfg = configManager.getSecurityConfig() || {};
934
- compliance = { compliant: !!cfg.compliant, violations: cfg.violations ?? [] };
935
- }
936
- } catch (_e) {
937
- compliance = { compliant: true, violations: [] };
938
- }
939
-
940
- const findings: SecurityFinding[] = compliance.violations.map((violation, index) => ({
941
- id: `config-${index}`,
942
- severity: "medium" as const,
943
- type: "Configuration",
944
- description: violation,
945
- remediation: "Review security configuration",
946
- }));
947
-
948
- const score = compliance.compliant ? 100 : Math.max(0, 100 - compliance.violations.length * 10);
949
-
950
- return {
951
- findings,
952
- score,
953
- details: `Configuration compliance: ${compliance.compliant ? "compliant" : "non-compliant"}`,
954
- };
955
- }
956
-
957
- /**
958
- * Execute secrets check
959
- */
960
- private async executeSecretsCheck(
961
- check: SecurityCheck,
962
- context: PipelineContext,
963
- ): Promise<{
964
- findings: SecurityFinding[];
965
- score: number;
966
- details: string;
967
- }> {
968
- // This would integrate with tools like TruffleHog, GitLeaks, etc.
969
- logger.info("Secrets check - integration with secret scanning tools required");
970
-
971
- return {
972
- findings: [],
973
- score: 100,
974
- details: "Secrets scan completed - no exposed secrets found",
975
- };
976
- }
977
-
978
- /**
979
- * Execute compliance check
980
- */
981
- private async executeComplianceCheck(
982
- check: SecurityCheck,
983
- context: PipelineContext,
984
- ): Promise<{
985
- findings: SecurityFinding[];
986
- score: number;
987
- details: string;
988
- }> {
989
- const complianceParams = check.parameters as { frameworks?: string[] };
990
- const frameworks: string[] = complianceParams.frameworks ?? ["OWASP", "CWE"];
991
- const findings: SecurityFinding[] = [];
992
-
993
- // Check for compliance with security frameworks
994
- for (const framework of frameworks) {
995
- // This would integrate with compliance checking tools
996
- logger.info(`Checking ${framework} compliance`, { framework });
997
- }
998
-
999
- return {
1000
- findings,
1001
- score: 100,
1002
- details: `Compliance check completed for frameworks: ${frameworks.join(", ")}`,
1003
- };
1004
- }
1005
-
1006
- /**
1007
- * Calculate security score for file findings
1008
- */
1009
- private calculateFileScore(findings: Array<{ severity: string }>): number {
1010
- const severityWeights: Record<string, number> = { critical: 20, high: 10, medium: 5, low: 2, info: 1 };
1011
- const penalty = findings.reduce((sum: number, finding) => {
1012
- return sum + (severityWeights[finding.severity] || 0);
1013
- }, 0);
1014
- return Math.max(0, 100 - penalty);
1015
- }
1016
-
1017
- /**
1018
- * Evaluate gate status based on check results and thresholds
1019
- */
1020
- private evaluateGateStatus(
1021
- gate: SecurityGate,
1022
- checkResults: CheckResult[],
1023
- ): {
1024
- status: "passed" | "failed" | "warning";
1025
- message: string;
1026
- } {
1027
- const allFindings = checkResults.flatMap((result) => result.findings);
1028
-
1029
- const criticalCount = allFindings.filter((f) => f.severity === "critical").length;
1030
- const highCount = allFindings.filter((f) => f.severity === "high").length;
1031
- const mediumCount = allFindings.filter((f) => f.severity === "medium").length;
1032
-
1033
- // Exclude error checks from average score calculation
1034
- const validChecks = checkResults.filter((result) => result.status !== "error");
1035
- const averageScore =
1036
- validChecks.length > 0 ? validChecks.reduce((sum, result) => sum + result.score, 0) / validChecks.length : 100;
1037
-
1038
- // Check thresholds
1039
- if (criticalCount > gate.thresholds.maxCritical) {
1040
- return {
1041
- status: "failed",
1042
- message: `Critical vulnerabilities (${criticalCount}) exceed threshold (${gate.thresholds.maxCritical})`,
1043
- };
1044
- }
1045
-
1046
- if (highCount > gate.thresholds.maxHigh) {
1047
- return {
1048
- status: "failed",
1049
- message: `High-severity vulnerabilities (${highCount}) exceed threshold (${gate.thresholds.maxHigh})`,
1050
- };
1051
- }
1052
-
1053
- if (averageScore < gate.thresholds.minSecurityScore) {
1054
- return {
1055
- status: "failed",
1056
- message: `Security score (${averageScore.toFixed(1)}) below threshold (${gate.thresholds.minSecurityScore})`,
1057
- };
1058
- }
1059
-
1060
- if (mediumCount > gate.thresholds.maxMedium) {
1061
- return {
1062
- status: "warning",
1063
- message: `Medium-severity vulnerabilities (${mediumCount}) exceed threshold (${gate.thresholds.maxMedium})`,
1064
- };
1065
- }
1066
-
1067
- return {
1068
- status: "passed",
1069
- message: "All security checks passed",
1070
- };
1071
- }
1072
-
1073
- /**
1074
- * Generate pipeline security report
1075
- */
1076
- private generatePipelineReport(
1077
- reportId: string,
1078
- stage: string,
1079
- startTime: number,
1080
- status: "passed" | "failed" | "warning",
1081
- gateResults: GateResult[],
1082
- context: PipelineContext,
1083
- ): PipelineSecurityReport {
1084
- const allFindings = gateResults.flatMap((gate) => gate.checks.flatMap((check) => check.findings));
1085
-
1086
- const summary = {
1087
- totalIssues: allFindings.length,
1088
- criticalIssues: allFindings.filter((f) => f.severity === "critical").length,
1089
- highIssues: allFindings.filter((f) => f.severity === "high").length,
1090
- mediumIssues: allFindings.filter((f) => f.severity === "medium").length,
1091
- lowIssues: allFindings.filter((f) => f.severity === "low").length,
1092
- securityScore: this.calculateOverallSecurityScore(gateResults),
1093
- compliance: status === "passed",
1094
- };
1095
-
1096
- const recommendations = this.generateRecommendations(gateResults, summary);
1097
-
1098
- return {
1099
- reportId,
1100
- timestamp: new Date(),
1101
- stage,
1102
- status,
1103
- duration: Date.now() - startTime,
1104
- gates: gateResults,
1105
- summary,
1106
- recommendations,
1107
- artifacts: this.generateArtifacts(reportId, gateResults),
1108
- };
1109
- }
1110
-
1111
- /**
1112
- * Calculate overall security score
1113
- */
1114
- private calculateOverallSecurityScore(gateResults: GateResult[]): number {
1115
- const allChecks = gateResults.flatMap((gate) => gate.checks);
1116
-
1117
- if (allChecks.length === 0) {
1118
- return 100;
1119
- }
1120
-
1121
- const totalScore = allChecks.reduce((sum, check) => sum + check.score, 0);
1122
- return totalScore / allChecks.length;
1123
- }
1124
-
1125
- /**
1126
- * Generate recommendations based on results
1127
- */
1128
- private generateRecommendations(
1129
- gateResults: GateResult[],
1130
- summary: { criticalIssues: number; highIssues: number; securityScore: number; [key: string]: unknown },
1131
- ): string[] {
1132
- const recommendations: string[] = [];
1133
-
1134
- if (summary.criticalIssues > 0) {
1135
- recommendations.push("Address critical security vulnerabilities immediately before deployment");
1136
- }
1137
-
1138
- if (summary.highIssues > 5) {
1139
- recommendations.push("Review and remediate high-severity security issues");
1140
- }
478
+ // --- Private Helpers ---
1141
479
 
1142
- if (summary.securityScore < 80) {
1143
- recommendations.push("Improve overall security posture through code review and security training");
1144
- }
1145
-
1146
- const failedGates = gateResults.filter((gate) => gate.status === "failed");
1147
- if (failedGates.length > 0) {
1148
- recommendations.push(`Review failed security gates: ${failedGates.map((g) => g.gateName).join(", ")}`);
1149
- }
1150
-
1151
- return recommendations;
1152
- }
1153
-
1154
- /**
1155
- * Generate artifacts for the security report
1156
- */
1157
- private generateArtifacts(reportId: string, gateResults: GateResult[]): string[] {
1158
- // In a real implementation, this would generate SARIF files, security reports, etc.
1159
- return [`security-report-${reportId}.json`, `security-findings-${reportId}.sarif`];
1160
- }
1161
-
1162
- /**
1163
- * Create empty report for stages with no gates
1164
- */
1165
- private createEmptyReport(reportId: string, stage: string, startTime: number): PipelineSecurityReport {
480
+ private buildDefaultContext(): PipelineContext {
1166
481
  return {
1167
- reportId,
1168
- timestamp: new Date(),
1169
- stage,
1170
- status: "passed",
1171
- duration: Date.now() - startTime,
1172
- gates: [],
1173
- summary: {
1174
- totalIssues: 0,
1175
- criticalIssues: 0,
1176
- highIssues: 0,
1177
- mediumIssues: 0,
1178
- lowIssues: 0,
1179
- securityScore: 100,
1180
- compliance: true,
1181
- },
1182
- recommendations: [],
1183
- artifacts: [],
1184
- };
482
+ repositoryUrl: this.config.repositoryUrl ?? "",
483
+ branch: this.config.branch ?? "main",
484
+ commit: this.config.commitHash ?? "",
485
+ author: this.config.author ?? "",
486
+ environment: this.config.environment ?? "test",
487
+ buildNumber: this.config.buildNumber ?? "0",
488
+ artifacts: this.config.artifacts ?? [],
489
+ } as PipelineContext;
1185
490
  }
1186
491
 
1187
- /**
1188
- * Initialize default security gates
1189
- */
1190
492
  private initializeDefaultGates(): void {
1191
493
  // Pre-commit gate
1192
494
  this.gates.set("pre-commit", {
@@ -1314,129 +616,4 @@ export class SecurityCIPipeline {
1314
616
  exceptions: [],
1315
617
  });
1316
618
  }
1317
-
1318
- /**
1319
- * Get security gate configuration
1320
- */
1321
- getSecurityGate(gateId: string): SecurityGate | null {
1322
- return this.gates.get(gateId) || null;
1323
- }
1324
-
1325
- /**
1326
- * Update security gate configuration
1327
- */
1328
- updateSecurityGate(gateId: string, updates: Partial<SecurityGate>): boolean {
1329
- const gate = this.gates.get(gateId);
1330
- if (!gate) {
1331
- return false;
1332
- }
1333
-
1334
- const updatedGate = { ...gate, ...updates, id: gateId };
1335
- this.gates.set(gateId, updatedGate);
1336
-
1337
- logger.info(`Updated security gate: ${updatedGate.name}`, { gateName: updatedGate.name });
1338
- return true;
1339
- }
1340
-
1341
- /**
1342
- * Get pipeline reports
1343
- */
1344
- getReports(
1345
- options: {
1346
- stage?: string;
1347
- status?: string;
1348
- since?: Date;
1349
- limit?: number;
1350
- } = {},
1351
- ): PipelineSecurityReport[] {
1352
- let reports = [...this.reports];
1353
-
1354
- if (options.stage) {
1355
- reports = reports.filter((r) => r.stage === options.stage);
1356
- }
1357
-
1358
- if (options.status) {
1359
- reports = reports.filter((r) => r.status === options.status);
1360
- }
1361
-
1362
- if (options.since) {
1363
- reports = reports.filter((r) => r.timestamp >= options.since!);
1364
- }
1365
-
1366
- // Sort by timestamp (newest first)
1367
- reports.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
1368
-
1369
- if (options.limit) {
1370
- reports = reports.slice(0, options.limit);
1371
- }
1372
-
1373
- return reports;
1374
- }
1375
-
1376
- /**
1377
- * Get pipeline statistics
1378
- */
1379
- getStatistics(): {
1380
- totalReports: number;
1381
- passRate: number;
1382
- averageSecurityScore: number;
1383
- mostCommonIssues: { type: string; count: number }[];
1384
- gatePerformance: { gateId: string; successRate: number; averageDuration: number }[];
1385
- } {
1386
- const totalReports = this.reports.length;
1387
- const passedReports = this.reports.filter((r) => r.status === "passed").length;
1388
- const passRate = totalReports > 0 ? passedReports / totalReports : 1;
1389
-
1390
- const averageSecurityScore =
1391
- totalReports > 0 ? this.reports.reduce((sum, r) => sum + r.summary.securityScore, 0) / totalReports : 100;
1392
-
1393
- // Count issue types
1394
- const issueTypes: Record<string, number> = {};
1395
- this.reports.forEach((report) => {
1396
- report.gates.forEach((gate) => {
1397
- gate.checks.forEach((check) => {
1398
- check.findings.forEach((finding) => {
1399
- issueTypes[finding.type] = (issueTypes[finding.type] || 0) + 1;
1400
- });
1401
- });
1402
- });
1403
- });
1404
-
1405
- const mostCommonIssues = Object.entries(issueTypes)
1406
- .map(([type, count]) => ({ type, count }))
1407
- .sort((a, b) => b.count - a.count)
1408
- .slice(0, 5);
1409
-
1410
- // Calculate gate performance
1411
- const gateStats: Record<string, { total: number; passed: number; totalDuration: number }> = {};
1412
-
1413
- this.reports.forEach((report) => {
1414
- report.gates.forEach((gate) => {
1415
- if (!gateStats[gate.gateId]) {
1416
- gateStats[gate.gateId] = { total: 0, passed: 0, totalDuration: 0 };
1417
- }
1418
-
1419
- gateStats[gate.gateId].total++;
1420
- gateStats[gate.gateId].totalDuration += gate.duration;
1421
-
1422
- if (gate.status === "passed") {
1423
- gateStats[gate.gateId].passed++;
1424
- }
1425
- });
1426
- });
1427
-
1428
- const gatePerformance = Object.entries(gateStats).map(([gateId, stats]) => ({
1429
- gateId,
1430
- successRate: stats.total > 0 ? stats.passed / stats.total : 0,
1431
- averageDuration: stats.total > 0 ? stats.totalDuration / stats.total : 0,
1432
- }));
1433
-
1434
- return {
1435
- totalReports,
1436
- passRate,
1437
- averageSecurityScore,
1438
- mostCommonIssues,
1439
- gatePerformance,
1440
- };
1441
- }
1442
619
  }