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.
98 lines • 3.74 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.toSarif = toSarif;
const rules_1 = require("../rules");
const redact_1 = require("./redact");
const package_json_1 = __importDefault(require("../../package.json"));
const crypto_1 = require("crypto");
function synthesizeFingerprint(r) {
return (0, crypto_1.createHash)('sha1')
.update([r.ruleId, r.file || '', r.line || 0, r.match || r.message].join('|'))
.digest('hex')
.slice(0, 16);
}
function toSarif(results, _repoRoot) {
const rulesMap = new Map();
const sarifResults = results.map((r) => {
if (!rulesMap.has(r.ruleId)) {
const meta = rules_1.RULES[r.ruleId];
rulesMap.set(r.ruleId, {
id: r.ruleId,
name: r.ruleId,
shortDescription: { text: r.message.slice(0, 64) },
fullDescription: { text: r.message },
properties: {
category: r.category,
severity: r.severity,
confidence: r.confidence,
},
defaultConfiguration: {
level: r.type === 'error' ? 'error' : r.type === 'warning' ? 'warning' : 'note',
},
help: { text: r.fix || (meta?.fix || '') },
helpUri: meta?.helpUri,
});
}
const level = r.type === 'error' ? 'error' : r.type === 'warning' ? 'warning' : 'note';
return {
ruleId: r.ruleId,
level,
message: { text: r.message },
locations: r.file
? [
{
physicalLocation: {
artifactLocation: { uri: normalizePath(r.file) },
region: r.range
? {
startLine: r.range.startLine,
startColumn: r.range.startColumn,
endLine: r.range.endLine,
endColumn: r.range.endColumn,
}
: r.line
? { startLine: r.line }
: undefined,
},
},
]
: [],
properties: {
category: r.category,
severity: r.severity,
confidence: r.confidence,
fingerprint: r.fingerprint,
match: (0, redact_1.redact)(r.match),
fix: r.fix,
},
partialFingerprints: { "ubon/fingerprint": r.fingerprint || synthesizeFingerprint(r) },
};
});
const sarif = {
$schema: 'https://json.schemastore.org/sarif-2.1.0.json',
version: '2.1.0',
runs: [
{
tool: {
driver: {
name: 'ubon',
// @ts-ignore version is allowed in SARIF driver
version: package_json_1.default.version,
informationUri: 'https://github.com/luisfer/ubon',
rules: Array.from(rulesMap.values()),
},
},
results: sarifResults,
},
],
};
return sarif;
}
function normalizePath(p) {
// Keep relative paths for SARIF consumers
return p.replace(/\\/g, '/');
}
//# sourceMappingURL=sarif.js.map