@avi770/testteam 1.2.0 → 2.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 (300) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/README.md +53 -6
  3. package/agents/24-signup-onboarding-tester.ts +429 -0
  4. package/agents/25-crud-flow-tester.ts +302 -0
  5. package/agents/26-form-validator.ts +297 -0
  6. package/agents/27-search-filter-tester.ts +326 -0
  7. package/agents/28-navigation-routing-tester.ts +425 -0
  8. package/agents/29-responsive-interaction-tester.ts +350 -0
  9. package/agents/30-multi-user-scenario-tester.ts +319 -0
  10. package/agents/31-load-tester.ts +134 -0
  11. package/agents/32-memory-leak-detector.ts +194 -0
  12. package/agents/33-bundle-analyzer.ts +132 -0
  13. package/agents/34-xss-scanner.ts +191 -0
  14. package/agents/35-csrf-tester.ts +82 -0
  15. package/agents/36-auth-fuzzer.ts +194 -0
  16. package/agents/37-dependency-scanner.ts +176 -0
  17. package/agents/38-secrets-scanner.ts +137 -0
  18. package/agents/39-api-contract-tester.ts +199 -0
  19. package/agents/40-rate-limit-tester.ts +94 -0
  20. package/agents/41-api-pagination-tester.ts +97 -0
  21. package/agents/42-graphql-tester.ts +222 -0
  22. package/agents/43-data-consistency-checker.ts +205 -0
  23. package/agents/44-backup-recovery-tester.ts +152 -0
  24. package/agents/45-data-privacy-scanner.ts +125 -0
  25. package/agents/46-seo-auditor.ts +294 -0
  26. package/agents/47-social-preview-tester.ts +232 -0
  27. package/agents/48-lighthouse-auditor.ts +213 -0
  28. package/agents/49-i18n-tester.ts +198 -0
  29. package/agents/50-timezone-tester.ts +173 -0
  30. package/agents/51-error-recovery-tester.ts +155 -0
  31. package/agents/52-offline-mode-tester.ts +180 -0
  32. package/agents/53-graceful-degradation-tester.ts +156 -0
  33. package/agents/54-websocket-tester.ts +151 -0
  34. package/agents/55-realtime-sync-tester.ts +194 -0
  35. package/agents/56-file-upload-tester.ts +194 -0
  36. package/agents/57-export-tester.ts +174 -0
  37. package/agents/58-payment-flow-tester.ts +183 -0
  38. package/agents/59-ssl-tls-auditor.ts +141 -0
  39. package/agents/60-dns-cdn-tester.ts +117 -0
  40. package/agents/61-docker-health-checker.ts +111 -0
  41. package/agents/62-env-config-validator.ts +152 -0
  42. package/agents/63-log-quality-auditor.ts +136 -0
  43. package/agents/64-analytics-tracker-tester.ts +165 -0
  44. package/agents/65-gdpr-compliance-tester.ts +215 -0
  45. package/agents/66-soc2-control-validator.ts +210 -0
  46. package/agents/67-wcag-aaa-tester.ts +241 -0
  47. package/agents/68-dead-code-detector.ts +135 -0
  48. package/agents/69-type-safety-auditor.ts +164 -0
  49. package/agents/70-complexity-analyzer.ts +179 -0
  50. package/agents/__tests__/24-signup-onboarding-tester.test.ts +274 -0
  51. package/agents/__tests__/25-crud-flow-tester.test.ts +322 -0
  52. package/agents/__tests__/26-form-validator.test.ts +345 -0
  53. package/agents/__tests__/27-search-filter-tester.test.ts +311 -0
  54. package/agents/__tests__/28-navigation-routing-tester.test.ts +328 -0
  55. package/agents/__tests__/29-responsive-interaction-tester.test.ts +297 -0
  56. package/agents/__tests__/30-multi-user-scenario-tester.test.ts +328 -0
  57. package/agents/__tests__/31-load-tester.test.ts +189 -0
  58. package/agents/__tests__/32-memory-leak-detector.test.ts +251 -0
  59. package/agents/__tests__/33-bundle-analyzer.test.ts +237 -0
  60. package/agents/__tests__/34-xss-scanner.test.ts +258 -0
  61. package/agents/__tests__/35-csrf-tester.test.ts +200 -0
  62. package/agents/__tests__/36-auth-fuzzer.test.ts +214 -0
  63. package/agents/__tests__/37-dependency-scanner.test.ts +266 -0
  64. package/agents/__tests__/38-secrets-scanner.test.ts +224 -0
  65. package/agents/__tests__/39-api-contract-tester.test.ts +312 -0
  66. package/agents/__tests__/40-rate-limit-tester.test.ts +192 -0
  67. package/agents/__tests__/41-api-pagination-tester.test.ts +198 -0
  68. package/agents/__tests__/42-graphql-tester.test.ts +252 -0
  69. package/agents/__tests__/43-data-consistency-checker.test.ts +232 -0
  70. package/agents/__tests__/44-backup-recovery-tester.test.ts +222 -0
  71. package/agents/__tests__/45-data-privacy-scanner.test.ts +223 -0
  72. package/agents/__tests__/46-seo-auditor.test.ts +261 -0
  73. package/agents/__tests__/47-social-preview-tester.test.ts +245 -0
  74. package/agents/__tests__/48-lighthouse-auditor.test.ts +276 -0
  75. package/agents/__tests__/49-i18n-tester.test.ts +201 -0
  76. package/agents/__tests__/50-timezone-tester.test.ts +172 -0
  77. package/agents/__tests__/51-error-recovery-tester.test.ts +162 -0
  78. package/agents/__tests__/52-offline-mode-tester.test.ts +164 -0
  79. package/agents/__tests__/53-graceful-degradation-tester.test.ts +168 -0
  80. package/agents/__tests__/54-websocket-tester.test.ts +157 -0
  81. package/agents/__tests__/55-realtime-sync-tester.test.ts +181 -0
  82. package/agents/__tests__/56-file-upload-tester.test.ts +172 -0
  83. package/agents/__tests__/57-export-tester.test.ts +169 -0
  84. package/agents/__tests__/58-payment-flow-tester.test.ts +182 -0
  85. package/agents/__tests__/59-ssl-tls-auditor.test.ts +179 -0
  86. package/agents/__tests__/60-dns-cdn-tester.test.ts +176 -0
  87. package/agents/__tests__/61-docker-health-checker.test.ts +150 -0
  88. package/agents/__tests__/62-env-config-validator.test.ts +166 -0
  89. package/agents/__tests__/63-log-quality-auditor.test.ts +175 -0
  90. package/agents/__tests__/64-analytics-tracker-tester.test.ts +158 -0
  91. package/agents/__tests__/65-gdpr-compliance-tester.test.ts +174 -0
  92. package/agents/__tests__/66-soc2-control-validator.test.ts +183 -0
  93. package/agents/__tests__/67-wcag-aaa-tester.test.ts +190 -0
  94. package/agents/__tests__/68-dead-code-detector.test.ts +174 -0
  95. package/agents/__tests__/69-type-safety-auditor.test.ts +173 -0
  96. package/agents/__tests__/70-complexity-analyzer.test.ts +177 -0
  97. package/agents/__tests__/registry.test.ts +13 -13
  98. package/agents/registry.ts +146 -5
  99. package/core/__tests__/integration.test.ts +4 -4
  100. package/core/__tests__/orchestrator.test.ts +17 -16
  101. package/core/license.ts +208 -211
  102. package/core/orchestrator.ts +6 -4
  103. package/dist/agents/24-signup-onboarding-tester.d.ts +35 -0
  104. package/dist/agents/24-signup-onboarding-tester.d.ts.map +1 -0
  105. package/dist/agents/24-signup-onboarding-tester.js +357 -0
  106. package/dist/agents/24-signup-onboarding-tester.js.map +1 -0
  107. package/dist/agents/25-crud-flow-tester.d.ts +11 -0
  108. package/dist/agents/25-crud-flow-tester.d.ts.map +1 -0
  109. package/dist/agents/25-crud-flow-tester.js +253 -0
  110. package/dist/agents/25-crud-flow-tester.js.map +1 -0
  111. package/dist/agents/26-form-validator.d.ts +12 -0
  112. package/dist/agents/26-form-validator.d.ts.map +1 -0
  113. package/dist/agents/26-form-validator.js +257 -0
  114. package/dist/agents/26-form-validator.js.map +1 -0
  115. package/dist/agents/27-search-filter-tester.d.ts +20 -0
  116. package/dist/agents/27-search-filter-tester.d.ts.map +1 -0
  117. package/dist/agents/27-search-filter-tester.js +267 -0
  118. package/dist/agents/27-search-filter-tester.js.map +1 -0
  119. package/dist/agents/28-navigation-routing-tester.d.ts +32 -0
  120. package/dist/agents/28-navigation-routing-tester.d.ts.map +1 -0
  121. package/dist/agents/28-navigation-routing-tester.js +363 -0
  122. package/dist/agents/28-navigation-routing-tester.js.map +1 -0
  123. package/dist/agents/29-responsive-interaction-tester.d.ts +26 -0
  124. package/dist/agents/29-responsive-interaction-tester.d.ts.map +1 -0
  125. package/dist/agents/29-responsive-interaction-tester.js +272 -0
  126. package/dist/agents/29-responsive-interaction-tester.js.map +1 -0
  127. package/dist/agents/30-multi-user-scenario-tester.d.ts +24 -0
  128. package/dist/agents/30-multi-user-scenario-tester.d.ts.map +1 -0
  129. package/dist/agents/30-multi-user-scenario-tester.js +254 -0
  130. package/dist/agents/30-multi-user-scenario-tester.js.map +1 -0
  131. package/dist/agents/31-load-tester.d.ts +12 -0
  132. package/dist/agents/31-load-tester.d.ts.map +1 -0
  133. package/dist/agents/31-load-tester.js +110 -0
  134. package/dist/agents/31-load-tester.js.map +1 -0
  135. package/dist/agents/32-memory-leak-detector.d.ts +12 -0
  136. package/dist/agents/32-memory-leak-detector.d.ts.map +1 -0
  137. package/dist/agents/32-memory-leak-detector.js +167 -0
  138. package/dist/agents/32-memory-leak-detector.js.map +1 -0
  139. package/dist/agents/33-bundle-analyzer.d.ts +10 -0
  140. package/dist/agents/33-bundle-analyzer.d.ts.map +1 -0
  141. package/dist/agents/33-bundle-analyzer.js +111 -0
  142. package/dist/agents/33-bundle-analyzer.js.map +1 -0
  143. package/dist/agents/34-xss-scanner.d.ts +11 -0
  144. package/dist/agents/34-xss-scanner.d.ts.map +1 -0
  145. package/dist/agents/34-xss-scanner.js +164 -0
  146. package/dist/agents/34-xss-scanner.js.map +1 -0
  147. package/dist/agents/35-csrf-tester.d.ts +11 -0
  148. package/dist/agents/35-csrf-tester.d.ts.map +1 -0
  149. package/dist/agents/35-csrf-tester.js +70 -0
  150. package/dist/agents/35-csrf-tester.js.map +1 -0
  151. package/dist/agents/36-auth-fuzzer.d.ts +13 -0
  152. package/dist/agents/36-auth-fuzzer.d.ts.map +1 -0
  153. package/dist/agents/36-auth-fuzzer.js +163 -0
  154. package/dist/agents/36-auth-fuzzer.js.map +1 -0
  155. package/dist/agents/37-dependency-scanner.d.ts +11 -0
  156. package/dist/agents/37-dependency-scanner.d.ts.map +1 -0
  157. package/dist/agents/37-dependency-scanner.js +139 -0
  158. package/dist/agents/37-dependency-scanner.js.map +1 -0
  159. package/dist/agents/38-secrets-scanner.d.ts +11 -0
  160. package/dist/agents/38-secrets-scanner.d.ts.map +1 -0
  161. package/dist/agents/38-secrets-scanner.js +116 -0
  162. package/dist/agents/38-secrets-scanner.js.map +1 -0
  163. package/dist/agents/39-api-contract-tester.d.ts +12 -0
  164. package/dist/agents/39-api-contract-tester.d.ts.map +1 -0
  165. package/dist/agents/39-api-contract-tester.js +142 -0
  166. package/dist/agents/39-api-contract-tester.js.map +1 -0
  167. package/dist/agents/40-rate-limit-tester.d.ts +12 -0
  168. package/dist/agents/40-rate-limit-tester.d.ts.map +1 -0
  169. package/dist/agents/40-rate-limit-tester.js +79 -0
  170. package/dist/agents/40-rate-limit-tester.js.map +1 -0
  171. package/dist/agents/41-api-pagination-tester.d.ts +12 -0
  172. package/dist/agents/41-api-pagination-tester.d.ts.map +1 -0
  173. package/dist/agents/41-api-pagination-tester.js +79 -0
  174. package/dist/agents/41-api-pagination-tester.js.map +1 -0
  175. package/dist/agents/42-graphql-tester.d.ts +13 -0
  176. package/dist/agents/42-graphql-tester.d.ts.map +1 -0
  177. package/dist/agents/42-graphql-tester.js +187 -0
  178. package/dist/agents/42-graphql-tester.js.map +1 -0
  179. package/dist/agents/43-data-consistency-checker.d.ts +11 -0
  180. package/dist/agents/43-data-consistency-checker.d.ts.map +1 -0
  181. package/dist/agents/43-data-consistency-checker.js +176 -0
  182. package/dist/agents/43-data-consistency-checker.js.map +1 -0
  183. package/dist/agents/44-backup-recovery-tester.d.ts +11 -0
  184. package/dist/agents/44-backup-recovery-tester.d.ts.map +1 -0
  185. package/dist/agents/44-backup-recovery-tester.js +128 -0
  186. package/dist/agents/44-backup-recovery-tester.js.map +1 -0
  187. package/dist/agents/45-data-privacy-scanner.d.ts +11 -0
  188. package/dist/agents/45-data-privacy-scanner.d.ts.map +1 -0
  189. package/dist/agents/45-data-privacy-scanner.js +100 -0
  190. package/dist/agents/45-data-privacy-scanner.js.map +1 -0
  191. package/dist/agents/46-seo-auditor.d.ts +12 -0
  192. package/dist/agents/46-seo-auditor.d.ts.map +1 -0
  193. package/dist/agents/46-seo-auditor.js +275 -0
  194. package/dist/agents/46-seo-auditor.js.map +1 -0
  195. package/dist/agents/47-social-preview-tester.d.ts +11 -0
  196. package/dist/agents/47-social-preview-tester.d.ts.map +1 -0
  197. package/dist/agents/47-social-preview-tester.js +219 -0
  198. package/dist/agents/47-social-preview-tester.js.map +1 -0
  199. package/dist/agents/48-lighthouse-auditor.d.ts +11 -0
  200. package/dist/agents/48-lighthouse-auditor.d.ts.map +1 -0
  201. package/dist/agents/48-lighthouse-auditor.js +192 -0
  202. package/dist/agents/48-lighthouse-auditor.js.map +1 -0
  203. package/dist/agents/49-i18n-tester.d.ts +13 -0
  204. package/dist/agents/49-i18n-tester.d.ts.map +1 -0
  205. package/dist/agents/49-i18n-tester.js +172 -0
  206. package/dist/agents/49-i18n-tester.js.map +1 -0
  207. package/dist/agents/50-timezone-tester.d.ts +11 -0
  208. package/dist/agents/50-timezone-tester.d.ts.map +1 -0
  209. package/dist/agents/50-timezone-tester.js +152 -0
  210. package/dist/agents/50-timezone-tester.js.map +1 -0
  211. package/dist/agents/51-error-recovery-tester.d.ts +11 -0
  212. package/dist/agents/51-error-recovery-tester.d.ts.map +1 -0
  213. package/dist/agents/51-error-recovery-tester.js +134 -0
  214. package/dist/agents/51-error-recovery-tester.js.map +1 -0
  215. package/dist/agents/52-offline-mode-tester.d.ts +12 -0
  216. package/dist/agents/52-offline-mode-tester.d.ts.map +1 -0
  217. package/dist/agents/52-offline-mode-tester.js +161 -0
  218. package/dist/agents/52-offline-mode-tester.js.map +1 -0
  219. package/dist/agents/53-graceful-degradation-tester.d.ts +12 -0
  220. package/dist/agents/53-graceful-degradation-tester.d.ts.map +1 -0
  221. package/dist/agents/53-graceful-degradation-tester.js +130 -0
  222. package/dist/agents/53-graceful-degradation-tester.js.map +1 -0
  223. package/dist/agents/54-websocket-tester.d.ts +10 -0
  224. package/dist/agents/54-websocket-tester.d.ts.map +1 -0
  225. package/dist/agents/54-websocket-tester.js +132 -0
  226. package/dist/agents/54-websocket-tester.js.map +1 -0
  227. package/dist/agents/55-realtime-sync-tester.d.ts +11 -0
  228. package/dist/agents/55-realtime-sync-tester.d.ts.map +1 -0
  229. package/dist/agents/55-realtime-sync-tester.js +175 -0
  230. package/dist/agents/55-realtime-sync-tester.js.map +1 -0
  231. package/dist/agents/56-file-upload-tester.d.ts +12 -0
  232. package/dist/agents/56-file-upload-tester.d.ts.map +1 -0
  233. package/dist/agents/56-file-upload-tester.js +166 -0
  234. package/dist/agents/56-file-upload-tester.js.map +1 -0
  235. package/dist/agents/57-export-tester.d.ts +11 -0
  236. package/dist/agents/57-export-tester.d.ts.map +1 -0
  237. package/dist/agents/57-export-tester.js +155 -0
  238. package/dist/agents/57-export-tester.js.map +1 -0
  239. package/dist/agents/58-payment-flow-tester.d.ts +11 -0
  240. package/dist/agents/58-payment-flow-tester.d.ts.map +1 -0
  241. package/dist/agents/58-payment-flow-tester.js +159 -0
  242. package/dist/agents/58-payment-flow-tester.js.map +1 -0
  243. package/dist/agents/59-ssl-tls-auditor.d.ts +10 -0
  244. package/dist/agents/59-ssl-tls-auditor.d.ts.map +1 -0
  245. package/dist/agents/59-ssl-tls-auditor.js +132 -0
  246. package/dist/agents/59-ssl-tls-auditor.js.map +1 -0
  247. package/dist/agents/60-dns-cdn-tester.d.ts +10 -0
  248. package/dist/agents/60-dns-cdn-tester.d.ts.map +1 -0
  249. package/dist/agents/60-dns-cdn-tester.js +105 -0
  250. package/dist/agents/60-dns-cdn-tester.js.map +1 -0
  251. package/dist/agents/61-docker-health-checker.d.ts +10 -0
  252. package/dist/agents/61-docker-health-checker.d.ts.map +1 -0
  253. package/dist/agents/61-docker-health-checker.js +95 -0
  254. package/dist/agents/61-docker-health-checker.js.map +1 -0
  255. package/dist/agents/62-env-config-validator.d.ts +10 -0
  256. package/dist/agents/62-env-config-validator.d.ts.map +1 -0
  257. package/dist/agents/62-env-config-validator.js +132 -0
  258. package/dist/agents/62-env-config-validator.js.map +1 -0
  259. package/dist/agents/63-log-quality-auditor.d.ts +9 -0
  260. package/dist/agents/63-log-quality-auditor.d.ts.map +1 -0
  261. package/dist/agents/63-log-quality-auditor.js +121 -0
  262. package/dist/agents/63-log-quality-auditor.js.map +1 -0
  263. package/dist/agents/64-analytics-tracker-tester.d.ts +10 -0
  264. package/dist/agents/64-analytics-tracker-tester.d.ts.map +1 -0
  265. package/dist/agents/64-analytics-tracker-tester.js +146 -0
  266. package/dist/agents/64-analytics-tracker-tester.js.map +1 -0
  267. package/dist/agents/65-gdpr-compliance-tester.d.ts +13 -0
  268. package/dist/agents/65-gdpr-compliance-tester.d.ts.map +1 -0
  269. package/dist/agents/65-gdpr-compliance-tester.js +186 -0
  270. package/dist/agents/65-gdpr-compliance-tester.js.map +1 -0
  271. package/dist/agents/66-soc2-control-validator.d.ts +14 -0
  272. package/dist/agents/66-soc2-control-validator.d.ts.map +1 -0
  273. package/dist/agents/66-soc2-control-validator.js +178 -0
  274. package/dist/agents/66-soc2-control-validator.js.map +1 -0
  275. package/dist/agents/67-wcag-aaa-tester.d.ts +13 -0
  276. package/dist/agents/67-wcag-aaa-tester.d.ts.map +1 -0
  277. package/dist/agents/67-wcag-aaa-tester.js +207 -0
  278. package/dist/agents/67-wcag-aaa-tester.js.map +1 -0
  279. package/dist/agents/68-dead-code-detector.d.ts +9 -0
  280. package/dist/agents/68-dead-code-detector.d.ts.map +1 -0
  281. package/dist/agents/68-dead-code-detector.js +116 -0
  282. package/dist/agents/68-dead-code-detector.js.map +1 -0
  283. package/dist/agents/69-type-safety-auditor.d.ts +9 -0
  284. package/dist/agents/69-type-safety-auditor.d.ts.map +1 -0
  285. package/dist/agents/69-type-safety-auditor.js +148 -0
  286. package/dist/agents/69-type-safety-auditor.js.map +1 -0
  287. package/dist/agents/70-complexity-analyzer.d.ts +10 -0
  288. package/dist/agents/70-complexity-analyzer.d.ts.map +1 -0
  289. package/dist/agents/70-complexity-analyzer.js +154 -0
  290. package/dist/agents/70-complexity-analyzer.js.map +1 -0
  291. package/dist/agents/registry.d.ts +3 -3
  292. package/dist/agents/registry.d.ts.map +1 -1
  293. package/dist/agents/registry.js +146 -5
  294. package/dist/agents/registry.js.map +1 -1
  295. package/dist/core/license.js +2 -5
  296. package/dist/core/license.js.map +1 -1
  297. package/dist/core/orchestrator.d.ts.map +1 -1
  298. package/dist/core/orchestrator.js +6 -4
  299. package/dist/core/orchestrator.js.map +1 -1
  300. package/package.json +2 -2
@@ -0,0 +1,134 @@
1
+ import { BaseAgent } from './base-agent';
2
+ import type { Finding } from '../core/types';
3
+ import { resolveEnvironment } from '../helpers/env-resolver';
4
+
5
+ /** Default API endpoints to test when no route scanning is available. */
6
+ const DEFAULT_ENDPOINTS = [
7
+ '/api/auth/login',
8
+ '/api/tasks',
9
+ '/api/clients',
10
+ '/api/dashboard',
11
+ '/api/users',
12
+ ] as const;
13
+
14
+ /** Timeout (ms) for individual requests during load testing. */
15
+ const REQUEST_TIMEOUT_MS = 10_000;
16
+
17
+ interface TimingResult {
18
+ endpoint: string;
19
+ durations: number[];
20
+ errors: number;
21
+ }
22
+
23
+ function percentile(sorted: number[], p: number): number {
24
+ if (sorted.length === 0) return 0;
25
+ const idx = Math.ceil((p / 100) * sorted.length) - 1;
26
+ return sorted[Math.max(0, idx)];
27
+ }
28
+
29
+ export class LoadTesterAgent extends BaseAgent {
30
+ readonly agentId = 31;
31
+ readonly agentName = 'Load Tester';
32
+ private baseUrl = '';
33
+
34
+ protected async preFlight(): Promise<void> {
35
+ const { env } = await resolveEnvironment(this.config, this.phase);
36
+ this.baseUrl = env.baseUrl;
37
+ }
38
+
39
+ protected async execute(): Promise<Finding[]> {
40
+ const findings: Finding[] = [];
41
+ const endpoints = this.discoverEndpoints();
42
+
43
+ // Phase 1: 100 concurrent requests
44
+ for (const endpoint of endpoints) {
45
+ const timing = await this.runConcurrent(endpoint, 100);
46
+ const sorted = [...timing.durations].sort((a, b) => a - b);
47
+ const avg = sorted.length > 0 ? sorted.reduce((s, v) => s + v, 0) / sorted.length : 0;
48
+ const p95 = percentile(sorted, 95);
49
+ const p99 = percentile(sorted, 99);
50
+
51
+ this.addEvidence({
52
+ type: 'log',
53
+ path: `load-test-100-${endpoint.replace(/\//g, '_')}`,
54
+ description: `100 concurrent: avg=${avg.toFixed(0)}ms p95=${p95.toFixed(0)}ms p99=${p99.toFixed(0)}ms errors=${timing.errors}`,
55
+ });
56
+
57
+ if (p95 > 500) {
58
+ findings.push({
59
+ id: `${this.agentId}-slow-p95-100-${endpoint.replace(/\//g, '_')}`,
60
+ type: 'test-bug',
61
+ severity: 'medium',
62
+ agentId: this.agentId,
63
+ module: 'load-test',
64
+ description: `Endpoint ${endpoint} p95=${p95.toFixed(0)}ms exceeds 500ms threshold at 100 concurrent requests (avg=${avg.toFixed(0)}ms, p99=${p99.toFixed(0)}ms)`,
65
+ });
66
+ }
67
+ }
68
+
69
+ // Phase 2: 500 concurrent requests
70
+ for (const endpoint of endpoints) {
71
+ const timing = await this.runConcurrent(endpoint, 500);
72
+ const sorted = [...timing.durations].sort((a, b) => a - b);
73
+ const avg = sorted.length > 0 ? sorted.reduce((s, v) => s + v, 0) / sorted.length : 0;
74
+ const p95 = percentile(sorted, 95);
75
+ const p99 = percentile(sorted, 99);
76
+
77
+ this.addEvidence({
78
+ type: 'log',
79
+ path: `load-test-500-${endpoint.replace(/\//g, '_')}`,
80
+ description: `500 concurrent: avg=${avg.toFixed(0)}ms p95=${p95.toFixed(0)}ms p99=${p99.toFixed(0)}ms errors=${timing.errors}`,
81
+ });
82
+
83
+ if (p95 > 1000) {
84
+ findings.push({
85
+ id: `${this.agentId}-slow-p95-500-${endpoint.replace(/\//g, '_')}`,
86
+ type: 'test-bug',
87
+ severity: 'high',
88
+ agentId: this.agentId,
89
+ module: 'load-test',
90
+ description: `Endpoint ${endpoint} p95=${p95.toFixed(0)}ms exceeds 1000ms threshold at 500 concurrent requests (avg=${avg.toFixed(0)}ms, p99=${p99.toFixed(0)}ms)`,
91
+ });
92
+ }
93
+ }
94
+
95
+ return findings;
96
+ }
97
+
98
+ private discoverEndpoints(): string[] {
99
+ // Use configured modules to derive API endpoints, or fall back to defaults
100
+ if (this.config.modules && this.config.modules.length > 0) {
101
+ return this.config.modules.map(m => `/api${m.route}`);
102
+ }
103
+ return [...DEFAULT_ENDPOINTS];
104
+ }
105
+
106
+ private async runConcurrent(endpoint: string, count: number): Promise<TimingResult> {
107
+ const url = `${this.baseUrl}${endpoint}`;
108
+ const durations: number[] = [];
109
+ let errors = 0;
110
+
111
+ const promises = Array.from({ length: count }, async () => {
112
+ const start = Date.now();
113
+ try {
114
+ await Promise.race([
115
+ fetch(url, {
116
+ method: 'GET',
117
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
118
+ }),
119
+ new Promise<never>((_, reject) =>
120
+ setTimeout(() => reject(new Error('Load test timeout')), REQUEST_TIMEOUT_MS),
121
+ ),
122
+ ]);
123
+ durations.push(Date.now() - start);
124
+ } catch {
125
+ errors++;
126
+ durations.push(Date.now() - start);
127
+ }
128
+ });
129
+
130
+ await Promise.all(promises);
131
+
132
+ return { endpoint, durations, errors };
133
+ }
134
+ }
@@ -0,0 +1,194 @@
1
+ import type { Browser, Page } from 'playwright';
2
+ import type { Finding } from '../core/types';
3
+ import { BaseAgent } from './base-agent';
4
+ import { resolveEnvironment } from '../helpers/env-resolver';
5
+ import { login } from '../helpers/navigation';
6
+
7
+ /** Threshold in bytes for heap growth. */
8
+ const MEDIUM_THRESHOLD_BYTES = 5 * 1024 * 1024; // 5MB
9
+ const HIGH_THRESHOLD_BYTES = 10 * 1024 * 1024; // 10MB
10
+ const NAV_TIMEOUT_MS = 5_000;
11
+ const ITERATION_COUNT = 10;
12
+ const MAX_MODULES = 5;
13
+
14
+ export class MemoryLeakDetectorAgent extends BaseAgent {
15
+ readonly agentId = 32;
16
+ readonly agentName = 'Memory Leak Detector';
17
+ private baseUrl = '';
18
+
19
+ protected async preFlight(): Promise<void> {
20
+ if (!this.config.modules || this.config.modules.length === 0) {
21
+ throw new Error('MemoryLeakDetectorAgent requires at least one module in config.modules');
22
+ }
23
+ const { env } = await resolveEnvironment(this.config, this.phase);
24
+ this.baseUrl = env.baseUrl;
25
+ }
26
+
27
+ protected async execute(): Promise<Finding[]> {
28
+ const findings: Finding[] = [];
29
+ const rawLoginUrl = this.config.auth.loginUrl ?? '/login';
30
+ const loginUrl = rawLoginUrl.startsWith('http') ? rawLoginUrl : `${this.baseUrl}${rawLoginUrl}`;
31
+ const credentials =
32
+ this.config.auth.credentials?.['admin'] ??
33
+ Object.values(this.config.auth.credentials ?? {})[0];
34
+
35
+ const { chromium } = await import('playwright');
36
+ let browser: Browser | null = null;
37
+
38
+ try {
39
+ browser = await chromium.launch({ headless: true });
40
+ const page = await browser.newPage();
41
+
42
+ if (credentials) {
43
+ try {
44
+ await login(page, credentials, loginUrl);
45
+ } catch (loginError) {
46
+ findings.push({
47
+ id: `${this.agentId}-login-failed`,
48
+ type: 'infra-issue',
49
+ severity: 'medium',
50
+ agentId: this.agentId,
51
+ module: 'memory-leak-detector',
52
+ description: `Login failed — continuing anonymously: ${loginError instanceof Error ? loginError.message.split('\n')[0] : String(loginError)}`,
53
+ });
54
+ }
55
+ }
56
+
57
+ const modules = this.config.modules.slice(0, MAX_MODULES);
58
+
59
+ for (const mod of modules) {
60
+ const moduleFindings = await this.testModuleMemory(page, mod.id, mod.route);
61
+ findings.push(...moduleFindings);
62
+ }
63
+
64
+ await page.close().catch(() => undefined);
65
+ } catch (playwrightError) {
66
+ findings.push({
67
+ id: `${this.agentId}-playwright-failure`,
68
+ type: 'infra-issue',
69
+ severity: 'medium',
70
+ agentId: this.agentId,
71
+ module: 'memory-leak-detector',
72
+ description: `Playwright execution failed: ${playwrightError instanceof Error ? playwrightError.message.split('\n')[0] : String(playwrightError)}`,
73
+ });
74
+ } finally {
75
+ if (browser) await browser.close().catch(() => undefined);
76
+ }
77
+
78
+ return findings;
79
+ }
80
+
81
+ private async testModuleMemory(
82
+ page: Page,
83
+ moduleId: string,
84
+ moduleRoute: string,
85
+ ): Promise<Finding[]> {
86
+ const findings: Finding[] = [];
87
+ const fullUrl = moduleRoute.startsWith('http')
88
+ ? moduleRoute
89
+ : `${this.baseUrl}${moduleRoute}`;
90
+
91
+ try {
92
+ await Promise.race([
93
+ page.goto(fullUrl, { waitUntil: 'domcontentloaded' }),
94
+ new Promise((_, reject) =>
95
+ setTimeout(() => reject(new Error('Navigation timeout')), NAV_TIMEOUT_MS),
96
+ ),
97
+ ]);
98
+ } catch {
99
+ findings.push({
100
+ id: `${this.agentId}-nav-failed-${moduleId}`,
101
+ type: 'test-bug',
102
+ severity: 'medium',
103
+ agentId: this.agentId,
104
+ module: moduleId,
105
+ description: `Failed to navigate to module ${moduleId} at ${moduleRoute}`,
106
+ });
107
+ return findings;
108
+ }
109
+
110
+ // Record initial heap size
111
+ const initialHeap = await this.getHeapSize(page);
112
+ if (initialHeap === null) {
113
+ findings.push({
114
+ id: `${this.agentId}-no-memory-api-${moduleId}`,
115
+ type: 'infra-issue',
116
+ severity: 'low',
117
+ agentId: this.agentId,
118
+ module: moduleId,
119
+ description: `performance.memory API not available for module ${moduleId}`,
120
+ });
121
+ return findings;
122
+ }
123
+
124
+ // Navigate away and back N times
125
+ for (let i = 0; i < ITERATION_COUNT; i++) {
126
+ try {
127
+ await Promise.race([
128
+ page.goto('about:blank', { waitUntil: 'domcontentloaded' }),
129
+ new Promise((_, reject) =>
130
+ setTimeout(() => reject(new Error('Timeout')), NAV_TIMEOUT_MS),
131
+ ),
132
+ ]);
133
+ await Promise.race([
134
+ page.goto(fullUrl, { waitUntil: 'domcontentloaded' }),
135
+ new Promise((_, reject) =>
136
+ setTimeout(() => reject(new Error('Timeout')), NAV_TIMEOUT_MS),
137
+ ),
138
+ ]);
139
+ } catch {
140
+ // Non-fatal — continue iterations
141
+ }
142
+ }
143
+
144
+ // Record final heap size
145
+ const finalHeap = await this.getHeapSize(page);
146
+ if (finalHeap === null) {
147
+ return findings;
148
+ }
149
+
150
+ const growth = finalHeap - initialHeap;
151
+
152
+ this.addEvidence({
153
+ type: 'log',
154
+ path: `memory-${moduleId}`,
155
+ description: `Module ${moduleId}: initial=${(initialHeap / 1024 / 1024).toFixed(2)}MB final=${(finalHeap / 1024 / 1024).toFixed(2)}MB growth=${(growth / 1024 / 1024).toFixed(2)}MB`,
156
+ });
157
+
158
+ if (growth > HIGH_THRESHOLD_BYTES) {
159
+ findings.push({
160
+ id: `${this.agentId}-high-leak-${moduleId}`,
161
+ type: 'code-bug-logic',
162
+ severity: 'high',
163
+ agentId: this.agentId,
164
+ module: moduleId,
165
+ description: `Potential memory leak in module ${moduleId}: heap grew ${(growth / 1024 / 1024).toFixed(2)}MB after ${ITERATION_COUNT} navigation cycles (threshold: 10MB)`,
166
+ });
167
+ } else if (growth > MEDIUM_THRESHOLD_BYTES) {
168
+ findings.push({
169
+ id: `${this.agentId}-medium-leak-${moduleId}`,
170
+ type: 'code-bug-logic',
171
+ severity: 'medium',
172
+ agentId: this.agentId,
173
+ module: moduleId,
174
+ description: `Possible memory leak in module ${moduleId}: heap grew ${(growth / 1024 / 1024).toFixed(2)}MB after ${ITERATION_COUNT} navigation cycles (threshold: 5MB)`,
175
+ });
176
+ }
177
+
178
+ return findings;
179
+ }
180
+
181
+ private async getHeapSize(page: Page): Promise<number | null> {
182
+ try {
183
+ const heapSize = await page.evaluate(() => {
184
+ const perf = performance as Performance & {
185
+ memory?: { usedJSHeapSize: number };
186
+ };
187
+ return perf.memory?.usedJSHeapSize ?? null;
188
+ });
189
+ return heapSize;
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+ }
@@ -0,0 +1,132 @@
1
+ import { readdir, stat } from 'node:fs/promises';
2
+ import { join, extname } from 'node:path';
3
+ import type { Finding } from '../core/types';
4
+ import { BaseAgent } from './base-agent';
5
+
6
+ /** Maximum individual chunk size before flagging. */
7
+ const CHUNK_SIZE_THRESHOLD_BYTES = 250 * 1024; // 250KB
8
+
9
+ interface FileInfo {
10
+ name: string;
11
+ path: string;
12
+ sizeBytes: number;
13
+ }
14
+
15
+ export class BundleAnalyzerAgent extends BaseAgent {
16
+ readonly agentId = 33;
17
+ readonly agentName = 'Bundle Analyzer';
18
+
19
+ protected async preFlight(): Promise<void> {
20
+ // No special pre-conditions — we'll report if assets dir is missing
21
+ }
22
+
23
+ protected async execute(): Promise<Finding[]> {
24
+ const findings: Finding[] = [];
25
+ const projectRoot = this.config.projectRoot ?? process.cwd();
26
+
27
+ const assetsDirs = [
28
+ join(projectRoot, 'dist', 'assets'),
29
+ join(projectRoot, 'out', 'assets'),
30
+ ];
31
+
32
+ let assetsDir: string | null = null;
33
+ for (const dir of assetsDirs) {
34
+ try {
35
+ const s = await stat(dir);
36
+ if (s.isDirectory()) {
37
+ assetsDir = dir;
38
+ break;
39
+ }
40
+ } catch {
41
+ // Directory does not exist — try next
42
+ }
43
+ }
44
+
45
+ if (!assetsDir) {
46
+ findings.push({
47
+ id: `${this.agentId}-no-assets-dir`,
48
+ type: 'infra-issue',
49
+ severity: 'low',
50
+ agentId: this.agentId,
51
+ module: 'bundle-analyzer',
52
+ description: `No assets directory found at dist/assets or out/assets in ${projectRoot}`,
53
+ });
54
+ return findings;
55
+ }
56
+
57
+ let files: FileInfo[];
58
+ try {
59
+ files = await this.scanAssets(assetsDir);
60
+ } catch (scanError) {
61
+ findings.push({
62
+ id: `${this.agentId}-scan-error`,
63
+ type: 'infra-issue',
64
+ severity: 'medium',
65
+ agentId: this.agentId,
66
+ module: 'bundle-analyzer',
67
+ description: `Failed to scan assets directory: ${scanError instanceof Error ? scanError.message : String(scanError)}`,
68
+ });
69
+ return findings;
70
+ }
71
+
72
+ const jsFiles = files.filter(f => extname(f.name) === '.js');
73
+ const mapFiles = files.filter(f => extname(f.name) === '.map');
74
+ let totalBundleSize = 0;
75
+
76
+ // Check each JS chunk size
77
+ for (const file of jsFiles) {
78
+ totalBundleSize += file.sizeBytes;
79
+
80
+ if (file.sizeBytes > CHUNK_SIZE_THRESHOLD_BYTES) {
81
+ findings.push({
82
+ id: `${this.agentId}-large-chunk-${file.name}`,
83
+ type: 'test-bug',
84
+ severity: 'medium',
85
+ agentId: this.agentId,
86
+ module: 'bundle-analyzer',
87
+ file: file.path,
88
+ description: `JS chunk ${file.name} is ${(file.sizeBytes / 1024).toFixed(1)}KB — exceeds 250KB threshold`,
89
+ });
90
+ }
91
+ }
92
+
93
+ // Check for source maps in production
94
+ if (mapFiles.length > 0) {
95
+ findings.push({
96
+ id: `${this.agentId}-source-maps-in-production`,
97
+ type: 'code-bug-security',
98
+ severity: 'medium',
99
+ agentId: this.agentId,
100
+ module: 'bundle-analyzer',
101
+ description: `Found ${mapFiles.length} source map file(s) in production build — these expose source code: ${mapFiles.map(f => f.name).join(', ')}`,
102
+ });
103
+ }
104
+
105
+ this.addEvidence({
106
+ type: 'report',
107
+ path: 'bundle-analysis',
108
+ description: `Total bundle size: ${(totalBundleSize / 1024).toFixed(1)}KB across ${jsFiles.length} JS files. Source maps: ${mapFiles.length}`,
109
+ });
110
+
111
+ return findings;
112
+ }
113
+
114
+ private async scanAssets(dir: string): Promise<FileInfo[]> {
115
+ const entries = await readdir(dir, { withFileTypes: true });
116
+ const files: FileInfo[] = [];
117
+
118
+ for (const entry of entries) {
119
+ const fullPath = join(dir, entry.name);
120
+ if (entry.isFile()) {
121
+ const fileStat = await stat(fullPath);
122
+ files.push({
123
+ name: entry.name,
124
+ path: fullPath,
125
+ sizeBytes: fileStat.size,
126
+ });
127
+ }
128
+ }
129
+
130
+ return files;
131
+ }
132
+ }
@@ -0,0 +1,191 @@
1
+ import type { Browser, Page } from 'playwright';
2
+ import type { Finding } from '../core/types';
3
+ import { BaseAgent } from './base-agent';
4
+ import { resolveEnvironment } from '../helpers/env-resolver';
5
+ import { login } from '../helpers/navigation';
6
+
7
+ const XSS_PAYLOADS = [
8
+ '<script>alert(\'xss\')</script>',
9
+ '"><img onerror=alert(1) src=x>',
10
+ 'javascript:alert(1)',
11
+ ] as const;
12
+
13
+ const INPUT_SELECTORS = 'input[type="text"], textarea';
14
+ const NAV_TIMEOUT_MS = 5_000;
15
+ const MAX_MODULES = 3;
16
+
17
+ export class XssScannerAgent extends BaseAgent {
18
+ readonly agentId = 34;
19
+ readonly agentName = 'XSS Scanner';
20
+ private baseUrl = '';
21
+
22
+ protected async preFlight(): Promise<void> {
23
+ if (!this.config.modules || this.config.modules.length === 0) {
24
+ throw new Error('XssScannerAgent requires at least one module in config.modules');
25
+ }
26
+ const { env } = await resolveEnvironment(this.config, this.phase);
27
+ this.baseUrl = env.baseUrl;
28
+ }
29
+
30
+ protected async execute(): Promise<Finding[]> {
31
+ const findings: Finding[] = [];
32
+ const rawLoginUrl = this.config.auth.loginUrl ?? '/login';
33
+ const loginUrl = rawLoginUrl.startsWith('http') ? rawLoginUrl : `${this.baseUrl}${rawLoginUrl}`;
34
+ const credentials =
35
+ this.config.auth.credentials?.['admin'] ??
36
+ Object.values(this.config.auth.credentials ?? {})[0];
37
+
38
+ const { chromium } = await import('playwright');
39
+ let browser: Browser | null = null;
40
+
41
+ try {
42
+ browser = await chromium.launch({ headless: true });
43
+ const page = await browser.newPage();
44
+
45
+ if (credentials) {
46
+ try {
47
+ await login(page, credentials, loginUrl);
48
+ } catch (loginError) {
49
+ findings.push({
50
+ id: `${this.agentId}-login-failed`,
51
+ type: 'infra-issue',
52
+ severity: 'medium',
53
+ agentId: this.agentId,
54
+ module: 'xss-scanner',
55
+ description: `Login failed — continuing anonymously: ${loginError instanceof Error ? loginError.message.split('\n')[0] : String(loginError)}`,
56
+ });
57
+ }
58
+ }
59
+
60
+ const modules = this.config.modules.slice(0, MAX_MODULES);
61
+ for (const mod of modules) {
62
+ const moduleFindings = await this.testModuleXss(page, mod.id, mod.route);
63
+ findings.push(...moduleFindings);
64
+ }
65
+
66
+ await page.close().catch(() => undefined);
67
+ } catch (playwrightError) {
68
+ findings.push({
69
+ id: `${this.agentId}-playwright-failure`,
70
+ type: 'infra-issue',
71
+ severity: 'medium',
72
+ agentId: this.agentId,
73
+ module: 'xss-scanner',
74
+ description: `Playwright execution failed: ${playwrightError instanceof Error ? playwrightError.message.split('\n')[0] : String(playwrightError)}`,
75
+ });
76
+ } finally {
77
+ if (browser) await browser.close().catch(() => undefined);
78
+ }
79
+
80
+ return findings;
81
+ }
82
+
83
+ private async testModuleXss(
84
+ page: Page,
85
+ moduleId: string,
86
+ moduleRoute: string,
87
+ ): Promise<Finding[]> {
88
+ const findings: Finding[] = [];
89
+ const fullUrl = moduleRoute.startsWith('http')
90
+ ? moduleRoute
91
+ : `${this.baseUrl}${moduleRoute}`;
92
+
93
+ try {
94
+ await Promise.race([
95
+ page.goto(fullUrl, { waitUntil: 'domcontentloaded' }),
96
+ new Promise((_, reject) =>
97
+ setTimeout(() => reject(new Error('Navigation timeout')), NAV_TIMEOUT_MS),
98
+ ),
99
+ ]);
100
+ } catch {
101
+ findings.push({
102
+ id: `${this.agentId}-nav-failed-${moduleId}`,
103
+ type: 'test-bug',
104
+ severity: 'medium',
105
+ agentId: this.agentId,
106
+ module: moduleId,
107
+ description: `Failed to navigate to module ${moduleId} at ${moduleRoute}`,
108
+ });
109
+ return findings;
110
+ }
111
+
112
+ // Find input fields
113
+ let inputCount = 0;
114
+ try {
115
+ inputCount = await Promise.race([
116
+ page.locator(INPUT_SELECTORS).count(),
117
+ new Promise<number>((_, reject) =>
118
+ setTimeout(() => reject(new Error('Timeout')), NAV_TIMEOUT_MS),
119
+ ),
120
+ ]) as number;
121
+ } catch {
122
+ // Timeout discovering inputs
123
+ }
124
+
125
+ if (inputCount === 0) {
126
+ findings.push({
127
+ id: `${this.agentId}-no-inputs-${moduleId}`,
128
+ type: 'test-bug',
129
+ severity: 'low',
130
+ agentId: this.agentId,
131
+ module: moduleId,
132
+ description: `No text inputs or textareas found in module ${moduleId}`,
133
+ });
134
+ return findings;
135
+ }
136
+
137
+ // Test each payload on each input
138
+ for (const payload of XSS_PAYLOADS) {
139
+ for (let i = 0; i < inputCount; i++) {
140
+ let dialogFired = false;
141
+ const dialogHandler = (): void => {
142
+ dialogFired = true;
143
+ };
144
+
145
+ page.on('dialog', dialogHandler);
146
+
147
+ try {
148
+ const input = page.locator(INPUT_SELECTORS).nth(i);
149
+ await Promise.race([
150
+ input.fill(payload),
151
+ new Promise((_, reject) =>
152
+ setTimeout(() => reject(new Error('Timeout')), NAV_TIMEOUT_MS),
153
+ ),
154
+ ]);
155
+
156
+ // Brief wait to see if dialog fires
157
+ await new Promise(resolve => setTimeout(resolve, 500));
158
+
159
+ if (dialogFired) {
160
+ findings.push({
161
+ id: `${this.agentId}-xss-found-${moduleId}-input${i}`,
162
+ type: 'code-bug-security',
163
+ severity: 'critical',
164
+ agentId: this.agentId,
165
+ module: moduleId,
166
+ description: `XSS vulnerability found in module ${moduleId} input #${i} with payload: ${payload}`,
167
+ });
168
+ }
169
+ } catch {
170
+ // Input interaction failed — non-critical
171
+ } finally {
172
+ page.removeListener('dialog', dialogHandler);
173
+ }
174
+
175
+ // Re-navigate to reset state
176
+ try {
177
+ await Promise.race([
178
+ page.goto(fullUrl, { waitUntil: 'domcontentloaded' }),
179
+ new Promise((_, reject) =>
180
+ setTimeout(() => reject(new Error('Timeout')), NAV_TIMEOUT_MS),
181
+ ),
182
+ ]);
183
+ } catch {
184
+ // Navigation reset failed
185
+ }
186
+ }
187
+ }
188
+
189
+ return findings;
190
+ }
191
+ }