From 5e5b65f9d1fa6e9ec08ccc47fe977070d17b781e Mon Sep 17 00:00:00 2001 From: Kevin de Jong Date: Wed, 2 Aug 2023 15:04:15 +0200 Subject: [PATCH] feat: parse SARIF files into objects --- .reuse/dep5 | 4 ++ package-lock.json | 4 +- package.json | 2 +- src/sarif/log.ts | 30 ++++++++ test/fixtures/example.sarif | 133 ++++++++++++++++++++++++++++++++++++ test/index.test.ts | 24 +++++-- 6 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/example.sarif diff --git a/.reuse/dep5 b/.reuse/dep5 index de68f31..4393a8d 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -10,3 +10,7 @@ License: CC0-1.0 Files: package*.json Copyright: 2023 Kevin de Jong License: CC0-1.0 + +Files: test/fixtures/example.sarif +Copyright: 2023 Kevin de Jong +License: MIT diff --git a/package-lock.json b/package-lock.json index c9fd324..f649201 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dev-build-deploy/sarif-it", - "version": "0.0.0", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dev-build-deploy/sarif-it", - "version": "0.0.0", + "version": "0.1.0", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.2", diff --git a/package.json b/package.json index ce956b1..911b7ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dev-build-deploy/sarif-it", - "version": "0.0.0", + "version": "0.1.0", "description": "Static Analysis Results Interchange Format Builder Library", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/sarif/log.ts b/src/sarif/log.ts index 0c520e3..c9d39b9 100644 --- a/src/sarif/log.ts +++ b/src/sarif/log.ts @@ -4,8 +4,11 @@ */ import * as sarif from "sarif"; +import * as fs from "fs"; import { Run } from "./run"; import { ISarif } from "../interfaces"; +import { Tool } from "./tool"; +import { Result } from "./result"; /** Static Analysis Results Format (SARIF) Version 2.1.0 */ export class Log extends ISarif { @@ -24,4 +27,31 @@ export class Log extends ISarif { this._data.runs.push(run.properties()); return this; } + + /** + * Creates a new SARIF log object from a file + * + * @param filePath The path to the SARIF file + * @returns A new SARIF log object + */ + static fromFile(filePath: string): Log { + const data = JSON.parse(fs.readFileSync(filePath, { encoding: "utf-8" })); + const log = new Log(); + + if (Object.keys(data).includes("runs") && Array.isArray(data.runs)) { + for (const sarifRun of data.runs) { + const tool = new Tool(sarifRun.tool.driver.name, sarifRun.tool.driver); + const run = new Run(tool); + if (Object.keys(sarifRun).includes("results") && Array.isArray(sarifRun.results)) { + for (const result of sarifRun.results) { + const res = new Result(result.message, result); + run.addResult(res); + } + } + log.addRun(run); + } + } + + return log; + } } diff --git a/test/fixtures/example.sarif b/test/fixtures/example.sarif new file mode 100644 index 0000000..d1a64cc --- /dev/null +++ b/test/fixtures/example.sarif @@ -0,0 +1,133 @@ +{ + "$schema": "http://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "reuse-me", + "version": "0", + "organization": "dev-build-deploy", + "informationUri": "https://github.com/dev-build-deploy/reuse-me#README.md", + "rules": [ + { + "id": "FL001", + "name": "MissingCopyrightInformation", + "shortDescription": { + "text": "Each Covered File MUST have Copyright associated with it.", + "markdown": "Each Covered File **MUST** have **[Copright](https://reuse.software/faq/#what-is-copyright)** associated with it." + } + }, + { + "id": "FL002", + "name": "MissingLicenseInformation", + "shortDescription": { + "text": "Each Covered File MUST have License Information associated with it.", + "markdown": "Each Covered File **MUST** have **[Licensing Information](https://reuse.software/faq/#what-is-license)** associated with it." + } + }, + { + "id": "FL003", + "name": "IncorrectLicenseFormat", + "shortDescription": { + "text": "The SPDX License Identifier ({0}) MUST be LicenseRef-[letters, numbers, \".\", or \"-\"] as defined by the SPDX Specification", + "markdown": "The SPDX License Identifier ({0}) **MUST** be `LicenseRef-`[`letters`, `numbers`, `.`, or `-`] as defined by the [SPDX Specification](https://spdx.github.io/spdx-spec/v2.3/)" + } + }, + { + "id": "PR001", + "name": "MissingLicenseFile", + "shortDescription": { + "text": "The Project MUST include a License File for every license, but is missing {0}.", + "markdown": "The Project **MUST** include a License File for every license, but is missing `${license}`." + } + }, + { + "id": "PR002", + "name": "IncorrectLicenseFile", + "shortDescription": { + "text": "The Project MUST NOT include License Files ({0}) for licenses under which none of the files in the Project are licensed.", + "markdown": "The Project **MUST NOT** include License Files (`{0}`) for licenses under which none of the files in the Project are licensed." + } + }, + { + "id": "PR003", + "name": "DuplicateSpdxIdentifiers", + "shortDescription": { + "text": "The Project MUST NOT include duplicate SPDX identifiers ({0}).", + "markdown": "The Project **MUST NOT** include duplicate SPDX identifiers (`{0}`)." + } + } + ] + } + }, + "results": [ + { + "message": { + "text": "The Project MUST include a License File for every license, but is missing MIT." + }, + "level": "error", + "ruleId": "MissingLicense", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "reuse-me" + } + } + } + ], + "occurrenceCount": 1 + }, + { + "message": { + "text": "Each Covered File MUST have Copyright associated with it." + }, + "level": "error", + "ruleId": "MissingCopyrightInformation", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "./test.sarif" + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "./src/config/tool.json" + } + } + } + ], + "occurrenceCount": 2 + }, + { + "message": { + "text": "Each Covered File MUST have License Information associated with it." + }, + "level": "error", + "ruleId": "MissingLicenseInformation", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "./test.sarif" + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "./src/config/tool.json" + } + } + } + ], + "occurrenceCount": 2 + } + ] + } + ] +} diff --git a/test/index.test.ts b/test/index.test.ts index e360165..d6f9742 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -4,6 +4,7 @@ */ import * as sarif from "../src"; +import * as fs from "fs"; describe("Log", () => { test("Default", () => { @@ -50,7 +51,17 @@ describe("Log", () => { test("Get Missing Property", () => { const log = new sarif.Log(); - expect(() => { log.get("does-not-exist") }).toThrowError("Property 'does-not-exist' does not exist."); + expect(() => { + log.get("does-not-exist"); + }).toThrowError("Property 'does-not-exist' does not exist."); + }); + + test("From File", () => { + const log = sarif.Log.fromFile("test/fixtures/example.sarif"); + expect(log).toBeInstanceOf(sarif.Log); + expect(log.properties()).toStrictEqual( + JSON.parse(fs.readFileSync("test/fixtures/example.sarif", { encoding: "utf-8" })) + ); }); }); @@ -349,7 +360,10 @@ describe("Tool", () => { test("Add Rules", () => { const tool = new sarif.Tool("sarif-it-test"); - tool.addRule(new sarif.Rule("test-rule-a")).addRule(new sarif.Rule("test-rule-b")).addRule(new sarif.Rule("test-rule-a")); + tool + .addRule(new sarif.Rule("test-rule-a")) + .addRule(new sarif.Rule("test-rule-b")) + .addRule(new sarif.Rule("test-rule-a")); expect(tool).toBeInstanceOf(sarif.Tool); expect(tool.properties()).toStrictEqual({ driver: { @@ -362,7 +376,7 @@ describe("Tool", () => { id: "test-rule-a", }, ], - } + }, }); }); @@ -373,6 +387,8 @@ describe("Tool", () => { test("Get Missing Property", () => { const tool = new sarif.Tool("sarif-it-test"); - expect(() => { tool.get("version") }).toThrowError("Property 'version' does not exist."); + expect(() => { + tool.get("version"); + }).toThrowError("Property 'version' does not exist."); }); });