har-o-scope 0.1.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 (75) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +179 -0
  3. package/completions/har-o-scope.bash +64 -0
  4. package/completions/har-o-scope.fish +43 -0
  5. package/completions/har-o-scope.zsh +63 -0
  6. package/dist/cli/colors.d.ts +17 -0
  7. package/dist/cli/colors.d.ts.map +1 -0
  8. package/dist/cli/colors.js +54 -0
  9. package/dist/cli/demo.d.ts +7 -0
  10. package/dist/cli/demo.d.ts.map +1 -0
  11. package/dist/cli/demo.js +62 -0
  12. package/dist/cli/formatters.d.ts +12 -0
  13. package/dist/cli/formatters.d.ts.map +1 -0
  14. package/dist/cli/formatters.js +249 -0
  15. package/dist/cli/index.d.ts +3 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +260 -0
  18. package/dist/cli/rules.d.ts +3 -0
  19. package/dist/cli/rules.d.ts.map +1 -0
  20. package/dist/cli/rules.js +36 -0
  21. package/dist/cli/sarif.d.ts +9 -0
  22. package/dist/cli/sarif.d.ts.map +1 -0
  23. package/dist/cli/sarif.js +104 -0
  24. package/dist/lib/analyze.d.ts +10 -0
  25. package/dist/lib/analyze.d.ts.map +1 -0
  26. package/dist/lib/analyze.js +83 -0
  27. package/dist/lib/classifier.d.ts +8 -0
  28. package/dist/lib/classifier.d.ts.map +1 -0
  29. package/dist/lib/classifier.js +74 -0
  30. package/dist/lib/diff.d.ts +15 -0
  31. package/dist/lib/diff.d.ts.map +1 -0
  32. package/dist/lib/diff.js +130 -0
  33. package/dist/lib/errors.d.ts +56 -0
  34. package/dist/lib/errors.d.ts.map +1 -0
  35. package/dist/lib/errors.js +65 -0
  36. package/dist/lib/evaluate.d.ts +19 -0
  37. package/dist/lib/evaluate.d.ts.map +1 -0
  38. package/dist/lib/evaluate.js +189 -0
  39. package/dist/lib/health-score.d.ts +18 -0
  40. package/dist/lib/health-score.d.ts.map +1 -0
  41. package/dist/lib/health-score.js +74 -0
  42. package/dist/lib/html-report.d.ts +15 -0
  43. package/dist/lib/html-report.d.ts.map +1 -0
  44. package/dist/lib/html-report.js +299 -0
  45. package/dist/lib/index.d.ts +26 -0
  46. package/dist/lib/index.d.ts.map +1 -0
  47. package/dist/lib/index.js +24 -0
  48. package/dist/lib/normalizer.d.ts +18 -0
  49. package/dist/lib/normalizer.d.ts.map +1 -0
  50. package/dist/lib/normalizer.js +201 -0
  51. package/dist/lib/rule-engine.d.ts +12 -0
  52. package/dist/lib/rule-engine.d.ts.map +1 -0
  53. package/dist/lib/rule-engine.js +122 -0
  54. package/dist/lib/sanitizer.d.ts +10 -0
  55. package/dist/lib/sanitizer.d.ts.map +1 -0
  56. package/dist/lib/sanitizer.js +129 -0
  57. package/dist/lib/schema.d.ts +85 -0
  58. package/dist/lib/schema.d.ts.map +1 -0
  59. package/dist/lib/schema.js +1 -0
  60. package/dist/lib/trace-sanitizer.d.ts +30 -0
  61. package/dist/lib/trace-sanitizer.d.ts.map +1 -0
  62. package/dist/lib/trace-sanitizer.js +85 -0
  63. package/dist/lib/types.d.ts +161 -0
  64. package/dist/lib/types.d.ts.map +1 -0
  65. package/dist/lib/types.js +1 -0
  66. package/dist/lib/unbatched-detect.d.ts +7 -0
  67. package/dist/lib/unbatched-detect.d.ts.map +1 -0
  68. package/dist/lib/unbatched-detect.js +59 -0
  69. package/dist/lib/validator.d.ts +4 -0
  70. package/dist/lib/validator.d.ts.map +1 -0
  71. package/dist/lib/validator.js +409 -0
  72. package/package.json +98 -0
  73. package/rules/generic/issue-rules.yaml +292 -0
  74. package/rules/generic/shared/base-conditions.yaml +28 -0
  75. package/rules/generic/shared/filters.yaml +12 -0
@@ -0,0 +1,292 @@
1
+ # Generic issue detection rules for har-o-scope.
2
+ # 16 YAML rules (+ 1 TypeScript rule: unbatched-api-calls).
3
+ # Zero platform-specific content. Works with any web application.
4
+
5
+ rules:
6
+
7
+ # ── Server ──────────────────────────────────────────────────────
8
+
9
+ slow-ttfb:
10
+ category: server
11
+ severity: warning
12
+ severity_escalation:
13
+ critical_threshold: 5
14
+ inherits: ["is_not_streaming", "is_not_websocket"]
15
+ title: "{count} request{s} with slow TTFB (> 800ms)"
16
+ description: "{count} requests had Time To First Byte exceeding 800ms. This indicates server-side processing delays, slow database queries, or backend bottlenecks."
17
+ recommendation: "Investigate server-side processing time. Check database query performance, API response times, and server resource utilization. Consider caching frequently requested resources."
18
+ condition:
19
+ match_all:
20
+ - field: "timings.wait"
21
+ gt: 800
22
+ impact:
23
+ field: "timings.wait"
24
+ baseline: 200
25
+ root_cause_weight:
26
+ server: 3
27
+
28
+ # ── Network ─────────────────────────────────────────────────────
29
+
30
+ stalled-requests:
31
+ category: network
32
+ severity: warning
33
+ severity_escalation:
34
+ critical_threshold: 10
35
+ inherits: ["is_not_streaming", "is_not_websocket"]
36
+ title: "{count} stalled request{s} (blocked > 1000ms)"
37
+ description: "{count} requests spent over 1 second waiting in the browser's connection queue. This often indicates HTTP/1.1 connection saturation (6 connections per domain limit)."
38
+ recommendation: "Reduce concurrent requests to the same domain. Enable HTTP/2 to multiplex requests over a single connection. Consider domain sharding as a last resort for HTTP/1.1."
39
+ condition:
40
+ match_all:
41
+ - field: "timings.blocked"
42
+ gt: 1000
43
+ impact:
44
+ field: "timings.blocked"
45
+ root_cause_weight:
46
+ client: 3
47
+
48
+ slow-dns:
49
+ category: network
50
+ severity: warning
51
+ severity_escalation:
52
+ critical_threshold: 5
53
+ title: "{count} request{s} with slow DNS lookup (> 100ms)"
54
+ description: "{count} requests had DNS resolution times exceeding 100ms. This suggests slow DNS resolvers or many unique domains being resolved."
55
+ recommendation: "Consider using a faster DNS provider (e.g., 1.1.1.1, 8.8.8.8). Reduce the number of unique domains. Use dns-prefetch hints for critical third-party domains."
56
+ condition:
57
+ match_all:
58
+ - field: "timings.dns"
59
+ gt: 100
60
+ impact:
61
+ field: "timings.dns"
62
+ root_cause_weight:
63
+ network: 3
64
+
65
+ slow-tls:
66
+ category: network
67
+ severity: warning
68
+ title: "{count} request{s} with slow TLS handshake (> 200ms)"
69
+ description: "{count} requests had TLS negotiation times exceeding 200ms. This may indicate geographic distance to the server, certificate chain issues, or missing OCSP stapling."
70
+ recommendation: "Check server TLS configuration. Enable OCSP stapling. Consider using a CDN to reduce round-trip distance. Verify the certificate chain is minimal."
71
+ condition:
72
+ match_all:
73
+ - field: "timings.ssl"
74
+ gt: 200
75
+ impact:
76
+ field: "timings.ssl"
77
+ baseline: 50
78
+ root_cause_weight:
79
+ network: 2
80
+
81
+ http1-downgrade:
82
+ category: network
83
+ severity: info
84
+ severity_escalation:
85
+ warning_ratio: 0.5
86
+ inherits: ["is_not_websocket"]
87
+ title: "{count} request{s} using HTTP/1.1"
88
+ description: "{count} requests used HTTP/1.1 instead of HTTP/2 or HTTP/3. HTTP/1.1 limits browsers to 6 concurrent connections per domain, causing request queueing on busy pages."
89
+ recommendation: "Ensure your server supports HTTP/2. Check if a corporate proxy or VPN is forcing an HTTP/1.1 downgrade. Most modern servers and CDNs support HTTP/2 by default."
90
+ condition:
91
+ match_all:
92
+ - field: "httpVersion"
93
+ not_matches: "h2|h3|HTTP/2|HTTP/3"
94
+ - field: "httpVersion"
95
+ not_equals: "unknown"
96
+ impact:
97
+ value: 0
98
+ root_cause_weight:
99
+ network: 2
100
+
101
+ connection-reuse:
102
+ category: network
103
+ severity: warning
104
+ min_count: 3
105
+ inherits: ["is_not_websocket"]
106
+ title: "Poor connection reuse: {count} new TCP connections"
107
+ description: "{count} requests established new TCP connections (connect time > 0). Reusing existing connections avoids the overhead of TCP handshake + TLS negotiation."
108
+ recommendation: "Ensure Connection: keep-alive is enabled. Reduce the number of unique domains. Enable HTTP/2 for multiplexed connections."
109
+ condition:
110
+ match_all:
111
+ - field: "timings.connect"
112
+ gt: 0
113
+ impact:
114
+ field: "timings.connect"
115
+ root_cause_weight:
116
+ network: 2
117
+
118
+ # ── Optimization ────────────────────────────────────────────────
119
+
120
+ missing-compression:
121
+ category: optimization
122
+ severity: info
123
+ severity_escalation:
124
+ warning_threshold: 11
125
+ title: "{count} text response{s} without compression"
126
+ description: "{count} text-based responses are not using gzip, Brotli, or other compression. Compressed responses are typically 60-80% smaller."
127
+ recommendation: "Enable gzip or Brotli compression on the server for text-based responses (HTML, CSS, JS, JSON, XML). Most web servers support this with a single configuration change."
128
+ condition:
129
+ match_all:
130
+ - field: "entry.response.content.mimeType"
131
+ matches: "text|javascript|json|xml|html|css"
132
+ - field: "contentSize"
133
+ gt: 1024
134
+ - no_response_header:
135
+ name: "content-encoding"
136
+ value_matches: "gzip|br|deflate|zstd"
137
+ impact:
138
+ value: 0
139
+
140
+ missing-cache-headers:
141
+ category: optimization
142
+ severity: info
143
+ inherits: ["is_static_resource"]
144
+ title: "{count} static resource{s} without cache headers"
145
+ description: "{count} static resources (scripts, styles, images, fonts) lack Cache-Control, ETag, or Last-Modified headers. The browser cannot cache these effectively and will re-fetch on every page load."
146
+ recommendation: "Add Cache-Control headers to static resources. Use max-age=31536000 with content-hashed filenames for immutable assets. Add ETag or Last-Modified for conditional revalidation."
147
+ condition:
148
+ match_all:
149
+ - no_response_header:
150
+ name: "cache-control"
151
+ - no_response_header:
152
+ name: "etag"
153
+ - no_response_header:
154
+ name: "last-modified"
155
+ impact:
156
+ value: 0
157
+
158
+ large-payload:
159
+ category: optimization
160
+ severity: info
161
+ severity_escalation:
162
+ warning_threshold: 5
163
+ title: "{count} oversized resource{s} (> 1 MB)"
164
+ description: "{count} responses exceeded 1 MB in transfer size. Large payloads slow down page loads, especially on slower connections."
165
+ recommendation: "Optimize large resources: compress images (WebP/AVIF), minify JS/CSS, paginate large API responses, lazy-load below-the-fold content."
166
+ condition:
167
+ match_all:
168
+ - field: "transferSizeResolved"
169
+ gt: 1048576
170
+ impact:
171
+ value: 0
172
+
173
+ cors-preflight:
174
+ category: optimization
175
+ severity: info
176
+ severity_escalation:
177
+ warning_threshold: 10
178
+ min_count: 3
179
+ title: "{count} CORS preflight request{s}"
180
+ description: "{count} OPTIONS preflight requests were sent before the actual requests. Each preflight adds a full round-trip of latency."
181
+ recommendation: "Configure Access-Control-Max-Age headers to cache preflight responses. Simplify requests to avoid triggering preflights (use simple methods and headers where possible)."
182
+ condition:
183
+ match_all:
184
+ - field: "entry.request.method"
185
+ equals: "OPTIONS"
186
+ impact:
187
+ field: "timings.total"
188
+
189
+ excessive-cookies:
190
+ category: optimization
191
+ severity: warning
192
+ title: "Excessive Set-Cookie headers on {count} response{s}"
193
+ description: "{count} responses included Set-Cookie headers. Cookies are sent with every subsequent request, increasing bandwidth usage. Large cookie payloads can also trigger warnings from server infrastructure."
194
+ recommendation: "Audit cookie usage. Use sessionStorage or localStorage for client-only data. Set appropriate cookie domains and paths to limit cookie scope. Consider using a cookieless domain for static assets."
195
+ condition:
196
+ match_all:
197
+ - has_response_header:
198
+ name: "set-cookie"
199
+ min_count: 10
200
+ impact:
201
+ value: 0
202
+
203
+ redirect-chains:
204
+ category: performance
205
+ severity: warning
206
+ min_count: 3
207
+ title: "{count} redirect{s} detected"
208
+ description: "{count} requests resulted in redirects (301, 302, 303, 307, 308). Each redirect adds a full round-trip. Chains of 3+ redirects significantly impact page load time."
209
+ recommendation: "Eliminate unnecessary redirects. Update links to point directly to the final URL. Use 301 (permanent) redirects where appropriate so browsers cache the redirect."
210
+ condition:
211
+ match_all:
212
+ - field: "entry.response.status"
213
+ in: [301, 302, 303, 307, 308]
214
+ impact:
215
+ field: "timings.total"
216
+ root_cause_weight:
217
+ server: 1
218
+ network: 1
219
+
220
+ # ── Security ────────────────────────────────────────────────────
221
+
222
+ mixed-content:
223
+ category: security
224
+ severity: critical
225
+ prerequisite:
226
+ any_entry_matches:
227
+ field: "entry.request.url"
228
+ matches: "^https://"
229
+ title: "{count} mixed content request{s} (HTTP on HTTPS page)"
230
+ description: "{count} requests were made over insecure HTTP from a secure HTTPS page. Browsers may block these requests or show security warnings."
231
+ recommendation: "Update all resource URLs to use HTTPS. Configure Content-Security-Policy with upgrade-insecure-requests. Check for hardcoded HTTP URLs in your code."
232
+ condition:
233
+ match_all:
234
+ - field: "entry.request.url"
235
+ matches: "^http://"
236
+ - field: "entry.request.url"
237
+ not_matches: "^http://localhost|^http://127\\.0\\.0\\.1|^http://\\[::1\\]"
238
+ impact:
239
+ value: 0
240
+ root_cause_weight:
241
+ client: 2
242
+
243
+ # ── Errors ──────────────────────────────────────────────────────
244
+
245
+ broken-resources:
246
+ category: errors
247
+ severity: critical
248
+ title: "{count} broken resource{s} (HTTP 4xx/5xx)"
249
+ description: "{count} requests returned error status codes. 4xx errors indicate client-side issues (missing resources, unauthorized access). 5xx errors indicate server failures."
250
+ recommendation: "Review the failing URLs. Fix 404s by updating links or deploying missing assets. Investigate 500s in server logs. Check 403s for permission or CORS issues."
251
+ condition:
252
+ match_all:
253
+ - field: "entry.response.status"
254
+ gte: 400
255
+ impact:
256
+ value: 0
257
+ root_cause_weight:
258
+ server: 2
259
+ client: 1
260
+
261
+ # ── Informational ───────────────────────────────────────────────
262
+
263
+ long-poll-detected:
264
+ category: informational
265
+ severity: info
266
+ title: "{count} long-poll or SSE connection{s} detected"
267
+ description: "{count} requests appear to be long-polling or Server-Sent Events connections (high wait time with small response body, or text/event-stream MIME type). These are expected behavior, not performance issues."
268
+ recommendation: "No action needed. Long-polling and SSE are legitimate real-time communication patterns. They will appear as slow requests in waterfall charts but are working as designed."
269
+ condition:
270
+ match_all:
271
+ - field: "isLongPoll"
272
+ equals: true
273
+ impact:
274
+ value: 0
275
+
276
+ # ── Aggregate ───────────────────────────────────────────────────
277
+
278
+ excessive-requests:
279
+ type: aggregate
280
+ category: client
281
+ severity: warning
282
+ severity_escalation:
283
+ critical_threshold: 500
284
+ aggregate_condition:
285
+ min_entries: 200
286
+ title: "High request count: {total} requests"
287
+ description: "This page made {total} HTTP requests. High request counts degrade performance, especially on HTTP/1.1 connections where browsers limit concurrent connections to 6 per domain."
288
+ recommendation: "Reduce request count by bundling scripts and styles, using CSS sprites or SVG icons, lazy-loading below-the-fold resources, and eliminating unnecessary analytics or tracking calls."
289
+ impact:
290
+ value: 0
291
+ root_cause_weight:
292
+ client: 5
@@ -0,0 +1,28 @@
1
+ # Reusable condition fragments for generic rules.
2
+ # Reference by name in a rule's `inherits` array.
3
+
4
+ conditions:
5
+
6
+ is_not_streaming:
7
+ field: "isLongPoll"
8
+ equals: false
9
+
10
+ is_not_websocket:
11
+ field: "resourceType"
12
+ not_equals: "websocket"
13
+
14
+ is_http1:
15
+ field: "httpVersion"
16
+ matches: "^HTTP/1"
17
+
18
+ is_success_status:
19
+ field: "entry.response.status"
20
+ gte: 200
21
+
22
+ is_text_content:
23
+ field: "entry.response.content.mimeType"
24
+ matches: "text|javascript|json|xml|html|css"
25
+
26
+ is_static_resource:
27
+ field: "resourceType"
28
+ in: ["script", "stylesheet", "image", "font"]
@@ -0,0 +1,12 @@
1
+ # Noise exclusion patterns for generic rules.
2
+ # Reference by name in a rule's `exclude` array.
3
+
4
+ filters:
5
+
6
+ analytics_tracking:
7
+ field: "entry.request.url"
8
+ matches: "google-analytics|googletagmanager|mixpanel|facebook.*/tr|linkedin.*/px|doubleclick|segment\\.io|amplitude"
9
+
10
+ status_0_or_cancelled:
11
+ field: "entry.response.status"
12
+ equals: 0