Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

node-sarif-builder

nvuillam749.3kMIT3.2.0TypeScript support: included

Module to help building SARIF log files

sarif, builder, node, simple, linter, output, format

readme

node-sarif-builder

Version Downloads/week Downloads/total Generated by github-dependents-info Test Mega-Linter GitHub contributors GitHub stars License PRs Welcome

Introduction

Until today, every SAST tool (not exhaustive list available at https://analysis-tools.dev/) is using its own custom output format.

In order to unify SAST tools output format, more and more tools and services are implementing SARIF format (example)

SARIF logs can be:

Example of linters that can output logs in SARIF format:

  • bandit (python)
  • checkov (terraform)
  • checkstyle (java)
  • cfn-lint (AWS CloudFormation)
  • codeql (multi-language)
  • devskim (security)
  • eslint (javascript,typescript,json)
  • gitleaks (security)
  • ktlint (Kotlin)
  • hadolint (Dockerfile)
  • MegaLinter (linters orchestrator)
  • psalm (php)
  • semgrep (multi-language)
  • revive (Go)
  • tflint (terraform)
  • terrascan (terrasform)
  • trivy (security)
  • and many more...

If you are a maintainer of any javascript/typescript based SAST tool, but also IaC tool, or any type of tool that can return a list of errors with a level of severity, you can either:

  • read the whole OASIS Specification and implement it
  • simply use this library to add SARIF as additional output format, so your tool will be natively compliant with any of SARIF-compliant tools !

Installation

  • with npm
npm install node-sarif-builder
  • with yarn
yarn add node-sarif-builder

Use

With node-sarif-builder, you can generate complex SARIF format with simple methods


  • Start by importing module
const { SarifBuilder, SarifRunBuilder, SarifResultBuilder, SarifRuleBuilder } = require("node-sarif-builder");

  • Create and init SarifBuilder and SarifRunBuilder objects
// SARIF builder
const sarifBuilder = new SarifBuilder();
// SARIF Run builder
const sarifRunBuilder = new SarifRunBuilder().initSimple({
    toolDriverName: "npm-groovy-lint",                           // Name of your analyzer tool
    toolDriverVersion: "9.0.5",                                  // Version of your analyzer tool
    url: "https://nvuillam.github.io/npm-groovy-lint/"           // Url of your analyzer tool
});

  • Add all rules that can be found in your results (recommended but optional)
// Add SARIF rules
for (const rule of rules) {                           // rules from your linter in any format
    const sarifRuleBuiler = new SarifRuleBuilder().initSimple({
        ruleId: rule.id,                              // ex: "no-any"
        shortDescriptionText: rule.description,       // ex: "Do not use any in your code !"
        helpUri: rule.docUrl                          // ex: "http://my.linter.com/rules/no-any"
    });
    sarifRunBuilder.addRule(sarifRuleBuiler);
}

  • For each found issue, create a SarifResultBuilder and add it to the SarifRunBuilder object
import { pathToFileURL } from 'url'

// Add results
for (const issue of issues) { // issues from your linter in any format
    const sarifResultBuilder = new SarifResultBuilder();
    const sarifResultInit = {
         // Transcode to a SARIF level:  can be "warning" or "error" or "note"
        level: issue.severity === "info" ? "note" : issue.severity,
        messageText: err.msg,                                     // Ex: "any is forbidden !"
        ruleId: err.rule,                                         // Ex: "no-any"
        fileUri: process.env.SARIF_URI_ABSOLUTE                   // Ex: src/myfile.ts
            ? pathToFileURL(fileNm)
            : path.relative(process.cwd(), fileNm).replace(/\\/g, '/'),
    };
    // When possible, provide location of the issue in the source code
    if (issue.range) {
        sarifResultInit.startLine = issue.range.start.line;        // any integer >= 1 (optional)
        sarifResultInit.startColumn = issue.range.start.character; // any integer >= 1 (optional)
        sarifResultInit.endLine = issue.range.end.line;            // any integer >= 1 (optional)
        sarifResultInit.endColumn = issue.range.end.character;     // any integer >= 1 (optional)
    }
    // Init sarifResultBuilder
    sarifResultBuilder.initSimple(sarifResultInit); 
    // Add result to sarifRunBuilder
    sarifRunBuilder.addResult(sarifResultBuilder);
}

  • Add run to sarifBuilder then generate JSON SARIF output file
    sarifBuilder.addRun(sarifRunBuilder);
    const sarifJsonString = sarifBuilder.buildSarifJsonString({ indent: false }); // indent:true if you like
    fs.writeFileSync(outputSarifFile,sarifJsonString);
    // const sarifObj = sarifBuilder.buildSarifOutput();                          // You could also just get the Sarif log as an object and not a string

Full example

import { pathToFileURL } from 'url'

function buildSarifResult(lintResult) {
    // SARIF builder
    const sarifBuilder = new SarifBuilder();
    // SARIF Run builder
    const sarifRunBuilder = new SarifRunBuilder().initSimple({
        toolDriverName: "npm-groovy-lint",
        toolDriverVersion: "9.0.5", 
        url: "https://nvuillam.github.io/npm-groovy-lint/"
    });
    // SARIF rules
    for (const ruleId of Object.keys(lintResult.rules || {})) {
        const rule = lintResult.rules[ruleId];
        const sarifRuleBuiler = new SarifRuleBuilder().initSimple({
            ruleId: ruleId,
            shortDescriptionText: rule.description,
            helpUri: rule.docUrl
        });
        sarifRunBuilder.addRule(sarifRuleBuiler);
    }
    // Add SARIF results (individual errors)
    for (const fileNm of Object.keys(lintResult.files)) {
        const fileErrors = lintResult.files[fileNm].errors;
        for (const err of fileErrors) {
            const sarifResultBuilder = new SarifResultBuilder();
            const sarifResultInit = {
                level: err.severity === "info" ? "note" : err.severity, // Other values can be "warning" or "error"
                messageText: err.msg,
                ruleId: err.rule,
                fileUri: process.env.SARIF_URI_ABSOLUTE
                    ? pathToFileURL(fileNm)
                    : path.relative(process.cwd(), fileNm).replace(/\\/g, '/')
            };
            if (err.range) {
                sarifResultInit.startLine = fixLine(err.range.start.line);
                sarifResultInit.startColumn = fixCol(err.range.start.character);
                sarifResultInit.endLine = fixLine(err.range.end.line);
                sarifResultInit.endColumn = fixCol(err.range.end.character);
            }
            sarifResultBuilder.initSimple(sarifResultInit);
            sarifRunBuilder.addResult(sarifResultBuilder);
        }
    }
    sarifBuilder.addRun(sarifRunBuilder);
    return sarifBuilder.buildSarifJsonString({ indent: false });
}

function fixLine(val) {
    if (val === null) {
        return undefined;
    }
    return val === 0 ? 1 : val;
}

function fixCol(val) {
    if (val === null) {
        return undefined;
    }
    return val === 0 ? 1 : val + 1;
}

Test

You can confirm that your generated SARIF logs are valid on https://sarifweb.azurewebsites.net/Validation

changelog

Changelog

Beta

[3.2.0] 2024-08-22

  • Upgrade npm dependencies
  • Upgrade MegaLinter to v8

[3.1.0] 2024-16-03

  • Update schema to final 2.1.0 release (but the schema definition remains the same)
    • New value: $schema: 'http://json.schemastore.org/sarif-2.1.0.json'
    • Previous value: $schema: 'http://json.schemastore.org/sarif-2.1.0-rtm.5.json'
  • Add github-dependents-info workflow

[3.0.0] 2023-11-26

  • BREAKING CHANGE: New minimal version of Node.js: v18
  • Upgrade Github Actions workflows
  • Upgrade MegaLinter
  • Upgrade npm dependencies

[2.0.3] 2022-10-23

  • Run yarn-audit-fix to upgrade dependencies with security issues (minimist, node-fetch)
  • CI: Upgrade MegaLinter to v6

[2.0.2] 2022-01-30

  • Update License to MIT

[2.0.1] 2022-01-12

[2.0.0] 2022-01-12

  • Mandatory properties toolDriverName and toolDriverVersion for SarifRunBuilder
  • Change default schema version to https://www.schemastore.org/schemas/json/sarif-2.1.0-rtm.5.json
  • When possible, automatically populate SARIF properties:
    • artifact.sourceLanguage
    • result.locations.location.physicalLocation.artifactLocation.index
    • result.ruleIndex
  • Fix bug when initSimple is called without region properties

[1.0.0] 2022-01-11

  • Initial version