ubon
Version:
Security scanner for AI-generated apps (Cursor, Lovable, Windsurf, v0). Catches hardcoded secrets, prompt injection, hallucinated imports, Server Actions / Edge runtime mistakes, and the vibe-coded vulnerabilities traditional linters miss.
74 lines • 3.98 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AccessibilityScanner = void 0;
const base_scanner_1 = require("./base-scanner");
const rules_1 = require("../rules");
class AccessibilityScanner extends base_scanner_1.BaseScanner {
name = 'Accessibility Scanner';
ruleIds = ['A11Y001', 'A11Y002', 'A11Y003', 'A11Y004', 'A11Y005', 'A11Y006', 'A11Y007', 'NEXT005', 'NEXT001', 'NEXT002'];
async scan(options) {
const results = [];
const rules = this.ruleIds.map((id) => (0, rules_1.getRule)(id)).filter(Boolean);
for await (const ctx of this.iterateFiles(options, '**/*.{js,jsx,ts,tsx,svelte,astro,html}', ['node_modules/**', 'dist/**', 'build/**', '.next/**', 'examples/**'])) {
const fileExt = ctx.file.split('.').pop()?.toLowerCase();
if (this.hasFileSuppression(ctx.lines))
continue;
for (const rule of rules) {
if (!rule)
continue;
if (rule.impl.fileTypes && !rule.impl.fileTypes.includes(fileExt || ''))
continue;
if (rule.impl.detect) {
const detectionResults = rule.impl.detect(ctx.content, ctx.file, ctx.lines);
for (const detection of detectionResults) {
if (this.isSuppressed(ctx.lines, detection.line - 1, rule.meta.id))
continue;
results.push(this.createResult({
type: rule.meta.severity === 'high' ? 'error' : 'warning',
category: rule.meta.category,
message: rule.meta.message,
file: ctx.file,
line: detection.line,
severity: rule.meta.severity,
ruleId: rule.meta.id,
confidence: detection.confidence || 0.8,
confidenceReason: detection.confidenceReason || `Accessibility issue detected by ${rule.meta.id} rule`,
fix: rule.meta.fix,
match: detection.match,
...(detection.fixEdits ? { fixEdits: detection.fixEdits } : {})
}, ctx.lines[detection.line - 1]));
}
}
if (rule.impl.patterns) {
for (const pattern of rule.impl.patterns) {
ctx.lines.forEach((line, index) => {
if (line.trim().startsWith('//') || line.trim().startsWith('*'))
return;
if (this.isSuppressed(ctx.lines, index, pattern.ruleId))
return;
const match = line.match(pattern.pattern);
if (!match)
return;
results.push(this.createResult({
type: pattern.severity === 'high' ? 'error' : 'warning',
category: rule.meta.category,
message: pattern.message,
file: ctx.file,
line: index + 1,
severity: pattern.severity,
ruleId: pattern.ruleId,
confidence: pattern.confidence,
confidenceReason: `Pattern match: ${pattern.message}`,
fix: pattern.fix,
match: match[0]?.slice(0, 200)
}, line));
});
}
}
}
}
return results;
}
}
exports.AccessibilityScanner = AccessibilityScanner;
//# sourceMappingURL=accessibility-scanner.js.map