@sasjs/lint 2.5.0 → 2.6.0

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.
@@ -25,7 +25,11 @@ jobs:
25
25
  - name: Install Dependencies
26
26
  run: npm ci
27
27
  - name: Security Audit
28
- run: npm audit --audit-level=low
28
+ # `npm audit` fails on advisories inside the `npm` package bundled by
29
+ # @semantic-release/npm, which no overrides entry can reach. The audit
30
+ # script names those and fails on everything else, so a new advisory
31
+ # anywhere in the tree still fails the build.
32
+ run: npm run audit
29
33
  - name: Check Code Style
30
34
  run: npm run lint
31
35
  - name: Run Unit Tests
@@ -1,6 +1,10 @@
1
+ import { Diagnostic, LintConfig } from '../types';
1
2
  /**
2
3
  * Analyses and produces a set of diagnostics for the given text content.
3
4
  * @param {string} text - the text content to be linted.
4
- * @returns {Diagnostic[]} array of diagnostic objects, each containing a warning, line number and column number.
5
+ * @param {LintConfig} configuration - an optional lint configuration. The
6
+ * nearest `.sasjslint` file provides the configuration when it is omitted.
7
+ * @returns {Diagnostic[]} array of diagnostic objects, each containing a
8
+ * warning, line number and column number.
5
9
  */
6
- export declare const lintText: (text: string) => Promise<import("..").Diagnostic[]>;
10
+ export declare const lintText: (text: string, configuration?: LintConfig) => Promise<Diagnostic[]>;
package/lint/lintText.js CHANGED
@@ -10,15 +10,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.lintText = void 0;
13
- const getLintConfig_1 = require("../utils/getLintConfig");
13
+ const utils_1 = require("../utils");
14
14
  const shared_1 = require("./shared");
15
15
  /**
16
16
  * Analyses and produces a set of diagnostics for the given text content.
17
17
  * @param {string} text - the text content to be linted.
18
- * @returns {Diagnostic[]} array of diagnostic objects, each containing a warning, line number and column number.
18
+ * @param {LintConfig} configuration - an optional lint configuration. The
19
+ * nearest `.sasjslint` file provides the configuration when it is omitted.
20
+ * @returns {Diagnostic[]} array of diagnostic objects, each containing a
21
+ * warning, line number and column number.
19
22
  */
20
- const lintText = (text) => __awaiter(void 0, void 0, void 0, function* () {
21
- const config = yield (0, getLintConfig_1.getLintConfig)();
23
+ const lintText = (text, configuration) => __awaiter(void 0, void 0, void 0, function* () {
24
+ const config = configuration || (yield (0, utils_1.getLintConfig)());
22
25
  return (0, shared_1.processText)(text, config);
23
26
  });
24
27
  exports.lintText = lintText;
@@ -1 +1 @@
1
- {"version":3,"file":"lintText.js","sourceRoot":"","sources":["../../src/lint/lintText.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,0DAAsD;AACtD,qCAAsC;AAEtC;;;;GAIG;AACI,MAAM,QAAQ,GAAG,CAAO,IAAY,EAAE,EAAE;IAC7C,MAAM,MAAM,GAAG,MAAM,IAAA,6BAAa,GAAE,CAAA;IACpC,OAAO,IAAA,oBAAW,EAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AAClC,CAAC,CAAA,CAAA;AAHY,QAAA,QAAQ,YAGpB"}
1
+ {"version":3,"file":"lintText.js","sourceRoot":"","sources":["../../src/lint/lintText.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,oCAAwC;AACxC,qCAAsC;AAEtC;;;;;;;GAOG;AACI,MAAM,QAAQ,GAAG,CACtB,IAAY,EACZ,aAA0B,EACH,EAAE;IACzB,MAAM,MAAM,GAAG,aAAa,IAAI,CAAC,MAAM,IAAA,qBAAa,GAAE,CAAC,CAAA;IACvD,OAAO,IAAA,oBAAW,EAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AAClC,CAAC,CAAA,CAAA;AANY,QAAA,QAAQ,YAMpB"}
package/package.json CHANGED
@@ -10,7 +10,8 @@
10
10
  "package:lib": "npm run build && cp ./package.json ./checkNodeVersion.js build && cp README.md build && cd build && npm version \"5.0.0\" && npm pack",
11
11
  "lint:fix": "npx prettier --write \"{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
12
12
  "lint": "npx prettier --check \"{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
13
- "prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
13
+ "prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true",
14
+ "audit": "node scripts/audit.mjs"
14
15
  },
15
16
  "publishConfig": {
16
17
  "access": "public"
@@ -71,5 +72,5 @@
71
72
  "@sasjs/utils": "3.6.2",
72
73
  "ignore": "7.0.8"
73
74
  },
74
- "version": "2.5.0"
75
+ "version": "2.6.0"
75
76
  }
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * A strict dependency audit.
4
+ *
5
+ * `npm audit` fails on ANY advisory, which cannot pass in this repo: the
6
+ * advisories below sit inside the `npm` package that `@semantic-release/npm`
7
+ * depends on, in its bundled tree, where no `overrides` entry can reach them.
8
+ * npm 12.1.0 bundles the same versions as 11.20.0, so no upgrade clears them.
9
+ * They are unreachable from this package's own code and its runtime
10
+ * dependencies.
11
+ *
12
+ * Lowering the threshold for everything would hide a new advisory anywhere
13
+ * else, so instead they are named below and every other advisory fails the
14
+ * build - including one of the same severity. If one disappears upstream, this
15
+ * script says so, so the exemption does not outlive its reason.
16
+ *
17
+ * Run with `npm run audit`.
18
+ */
19
+ import { execFileSync } from 'node:child_process'
20
+
21
+ /**
22
+ * Advisories that cannot be fixed from this repo, and why.
23
+ */
24
+ const EXEMPT = new Map([
25
+ ['GHSA-rpw4-54j3-4h4q', 'ip-address, inside the npm package bundled by @semantic-release/npm'],
26
+ ['GHSA-2vr4-cq9g-pvrc', 'ip-address, inside the npm package bundled by @semantic-release/npm'],
27
+ ['GHSA-3wwx-pv8p-q78v', 'undici, inside the npm package bundled by @semantic-release/npm'],
28
+ // The advisories below landed upstream after the exemptions above were
29
+ // written, against the same bundled copies. npm 12.1.0 bundles identical
30
+ // versions of all three packages, so an upgrade clears none of them.
31
+ ['GHSA-j6r3-76f7-8jcv', 'ip-address, inside the npm package bundled by @semantic-release/npm'],
32
+ ['GHSA-h3mg-xc3c-68pw', 'ip-address, inside the npm package bundled by @semantic-release/npm'],
33
+ ['GHSA-r53p-7pc4-xj5r', 'undici, inside the npm package bundled by @semantic-release/npm'],
34
+ ['GHSA-rfgv-xxqx-mfg5', 'undici, inside the npm package bundled by @semantic-release/npm'],
35
+ [
36
+ 'GHSA-q2hr-2g5m-vwhr',
37
+ 'brace-expansion, inside the npm package bundled by @semantic-release/npm'
38
+ ],
39
+ [
40
+ 'GHSA-qhr7-859c-m2p7',
41
+ 'brace-expansion, inside the npm package bundled by @semantic-release/npm'
42
+ ],
43
+ [
44
+ 'GHSA-6j4f-fj2g-mc7p',
45
+ 'brace-expansion, inside the npm package bundled by @semantic-release/npm'
46
+ ]
47
+ ])
48
+
49
+ const run = () => {
50
+ try {
51
+ return execFileSync('npm', ['audit', '--json'], {
52
+ encoding: 'utf8',
53
+ stdio: ['ignore', 'pipe', 'ignore']
54
+ })
55
+ } catch (error) {
56
+ // npm audit exits non-zero when it finds anything, which is the case we are
57
+ // here to read, so the report on stdout is the payload either way.
58
+ if (error.stdout) return error.stdout
59
+ throw error
60
+ }
61
+ }
62
+
63
+ let report
64
+ try {
65
+ report = JSON.parse(run())
66
+ } catch {
67
+ console.error('could not read the npm audit report')
68
+ process.exit(1)
69
+ }
70
+
71
+ const ghsaOf = (via) => {
72
+ if (typeof via === 'string') return undefined
73
+ const match = /GHSA-[a-z0-9-]+/i.exec(via?.url ?? '')
74
+ return match ? match[0] : undefined
75
+ }
76
+
77
+ const found = new Map()
78
+ for (const vulnerability of Object.values(report.vulnerabilities ?? {})) {
79
+ for (const via of vulnerability.via ?? []) {
80
+ const id = ghsaOf(via)
81
+ if (!id) continue
82
+ if (!found.has(id)) {
83
+ found.set(id, {
84
+ id,
85
+ title: via.title ?? '',
86
+ severity: via.severity ?? vulnerability.severity,
87
+ packages: new Set()
88
+ })
89
+ }
90
+ found.get(id).packages.add(vulnerability.name)
91
+ }
92
+ }
93
+
94
+ const exempt = [...found.keys()].filter((id) => EXEMPT.has(id))
95
+ const failing = [...found.values()].filter((entry) => !EXEMPT.has(entry.id))
96
+ const stale = [...EXEMPT.keys()].filter((id) => !found.has(id))
97
+
98
+ console.log(`advisories found: ${found.size}`)
99
+ for (const entry of found.values()) {
100
+ const mark = EXEMPT.has(entry.id) ? 'exempt' : 'FAIL '
101
+ console.log(
102
+ ` ${mark} ${entry.severity.padEnd(8)} ${entry.id} ${[...entry.packages].join(', ')}`
103
+ )
104
+ }
105
+
106
+ for (const id of stale) {
107
+ console.log(` GONE ${id} no longer reported - remove its exemption`)
108
+ }
109
+
110
+ if (failing.length === 0 && stale.length === 0) {
111
+ console.log(
112
+ `audit clean: ${exempt.length} exempt, ${stale.length} stale, ${failing.length} failing`
113
+ )
114
+ process.exit(0)
115
+ }
116
+
117
+ if (stale.length > 0) {
118
+ console.error(
119
+ `${stale.length} exemption(s) no longer match a reported advisory.`
120
+ )
121
+ }
122
+
123
+ if (failing.length > 0) {
124
+ console.error(`${failing.length} advisory(ies) are NOT exempt:`)
125
+ for (const entry of failing) {
126
+ console.error(` ${entry.severity} ${entry.id} ${entry.title}`)
127
+ console.error(` ${[...entry.packages].join(', ')}`)
128
+ }
129
+ }
130
+
131
+ process.exit(1)