argusqa-os 9.6.6 → 9.7.4
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.
- package/README.md +394 -384
- package/glama.json +2 -2
- package/package.json +77 -71
- package/src/adapters/browser.js +11 -3
- package/src/cli/chrome-launcher.js +175 -0
- package/src/cli/doctor.js +133 -0
- package/src/cli/pr-validate.js +25 -6
- package/src/mcp-server.js +27 -9
- package/src/orchestration/orchestrator.js +9 -7
- package/src/orchestration/report-processor.js +33 -1
- package/src/orchestration/watch-mode.js +20 -0
- package/src/utils/a11y-deep-analyzer.js +1 -1
- package/src/utils/contract-validator.js +27 -2
- package/src/utils/design-fidelity-analyzer.js +1 -1
- package/src/utils/flow-runner.js +16 -2
- package/src/utils/font-analyzer.js +1 -1
- package/src/utils/form-analyzer.js +1 -1
- package/src/utils/har-recorder.js +1 -1
- package/src/utils/issues-analyzer.js +12 -19
- package/src/utils/mcp-parsers.js +20 -0
- package/src/utils/motion-analyzer.js +1 -1
- package/src/utils/noise-filter.js +159 -0
- package/src/utils/pdf-exporter.js +146 -0
- package/src/utils/pr-diff-analyzer.js +11 -2
- package/src/utils/root-cause-linker.js +175 -0
- package/src/utils/screen-recorder.js +250 -0
- package/src/utils/security-analyzer.js +132 -1
- package/src/utils/theme-analyzer.js +1 -1
- package/src/utils/visual-diff-analyzer.js +1 -1
- package/src/utils/web-vitals-analyzer.js +1 -1
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* • HTTP resource on HTTPS page (D6.9) — skips loopback; only fires on real HTTPS origins
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import { execFile } from 'child_process';
|
|
21
22
|
import { thresholds } from '../config/targets.js';
|
|
22
23
|
import { childLogger } from './logger.js';
|
|
23
24
|
|
|
@@ -111,7 +112,27 @@ export const SECURITY_ANALYSIS_SCRIPT = `async () => {
|
|
|
111
112
|
}
|
|
112
113
|
} catch (e) {}
|
|
113
114
|
|
|
114
|
-
|
|
115
|
+
// 7. SRI check — external scripts and stylesheets without integrity attribute
|
|
116
|
+
var sriViolations = [];
|
|
117
|
+
try {
|
|
118
|
+
var pageOrigin = location.origin;
|
|
119
|
+
var extScripts = Array.prototype.slice.call(document.querySelectorAll('script[src]:not([integrity])'));
|
|
120
|
+
for (var sri_i = 0; sri_i < extScripts.length && sri_i < 20; sri_i++) {
|
|
121
|
+
var scriptSrc = extScripts[sri_i].src || '';
|
|
122
|
+
if (scriptSrc && !scriptSrc.startsWith(pageOrigin) && !scriptSrc.startsWith('/') && !scriptSrc.startsWith('blob:') && !scriptSrc.startsWith('data:')) {
|
|
123
|
+
sriViolations.push({ tag: 'script', src: scriptSrc.slice(0, 200) });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
var extLinks = Array.prototype.slice.call(document.querySelectorAll('link[rel="stylesheet"][href]:not([integrity])'));
|
|
127
|
+
for (var sri_j = 0; sri_j < extLinks.length && sri_j < 20; sri_j++) {
|
|
128
|
+
var linkHref = extLinks[sri_j].href || '';
|
|
129
|
+
if (linkHref && !linkHref.startsWith(pageOrigin) && !linkHref.startsWith('/') && !linkHref.startsWith('blob:') && !linkHref.startsWith('data:')) {
|
|
130
|
+
sriViolations.push({ tag: 'link', src: linkHref.slice(0, 200) });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {}
|
|
134
|
+
|
|
135
|
+
return JSON.stringify({ storageTokenKeys: storageTokenKeys, evalUsage: evalUsage, jsCookies: jsCookies, hasCSP: hasCSP, hasXFrame: hasXFrame, unsandboxedIframes: unsandboxedIframes, unsafeBlankLinks: unsafeBlankLinks, sriViolations: sriViolations });
|
|
115
136
|
}`;
|
|
116
137
|
|
|
117
138
|
/**
|
|
@@ -219,6 +240,20 @@ export function parseSecurityAnalysisResult(rawResult, url) {
|
|
|
219
240
|
});
|
|
220
241
|
}
|
|
221
242
|
|
|
243
|
+
// SRI violations — external scripts/stylesheets without integrity attribute
|
|
244
|
+
if (Array.isArray(data.sriViolations) && data.sriViolations.length > 0) {
|
|
245
|
+
for (const v of data.sriViolations) {
|
|
246
|
+
bugs.push({
|
|
247
|
+
type: 'security_missing_sri',
|
|
248
|
+
tag: v.tag,
|
|
249
|
+
src: v.src,
|
|
250
|
+
message: `External <${v.tag}> without integrity attribute: "${String(v.src).slice(0, 200)}" — add integrity="sha384-..." to prevent supply-chain attacks`,
|
|
251
|
+
severity: 'warning',
|
|
252
|
+
url,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
222
257
|
return bugs;
|
|
223
258
|
}
|
|
224
259
|
|
|
@@ -300,3 +335,99 @@ export function analyzeSecurityNetwork(networkReqs, url) {
|
|
|
300
335
|
}
|
|
301
336
|
return bugs;
|
|
302
337
|
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Detect source map files being served in production.
|
|
341
|
+
* Source maps expose original unminified source code to anyone with DevTools open.
|
|
342
|
+
*
|
|
343
|
+
* @param {object[]} networkReqs - Network request entries ({ url })
|
|
344
|
+
* @param {string} url - Page URL for context
|
|
345
|
+
* @returns {object[]}
|
|
346
|
+
*/
|
|
347
|
+
export function checkSourceMapExposure(networkReqs, url) {
|
|
348
|
+
const bugs = [];
|
|
349
|
+
for (const req of (Array.isArray(networkReqs) ? networkReqs : [])) {
|
|
350
|
+
const reqUrl = req.url ?? req.requestUrl ?? '';
|
|
351
|
+
if (!reqUrl) continue;
|
|
352
|
+
if (/\.(js|css)\.map(\?|$)/i.test(reqUrl) || /\/[^/]+\.map(\?|$)/.test(reqUrl)) {
|
|
353
|
+
bugs.push({
|
|
354
|
+
type: 'security_sourcemap_exposed',
|
|
355
|
+
requestUrl: reqUrl,
|
|
356
|
+
message: `Source map publicly accessible: "${reqUrl.slice(0, 200)}" — remove or restrict .map files in production to protect original source code`,
|
|
357
|
+
severity: 'warning',
|
|
358
|
+
url,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return bugs;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Detect open redirect parameters in network request URLs.
|
|
367
|
+
* Open redirects allow attackers to craft phishing URLs that appear to come from
|
|
368
|
+
* the legitimate domain.
|
|
369
|
+
*
|
|
370
|
+
* @param {object[]} networkReqs - Network request entries ({ url })
|
|
371
|
+
* @param {string} url - Page URL for context
|
|
372
|
+
* @returns {object[]}
|
|
373
|
+
*/
|
|
374
|
+
export function checkOpenRedirects(networkReqs, url) {
|
|
375
|
+
// 'to', 'target', 'url' excluded — too common in non-redirect contexts (CDN proxies, nav params).
|
|
376
|
+
const redirectParams = /[?&](redirect|return|next|dest|destination|goto|redir|forward)=/i;
|
|
377
|
+
const bugs = [];
|
|
378
|
+
for (const req of (Array.isArray(networkReqs) ? networkReqs : [])) {
|
|
379
|
+
const reqUrl = req.url ?? req.requestUrl ?? '';
|
|
380
|
+
if (!reqUrl || !redirectParams.test(reqUrl)) continue;
|
|
381
|
+
bugs.push({
|
|
382
|
+
type: 'security_open_redirect',
|
|
383
|
+
requestUrl: reqUrl,
|
|
384
|
+
message: `Potential open redirect parameter in URL: "${reqUrl.slice(0, 200)}" — validate redirect targets server-side against an allowlist`,
|
|
385
|
+
severity: 'warning',
|
|
386
|
+
url,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return bugs;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Run `npm audit --json` in the given project directory and convert CVEs to findings.
|
|
394
|
+
* Skips silently if projectDir is falsy, npm is not available, or the project has
|
|
395
|
+
* no package.json (not a Node project).
|
|
396
|
+
*
|
|
397
|
+
* @param {string|null} projectDir - Absolute path to the project root
|
|
398
|
+
* @returns {Promise<object[]>}
|
|
399
|
+
*/
|
|
400
|
+
export async function auditNpmDependencies(projectDir) {
|
|
401
|
+
if (!projectDir) return [];
|
|
402
|
+
|
|
403
|
+
return new Promise(resolve => {
|
|
404
|
+
// shell: true resolves npm.cmd on Windows; harmless on macOS/Linux.
|
|
405
|
+
execFile('npm', ['audit', '--json'], { cwd: projectDir, maxBuffer: 4 * 1024 * 1024, shell: true }, (err, stdout) => {
|
|
406
|
+
// npm audit exits non-zero when vulnerabilities exist — we still want stdout.
|
|
407
|
+
if (!stdout) return resolve([]);
|
|
408
|
+
let report;
|
|
409
|
+
try { report = JSON.parse(stdout); } catch { return resolve([]); }
|
|
410
|
+
|
|
411
|
+
const bugs = [];
|
|
412
|
+
const vulns = report?.vulnerabilities ?? report?.advisories ?? {};
|
|
413
|
+
|
|
414
|
+
for (const [name, info] of Object.entries(vulns)) {
|
|
415
|
+
const sev = String(info.severity ?? 'moderate').toLowerCase();
|
|
416
|
+
const via = Array.isArray(info.via)
|
|
417
|
+
? info.via.filter(v => typeof v === 'string').join(', ')
|
|
418
|
+
: '';
|
|
419
|
+
bugs.push({
|
|
420
|
+
type: 'security_npm_vulnerability',
|
|
421
|
+
package: name,
|
|
422
|
+
severity: sev === 'critical' || sev === 'high' ? 'critical' : 'warning',
|
|
423
|
+
message: `npm vulnerability in "${name}"${via ? ` via ${via}` : ''} (${sev}) — run \`npm audit fix\` to resolve`,
|
|
424
|
+
via,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Deduplicate by package name (advisories-style reports can have duplicates).
|
|
429
|
+
const seen = new Set();
|
|
430
|
+
resolve(bugs.filter(b => { if (seen.has(b.package)) return false; seen.add(b.package); return true; }));
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ARGUS Web Vitals Analyzer (
|
|
2
|
+
* ARGUS Web Vitals Analyzer (Advanced Performance Metrics)
|
|
3
3
|
*
|
|
4
4
|
* Captures Core Web Vitals and performance metrics directly via the browser
|
|
5
5
|
* Performance API. Unlike Lighthouse, this works in headless Chrome — metrics
|