Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ignore warnings via .nsci-ignore file #7

Merged
merged 26 commits into from
Jul 3, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
00c8094
feat: add getIngoreFile fn
tony-go Jun 9, 2022
04c234e
chore: delete di and use mock-fs
tony-go Jun 19, 2022
a8756bc
feat: make validator return error
tony-go Jun 19, 2022
dc9153f
fix: root path
tony-go Jun 19, 2022
0845d8d
feat: add base filter
tony-go Jun 20, 2022
226a533
chore: update ns pakcages
tony-go Jun 20, 2022
d3e6408
fix: types
tony-go Jun 20, 2022
a84d795
Merge branch 'main' into feature/nsci-ignore
tony-go Jun 22, 2022
5b2f2c3
chore(interpret): create hasWarningsIgnorePatterns func
tony-go Jun 23, 2022
49f33ea
chore(adapt): fix import)
tony-go Jun 23, 2022
04fee5a
fix(adapt): dont return ignorePatterns
tony-go Jun 23, 2022
5d28da2
fix(ignore-file): add jsxray types
tony-go Jun 23, 2022
7676d7f
fix: use process.cwd()
tony-go Jun 23, 2022
02e0458
fix: use IgnorePatterns.default instead of creating manual object
tony-go Jun 23, 2022
fc56f99
fix: lint + use IgnorePatterns.default
tony-go Jun 23, 2022
39d2bba
fix: standardizeExternalConfiguration type
tony-go Jun 23, 2022
4212bea
chore: rename filter function
tony-go Jun 23, 2022
ffdc9bd
chore: rename a few variables/func/types
tony-go Jun 27, 2022
6c2825a
test: move test and fix types
tony-go Jun 29, 2022
f0c0fb7
chore: apply linter
tony-go Jun 29, 2022
a2682de
chore(interpret): add fixture generators
tony-go Jun 29, 2022
c5156ce
chore: rename ignore file
tony-go Jul 1, 2022
ef2eac9
fix: create temporary logger abstract
tony-go Jul 1, 2022
0bc588a
chore: apply linter
tony-go Jul 1, 2022
f27eb14
doc: add .nodesecureignore base doc
tony-go Jul 1, 2022
fc25e10
fix: IgnorePatterns & IgnoreWarningsPatterns abstract
tony-go Jul 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 141 additions & 106 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@types/chai": "^4.3.0",
"@types/lodash.set": "^4.3.6",
"@types/mocha": "^9.0.0",
"@types/mock-fs": "^4.13.1",
"@types/node": "^16.11.12",
"@types/pluralize": "^0.0.29",
"@types/sade": "^1.7.4",
Expand All @@ -73,6 +74,7 @@
"eslint-import-resolver-typescript": "^2.5.0",
"eslint-plugin-prettier": "^4.0.0",
"mocha": "^9.1.4",
"mock-fs": "^5.1.2",
"prettier": "^2.5.1",
"rimraf": "^3.0.2",
"ts-node": "^10.4.0",
Expand All @@ -83,6 +85,7 @@
"@nodesecure/scanner": "^3.6.0",
"@nodesecure/vuln": "^1.7.0",
"@slimio/async-cli-spinner": "^0.5.2",
"ajv": "^8.11.0",
"kleur": "^4.1.4",
"lodash.set": "^4.3.2",
"pluralize": "^8.0.0",
Expand Down
48 changes: 46 additions & 2 deletions src/analysis/interpretation/interpret.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,27 @@ import { StandardVulnerability } from "@nodesecure/vuln/types/strategy";
import { expect } from "chai";

// Import Internal Dependencies
import {
IgnorePatterns,
IgnoreWarningsPatterns
} from "../../configuration/external/nodesecure/ignore-file";
import { Nsci } from "../../configuration/standard/index.js";
import * as pipeline from "../../reporting/status.js";
import { DependencyWarning } from "../../types/index.js";

import { runPayloadInterpreter } from "./interpret.js";
import {
runPayloadInterpreter,
filterDependenciesWarnings
} from "./interpret.js";

// CONSTANTS
const kDefaultRuntimeConfiguration: Nsci.Configuration = {
rootDir: process.cwd(),
strategy: Nsci.vulnStrategy.npm,
reporters: [Nsci.reporterTarget.CONSOLE],
vulnerabilitySeverity: Nsci.vulnSeverity.ALL,
warnings: Nsci.warnings.ERROR
warnings: Nsci.warnings.ERROR,
ignorePatterns: IgnorePatterns.default()
};

const kDefaultScannerPayload: Scanner.Payload = {
Expand All @@ -27,6 +36,41 @@ const kDefaultScannerPayload: Scanner.Payload = {
vulnerabilityStrategy: "npm"
};

describe("filterDependenciesWarnings", () => {
it("should not filter warnings if ignorePatterns.warnings is an empty object", () => {
const warnings: DependencyWarning[] = [];
const emptyIgnorePatterns: IgnorePatterns = IgnorePatterns.default();

const filteredWarnings = filterDependenciesWarnings(
warnings,
emptyIgnorePatterns
);

expect(filteredWarnings).to.deep.equal(warnings);
});

it("should filter warnings if ignorePatterns.warnings is not an empty object", () => {
const warnings: DependencyWarning[] = [
{
package: "lodash.difference",
warnings: [{ kind: "unsafe-stmt", location: {} as any }]
}
];
const ignorePatterns: IgnorePatterns = {
warnings: new IgnoreWarningsPatterns({
"unsafe-stmt": ["lodash.difference"]
})
};

const filteredWarnings = filterDependenciesWarnings(
warnings,
ignorePatterns
);

expect(filteredWarnings).to.deep.equal([]);
});
});

/* eslint-disable max-nested-callbacks */
describe("Pipeline check workflow", () => {
describe("When running the payload interpreter", () => {
Expand Down
34 changes: 33 additions & 1 deletion src/analysis/interpretation/interpret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { GlobalWarning } from "@nodesecure/scanner/types/scanner";
import set from "lodash.set";

// Import Internal Dependencies
import { IgnorePatterns } from "../../configuration/external/nodesecure/ignore-file.js";
import { Nsci } from "../../configuration/standard/index.js";
import { pipeline } from "../../reporting/index.js";
import { DependencyWarning } from "../../types/index.js";
import {
extractScannerPayload,
WorkableVulnerability
Expand Down Expand Up @@ -76,6 +78,32 @@ function interpretPayloadChecks(
};
}

export function filterDependenciesWarnings(
tony-go marked this conversation as resolved.
Show resolved Hide resolved
dependenciesWarnings: DependencyWarning[],
ignorePatterns: IgnorePatterns
): DependencyWarning[] {
const isWarningsEmpty =
tony-go marked this conversation as resolved.
Show resolved Hide resolved
ignorePatterns.warnings === undefined ||
Object.keys(ignorePatterns?.warnings).length === 0;
if (isWarningsEmpty) {
return dependenciesWarnings;
}

return dependenciesWarnings.filter(function excludeIgnorableWarnings(
dependencyWarnings
) {
if (
dependencyWarnings.warnings.find((w) =>
tony-go marked this conversation as resolved.
Show resolved Hide resolved
ignorePatterns.warnings.has(w.kind, dependencyWarnings.package)
)
) {
return false;
}

return true;
});
}

/**
* This interpreter accumulates each Check Function output in order to determine
* a global pipeline status and at the same time compact the original payload to
Expand All @@ -89,11 +117,15 @@ export function runPayloadInterpreter(
rc: Nsci.Configuration
): OutcomePayloadFromPipelineChecks {
const { warnings, dependencies } = extractScannerPayload(payload);
const filteredDependencies = filterDependenciesWarnings(
dependencies.warnings,
rc.ignorePatterns
);

/* eslint-disable @typescript-eslint/explicit-function-return-type */
return interpretPayloadChecks([
() => checkGlobalWarnings(warnings),
() => checkDependenciesWarnings(dependencies.warnings, rc),
() => checkDependenciesWarnings(filteredDependencies, rc),
() => checkDependenciesVulns(dependencies.vulnerabilities, rc)
]);
}
5 changes: 4 additions & 1 deletion src/configuration/external/adapt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { constants, accessSync } from "fs";
import { resolve } from "path";

// Import Internal Dependencies
import { IgnoreWarningsPatterns } from "../external/nodesecure/ignore-file";
tony-go marked this conversation as resolved.
Show resolved Hide resolved
import { Nsci } from "../standard/index.js";

import {
Expand Down Expand Up @@ -121,6 +122,8 @@ export function adaptExternalToStandardConfiguration(
reporters: adaptReporters(reporters),
strategy: adaptStrategy(strategy),
vulnerabilitySeverity: adaptSeverity(vulnerabilities),
warnings: adaptWarnings(warnings)
warnings: adaptWarnings(warnings),
// TODO(tony): check where this is call before merging
ignorePatterns: { warnings: new IgnoreWarningsPatterns() }
tony-go marked this conversation as resolved.
Show resolved Hide resolved
};
}
59 changes: 59 additions & 0 deletions src/configuration/external/nodesecure/ignore-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Validator from "ajv";

export class IgnorePatterns {
public warnings: IgnoreWarningsPatterns;

constructor(warnings: IgnoreWarningsPatterns = new IgnoreWarningsPatterns()) {
this.warnings = warnings;
}

static default(): IgnorePatterns {
return new IgnorePatterns();
}
}

export class IgnoreWarningsPatterns {
public entries: Record<string, string[]>;
tony-go marked this conversation as resolved.
Show resolved Hide resolved

constructor(entries: Record<string, string[]> = {}) {
this.entries = entries;
}

has(warning: string, pkg: string): boolean {
return this.entries[warning]?.includes(pkg);
}
}

const kIgnoreFileSchema = {
type: "object",
properties: {
warnings: {
type: "object",
patternProperties: {
"^[0-9]{2,6}$": {
type: "array",
items: {
type: "string"
}
}
}
}
},
additionalProperties: false
} as const;

export const kIgnoreFileName = ".nsci-ignore";

export function validateIgnoreFile(ignoreFile: string): {
isValid: boolean;
error?: string;
} {
const validator = new Validator();
const validate = validator.compile(kIgnoreFileSchema);
const isValid = validate(ignoreFile);

return {
isValid,
error: validate.errors ? validate?.errors[0]?.message : undefined
};
}
51 changes: 51 additions & 0 deletions src/configuration/external/nodesecure/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Third-party Dependencies
import { expect } from "chai";
import mock from "mock-fs";

// Internal Dependencies
import { IgnorePatterns } from "./ignore-file";

import { getIgnoreFile, kIgnoreFilePath } from "./index";

describe("getIgnoreFile", () => {
const kDefaultIgnoreFileContent = IgnorePatterns.default();

it("should return empty object if file doen't exist", async () => {
const result = await getIgnoreFile();

expect(result).deep.equal(kDefaultIgnoreFileContent);
});

it("should return empty object if file format is invalid", async () => {
const invalidIgnoreFile = { foo: "bar" };
createFakeIgnoreFile(JSON.stringify(invalidIgnoreFile));

const result = await getIgnoreFile();

expect(result).deep.equal(kDefaultIgnoreFileContent);
mock.restore();
});

it("should return the ignore file if it's valid", async () => {
const validIgnoreFile = { warnings: {} };
createFakeIgnoreFile(JSON.stringify(validIgnoreFile));

const result = await getIgnoreFile();

expect(result).to.be.deep.equal(validIgnoreFile);
mock.restore();
});
});

/**
* HELPERS
*/

function createFakeIgnoreFile(fileContent: string): void {
mock(
{
[kIgnoreFilePath]: Buffer.from(fileContent)
},
{} as any
);
}
42 changes: 42 additions & 0 deletions src/configuration/external/nodesecure/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
// Node.Js Dependencies
import { readFile } from "fs/promises";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

// Import Third-party Dependencies
import { RC as NodeSecureRuntimeConfig, read } from "@nodesecure/rc";
import { match } from "ts-pattern";
import type { Result } from "ts-results";

// Import Internal Dependencies
import { consolePrinter } from "../../../../lib/console-printer";
import { Maybe } from "../../../types/index.js";
import {
defaultExternalConfigOptions,
ExternalConfigAdapter,
ExternalRuntimeConfiguration
} from "../common.js";

import {
validateIgnoreFile,
kIgnoreFileName,
IgnorePatterns,
IgnoreWarningsPatterns
} from "./ignore-file";

const { font: log } = consolePrinter;
const __dirname = dirname(fileURLToPath(import.meta.url));
const kRootPath = join(__dirname, "..", "..", "..", "..");
export const kIgnoreFilePath = join(kRootPath, kIgnoreFileName);
tony-go marked this conversation as resolved.
Show resolved Hide resolved

function interpretNodeSecureConfigResult(
config: Result<NodeSecureRuntimeConfig, NodeJS.ErrnoException>
): NodeSecureRuntimeConfig | undefined {
Expand Down Expand Up @@ -55,6 +73,30 @@ export async function getNodeSecureConfig(): Promise<
return interpretNodeSecureConfigResult(config);
}

export async function getIgnoreFile(): Promise<IgnorePatterns> {
try {
const ignoreFile = await readFile(kIgnoreFilePath, "utf8");
const ignoreObject = JSON.parse(ignoreFile);
const { isValid, error } = validateIgnoreFile(ignoreObject);
if (!isValid) {
log
.error(
`x Invalid ignore file: ${error}, empty one will be used instead`
)
.print();

return { warnings: new IgnoreWarningsPatterns() };
tony-go marked this conversation as resolved.
Show resolved Hide resolved
}
log.success("✔ Ignore file loaded").print();

return JSON.parse(ignoreFile) as IgnorePatterns;
} catch (error: any) {
log.error(`x Cannot load ignore file: ${error.message}`).print();

return { warnings: new IgnoreWarningsPatterns() };
tony-go marked this conversation as resolved.
Show resolved Hide resolved
}
}

function adaptNodeSecureConfigToExternalConfig(
runtimeConfig: NodeSecureRuntimeConfig
): ExternalRuntimeConfiguration {
Expand Down
7 changes: 5 additions & 2 deletions src/configuration/external/standardize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RC as NodeSecureRuntimeConfig } from "@nodesecure/rc";
import { expect } from "chai";

// Import Internal Dependencies
import { IgnorePatterns } from "../../configuration/external/nodesecure/ignore-file";
import { Nsci } from "../standard/index.js";

import { ExternalRuntimeConfiguration } from "./common.js";
Expand All @@ -29,7 +30,8 @@ describe("Standardize CLI/API configuration to Nsci runtime configuration", () =
strategy: "NPM_AUDIT",
reporters: ["console", "html"],
vulnerabilitySeverity: "all",
warnings: "error"
warnings: "error",
ignorePatterns: IgnorePatterns.default()
};

expect(
Expand Down Expand Up @@ -131,6 +133,7 @@ it("should standardize NodeSecure runtime configuration to Nsci runtime configur
"encoded-literal": "off",
"unsafe-regex": "error",
"short-identifiers": "warning"
}
},
ignorePatterns: IgnorePatterns.default()
});
});
Loading