pr-checkmate 3.2.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import type { GrypeScanEnvOverrides, PRCheckMateConfig } from '../../../interfaces/index.js';
1
2
  import { Check } from '../../types.js';
2
3
  /**
3
4
  * Dependency vulnerability scan via Anchore's `grype`. Non-blocking and ON by
@@ -19,4 +20,11 @@ import { Check } from '../../types.js';
19
20
  * installed `node_modules` is minutes of work whose every finding this check then
20
21
  * throws away as third-party.
21
22
  */
23
+ /**
24
+ * The effective grypeScan settings for this run: the top-level values, with the
25
+ * `ci` or `local` block layered on top depending on where we are running. Only
26
+ * keys actually set in the block win, so `local: { enabled: false }` leaves the
27
+ * severity and ignore list alone.
28
+ */
29
+ export declare function resolveGrypeScan(config: PRCheckMateConfig['grypeScan'], env?: NodeJS.ProcessEnv): GrypeScanEnvOverrides;
22
30
  export declare const grypeScanCheck: Check;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.grypeScanCheck = void 0;
4
+ exports.resolveGrypeScan = resolveGrypeScan;
4
5
  const execa_js_1 = require("../../../utils/execa.js");
5
6
  const index_js_1 = require("../../../utils/index.js");
6
7
  const types_js_1 = require("../../types.js");
@@ -43,17 +44,30 @@ function severityRank(severity) {
43
44
  * installed `node_modules` is minutes of work whose every finding this check then
44
45
  * throws away as third-party.
45
46
  */
47
+ /**
48
+ * The effective grypeScan settings for this run: the top-level values, with the
49
+ * `ci` or `local` block layered on top depending on where we are running. Only
50
+ * keys actually set in the block win, so `local: { enabled: false }` leaves the
51
+ * severity and ignore list alone.
52
+ */
53
+ function resolveGrypeScan(config, env = process.env) {
54
+ const { ci, local, ...base } = config ?? {};
55
+ const overrides = ((0, index_js_1.isCiEnvironment)(env) ? ci : local) ?? {};
56
+ const defined = Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== undefined));
57
+ return { ...base, ...defined };
58
+ }
46
59
  exports.grypeScanCheck = {
47
60
  name: 'Grype Scan',
48
61
  phase: 'informational',
49
62
  weight: 'heavy',
50
63
  applies(ctx) {
51
- return ctx.config.grypeScan?.enabled !== false;
64
+ return resolveGrypeScan(ctx.config.grypeScan).enabled !== false;
52
65
  },
53
66
  async run(ctx) {
54
- const minSeverity = (ctx.config.grypeScan?.minSeverity ?? 'medium').toLowerCase();
67
+ const settings = resolveGrypeScan(ctx.config.grypeScan);
68
+ const minSeverity = (settings.minSeverity ?? 'medium').toLowerCase();
55
69
  const minRank = severityRank(minSeverity);
56
- const ignore = (ctx.config.grypeScan?.ignore ?? []).map(s => s.toLowerCase());
70
+ const ignore = (settings.ignore ?? []).map(s => s.toLowerCase());
57
71
  // Never walk `ignoreDirs` — the same contract every other check honours, and
58
72
  // here it is the difference between a usable check and an unusable one. A bare
59
73
  // `grype dir:.` catalogs every file under `node_modules`, which on this repo took
@@ -61,7 +75,7 @@ exports.grypeScanCheck = {
61
75
  // findings: grype resolves package versions from the lockfile, and installed
62
76
  // third-party code is not something the client could fix anyway (see below).
63
77
  const excludes = (ctx.config.ignoreDirs ?? []).flatMap(dir => ['--exclude', `**/${dir}/**`]);
64
- const timeout = ctx.config.grypeScan?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
78
+ const timeout = settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
65
79
  const result = await (0, execa_js_1.execa)('grype', ['dir:.', '-o', 'json', '-q', ...excludes], {
66
80
  cwd: ctx.cwd,
67
81
  reject: false,
@@ -369,6 +369,13 @@ export interface PRCheckMateConfig {
369
369
  * can never stall a commit.
370
370
  */
371
371
  timeoutMs?: number;
372
+ /**
373
+ * Overrides applied only on a CI build (see `isCiEnvironment`). Any key set here wins
374
+ * over the top-level value, so CI can keep the scan while local runs turn it off.
375
+ */
376
+ ci?: GrypeScanEnvOverrides;
377
+ /** Same as `ci`, applied only outside CI (a developer's machine, `be-commit`, hooks). */
378
+ local?: GrypeScanEnvOverrides;
372
379
  };
373
380
  bannedImports?: Array<{
374
381
  name: string;
@@ -430,3 +437,10 @@ export interface PRCheckMateConfig {
430
437
  }
431
438
  export type { CheckStatus, CheckResult } from '../utils/result-collector.js';
432
439
  export type { Language } from '../utils/detect-language.js';
440
+ /** Per-environment overrides for `grypeScan`: the subset of its settings that may differ by environment. */
441
+ export interface GrypeScanEnvOverrides {
442
+ enabled?: boolean;
443
+ minSeverity?: 'negligible' | 'low' | 'medium' | 'high' | 'critical';
444
+ ignore?: string[];
445
+ timeoutMs?: number;
446
+ }
@@ -1,3 +1,4 @@
1
+ import type { GrypeScanEnvOverrides, PRCheckMateConfig } from '../../../interfaces/index.js';
1
2
  import { Check } from '../../types.js';
2
3
  /**
3
4
  * Dependency vulnerability scan via Anchore's `grype`. Non-blocking and ON by
@@ -19,4 +20,11 @@ import { Check } from '../../types.js';
19
20
  * installed `node_modules` is minutes of work whose every finding this check then
20
21
  * throws away as third-party.
21
22
  */
23
+ /**
24
+ * The effective grypeScan settings for this run: the top-level values, with the
25
+ * `ci` or `local` block layered on top depending on where we are running. Only
26
+ * keys actually set in the block win, so `local: { enabled: false }` leaves the
27
+ * severity and ignore list alone.
28
+ */
29
+ export declare function resolveGrypeScan(config: PRCheckMateConfig['grypeScan'], env?: NodeJS.ProcessEnv): GrypeScanEnvOverrides;
22
30
  export declare const grypeScanCheck: Check;
@@ -1,5 +1,5 @@
1
1
  import { execa } from '../../../utils/execa.js';
2
- import { collectDirectDeps, logger, normalizeName } from '../../../utils/index.js';
2
+ import { collectDirectDeps, isCiEnvironment, logger, normalizeName, } from '../../../utils/index.js';
3
3
  import { pass, skip, warn } from '../../types.js';
4
4
  import { notInstalled } from '../tool-install.js';
5
5
  const SEVERITY_ORDER = ['negligible', 'low', 'medium', 'high', 'critical'];
@@ -40,17 +40,30 @@ function severityRank(severity) {
40
40
  * installed `node_modules` is minutes of work whose every finding this check then
41
41
  * throws away as third-party.
42
42
  */
43
+ /**
44
+ * The effective grypeScan settings for this run: the top-level values, with the
45
+ * `ci` or `local` block layered on top depending on where we are running. Only
46
+ * keys actually set in the block win, so `local: { enabled: false }` leaves the
47
+ * severity and ignore list alone.
48
+ */
49
+ export function resolveGrypeScan(config, env = process.env) {
50
+ const { ci, local, ...base } = config ?? {};
51
+ const overrides = (isCiEnvironment(env) ? ci : local) ?? {};
52
+ const defined = Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== undefined));
53
+ return { ...base, ...defined };
54
+ }
43
55
  export const grypeScanCheck = {
44
56
  name: 'Grype Scan',
45
57
  phase: 'informational',
46
58
  weight: 'heavy',
47
59
  applies(ctx) {
48
- return ctx.config.grypeScan?.enabled !== false;
60
+ return resolveGrypeScan(ctx.config.grypeScan).enabled !== false;
49
61
  },
50
62
  async run(ctx) {
51
- const minSeverity = (ctx.config.grypeScan?.minSeverity ?? 'medium').toLowerCase();
63
+ const settings = resolveGrypeScan(ctx.config.grypeScan);
64
+ const minSeverity = (settings.minSeverity ?? 'medium').toLowerCase();
52
65
  const minRank = severityRank(minSeverity);
53
- const ignore = (ctx.config.grypeScan?.ignore ?? []).map(s => s.toLowerCase());
66
+ const ignore = (settings.ignore ?? []).map(s => s.toLowerCase());
54
67
  // Never walk `ignoreDirs` — the same contract every other check honours, and
55
68
  // here it is the difference between a usable check and an unusable one. A bare
56
69
  // `grype dir:.` catalogs every file under `node_modules`, which on this repo took
@@ -58,7 +71,7 @@ export const grypeScanCheck = {
58
71
  // findings: grype resolves package versions from the lockfile, and installed
59
72
  // third-party code is not something the client could fix anyway (see below).
60
73
  const excludes = (ctx.config.ignoreDirs ?? []).flatMap(dir => ['--exclude', `**/${dir}/**`]);
61
- const timeout = ctx.config.grypeScan?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
74
+ const timeout = settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
62
75
  const result = await execa('grype', ['dir:.', '-o', 'json', '-q', ...excludes], {
63
76
  cwd: ctx.cwd,
64
77
  reject: false,
@@ -369,6 +369,13 @@ export interface PRCheckMateConfig {
369
369
  * can never stall a commit.
370
370
  */
371
371
  timeoutMs?: number;
372
+ /**
373
+ * Overrides applied only on a CI build (see `isCiEnvironment`). Any key set here wins
374
+ * over the top-level value, so CI can keep the scan while local runs turn it off.
375
+ */
376
+ ci?: GrypeScanEnvOverrides;
377
+ /** Same as `ci`, applied only outside CI (a developer's machine, `be-commit`, hooks). */
378
+ local?: GrypeScanEnvOverrides;
372
379
  };
373
380
  bannedImports?: Array<{
374
381
  name: string;
@@ -430,3 +437,10 @@ export interface PRCheckMateConfig {
430
437
  }
431
438
  export type { CheckStatus, CheckResult } from '../utils/result-collector.js';
432
439
  export type { Language } from '../utils/detect-language.js';
440
+ /** Per-environment overrides for `grypeScan`: the subset of its settings that may differ by environment. */
441
+ export interface GrypeScanEnvOverrides {
442
+ enabled?: boolean;
443
+ minSeverity?: 'negligible' | 'low' | 'medium' | 'high' | 'critical';
444
+ ignore?: string[];
445
+ timeoutMs?: number;
446
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-checkmate",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Security-first PR gate for CI: secret scanning, grype dependency scanning, SBOM — plus linting and formatting across 11 languages",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -221,6 +221,14 @@
221
221
  "timeoutMs": {
222
222
  "type": "number",
223
223
  "default": 120000
224
+ },
225
+ "ci": {
226
+ "$ref": "#/$defs/grypeScanEnvOverrides",
227
+ "description": "Overrides applied only on a CI build."
228
+ },
229
+ "local": {
230
+ "$ref": "#/$defs/grypeScanEnvOverrides",
231
+ "description": "Overrides applied only outside CI (developer machine, local commits)."
224
232
  }
225
233
  },
226
234
  "description": "Direct-dependency vulnerability scan via grype."
@@ -1067,6 +1075,18 @@
1067
1075
  }
1068
1076
  },
1069
1077
  "$defs": {
1078
+ "grypeScanEnvOverrides": {
1079
+ "type": "object",
1080
+ "properties": {
1081
+ "enabled": { "type": "boolean" },
1082
+ "minSeverity": {
1083
+ "type": "string",
1084
+ "enum": ["negligible", "low", "medium", "high", "critical"]
1085
+ },
1086
+ "ignore": { "type": "array", "items": { "type": "string" } },
1087
+ "timeoutMs": { "type": "number" }
1088
+ }
1089
+ },
1070
1090
  "toggle": {
1071
1091
  "type": "object",
1072
1092
  "properties": {
@@ -221,6 +221,14 @@
221
221
  "timeoutMs": {
222
222
  "type": "number",
223
223
  "default": 120000
224
+ },
225
+ "ci": {
226
+ "$ref": "#/$defs/grypeScanEnvOverrides",
227
+ "description": "Overrides applied only on a CI build."
228
+ },
229
+ "local": {
230
+ "$ref": "#/$defs/grypeScanEnvOverrides",
231
+ "description": "Overrides applied only outside CI (developer machine, local commits)."
224
232
  }
225
233
  },
226
234
  "description": "Direct-dependency vulnerability scan via grype."
@@ -1067,6 +1075,18 @@
1067
1075
  }
1068
1076
  },
1069
1077
  "$defs": {
1078
+ "grypeScanEnvOverrides": {
1079
+ "type": "object",
1080
+ "properties": {
1081
+ "enabled": { "type": "boolean" },
1082
+ "minSeverity": {
1083
+ "type": "string",
1084
+ "enum": ["negligible", "low", "medium", "high", "critical"]
1085
+ },
1086
+ "ignore": { "type": "array", "items": { "type": "string" } },
1087
+ "timeoutMs": { "type": "number" }
1088
+ }
1089
+ },
1070
1090
  "toggle": {
1071
1091
  "type": "object",
1072
1092
  "properties": {