export_sarif
Scan a directory and export security results as SARIF v2.1.0 for CI/CD integration with GitHub, GitLab, and Azure DevOps.
Instructions
Scan a directory and export results in SARIF v2.1.0 format for CI/CD integration (GitHub, GitLab, Azure DevOps). Returns JSON string.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Directory to scan |
Implementation Reference
- src/tools/export-sarif.ts:32-104 (handler)Main handler function that walks a directory, analyzes code with check-code, and builds a SARIF 2.1.0 JSON output string.
export function exportSarif(path: string, rules?: SecurityRule[]): string { const scanRoot = resolve(path); const config = loadConfig(scanRoot); const excludes = new Set([...DEFAULT_EXCLUDES, ...config.scan.exclude]); const filePaths: string[] = []; walkDirectory(scanRoot, true, excludes, filePaths); const allResults: SarifResult[] = []; for (const filePath of filePaths) { try { const stat = statSync(filePath); if (stat.size > config.scan.maxFileSize) continue; const content = readFileSync(filePath, "utf-8"); const ext = extname(filePath).toLowerCase(); let language = EXTENSION_MAP[ext]; if (!language && basename(filePath).startsWith("Dockerfile")) language = "dockerfile"; if (!language) continue; const findings = analyzeCode(content, language, undefined, filePath, scanRoot, rules); for (const f of findings) { allResults.push({ ruleId: f.rule.id, level: severityToLevel(f.rule.severity), message: { text: `${f.rule.name}: ${f.rule.description} Fix: ${f.rule.fix}`, }, locations: [{ physicalLocation: { artifactLocation: { uri: filePath }, region: { startLine: f.line }, }, }], }); } } catch { /* skip */ } } // Build SARIF rules from all known rules (deduped by what was found) const foundRuleIds = new Set(allResults.map(r => r.ruleId)); const sarifRules = owaspRules .filter(r => foundRuleIds.has(r.id)) .map(r => ({ id: r.id, name: r.name, shortDescription: { text: r.name }, fullDescription: { text: r.description }, helpUri: `https://guardvibe.dev`, properties: { tags: [r.owasp], ...(r.compliance ? { compliance: r.compliance } : {}), }, })); const sarif = { $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", version: "2.1.0", runs: [{ tool: { driver: { name: "GuardVibe", version: _pkg.version, informationUri: "https://guardvibe.dev", rules: sarifRules, }, }, results: allResults, }], }; return JSON.stringify(sarif, null, 2); } - src/tools/export-sarif.ts:14-24 (schema)SarifResult interface defining the structure of each SARIF result entry.
interface SarifResult { ruleId: string; level: "error" | "warning" | "note"; message: { text: string }; locations: Array<{ physicalLocation: { artifactLocation: { uri: string }; region: { startLine: number }; }; }>; } - src/cli/scan.ts:28-30 (registration)First CLI registration: dynamically imports exportSarif when format is 'sarif'.
if (format === "sarif") { const { exportSarif } = await import("../tools/export-sarif.js"); result = exportSarif(process.cwd()); - src/cli/scan.ts:60-62 (registration)Second CLI registration in runDirectoryScan: dynamically imports exportSarif when format is 'sarif'.
if (format === "sarif") { const { exportSarif } = await import("../tools/export-sarif.js"); result = exportSarif(scanPath); - src/tools/export-sarif.ts:26-30 (helper)Helper function that maps severity strings to SARIF level (error/warning/note).
function severityToLevel(severity: string): "error" | "warning" | "note" { if (severity === "critical" || severity === "high") return "error"; if (severity === "medium") return "warning"; return "note"; }