@openreachtech/mentsu-npm-audit-gate 0.0.0 → 0.1.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.
package/README.ja.md CHANGED
@@ -25,8 +25,8 @@ ES モジュール(`"type": "module"`)です。ESM の `import` 構文でイ
25
25
  リストの項目を検査するには、型ファイルをプロジェクトで 1 度だけ取り込みます。そのうえで、リストに `NpmAuditType.IgnoredAdvisory` を付けます。
26
26
 
27
27
  ```js
28
- // types/npm-audit-gate.d.ts
29
- import '@openreachtech/mentsu-npm-audit-gate/types/npm-audit-gate.d.ts'
28
+ // types/env/npm-audit-gate.d.ts
29
+ import '@openreachtech/mentsu-npm-audit-gate/types/index.d.ts'
30
30
  ```
31
31
 
32
32
  ```js
@@ -56,7 +56,40 @@ process.exitCode = alphaGate.auditAdvisories()
56
56
 
57
57
  リストに挙がっていない advisory がレポートされたとき、リストの項目がレポートされなくなったとき、`npm audit` がレポートを返さなかったときは、終了コード 1 で終わります。それ以外は 0 で終わります。
58
58
 
59
- `NpmAuditType` のほかの型(`Gate`、`Fetcher`、`ReportResponse`、`Logger` と、それぞれの `Ctor`)は、このパッケージの JSDoc を読むので、使うプロジェクトでは、`jsconfig.json` に `allowJs: true` と、3 以上の `maxNodeModuleJsDepth` が必要です。
59
+ `NpmAuditType` のほかの型(クラスごとに、そのクラスの名前の型と、その `Ctor`)は、このパッケージの JSDoc を読むので、使うプロジェクトでは、`jsconfig.json` に `allowJs: true` と、3 以上の `maxNodeModuleJsDepth` が必要です。
60
+
61
+ ### パッケージが配布するスクリプトの実行
62
+
63
+ 監査を実行するスクリプトはパッケージが配布しているので、リポジトリごとにコピーを持つ必要はありません。`package.json` の script から、パスを指定して呼び出してください。
64
+
65
+ 通す advisory を挙げたファイルを、唯一の引数として渡します。ファイルの既定のエクスポートは配列です。
66
+
67
+ ```js
68
+ // scripts/stopgap-audit/IGNORED_ADVISORIES.js
69
+ export default [
70
+ {
71
+ id: 'GHSA-aaaa-bbbb-cccc',
72
+ package: 'alpha-package',
73
+ path: 'beta-package > alpha-package',
74
+ reason: 'No patched release exists, and the package is reached through a development dependency only',
75
+ removeWhen: 'alpha-package publishes a release outside the affected range',
76
+ },
77
+ ]
78
+ ```
79
+
80
+ ```json
81
+ {
82
+ "scripts": {
83
+ "audit:npm": "node node_modules/@openreachtech/mentsu-npm-audit-gate/scripts/npm-audit-gate.js scripts/stopgap-audit/IGNORED_ADVISORIES.js"
84
+ }
85
+ }
86
+ ```
87
+
88
+ ファイルは、作業ディレクトリ基準で解決します。ファイルが無ければ、スクリプトは失敗します。
89
+
90
+ 終了コードは `NpmAuditGate` と同じで、リストを読み込めなかったときも 1 で終わります。
91
+
92
+ このパッケージは、コマンド名を意図して宣言していません。コマンド名は、インストールした全パッケージが共有する `node_modules/.bin/` で解決されるので、同じ名前を宣言した別のパッケージに、警告なしで奪われます。パスは、このパッケージだけを指します。
60
93
 
61
94
  ## API
62
95
 
package/README.md CHANGED
@@ -25,8 +25,8 @@ Write a script that hands the list to `NpmAuditGate`, and run it in the project
25
25
  To check the entries of the list, import the type file once in your project. Then annotate the list with `NpmAuditType.IgnoredAdvisory`.
26
26
 
27
27
  ```js
28
- // types/npm-audit-gate.d.ts
29
- import '@openreachtech/mentsu-npm-audit-gate/types/npm-audit-gate.d.ts'
28
+ // types/env/npm-audit-gate.d.ts
29
+ import '@openreachtech/mentsu-npm-audit-gate/types/index.d.ts'
30
30
  ```
31
31
 
32
32
  ```js
@@ -56,7 +56,40 @@ process.exitCode = alphaGate.auditAdvisories()
56
56
 
57
57
  The process ends with exit code 1 when an advisory the list does not name is reported, when an entry of the list is no longer reported, or when `npm audit` answers with no report. Otherwise it ends with 0.
58
58
 
59
- The other types of `NpmAuditType` (`Gate`, `Fetcher`, `ReportResponse`, `Logger` and their `Ctor`) read the JSDoc of this package, so a project that uses them needs `allowJs: true` and `maxNodeModuleJsDepth` of 3 or more in its `jsconfig.json`.
59
+ The other types of `NpmAuditType` (one for each class, named after it, and its `Ctor`) read the JSDoc of this package, so a project that uses them needs `allowJs: true` and `maxNodeModuleJsDepth` of 3 or more in its `jsconfig.json`.
60
+
61
+ ### Running the script shipped with the package
62
+
63
+ The package ships the script that runs the audit, so a repository does not need to carry a copy of it. Call it by its path from a script of `package.json`.
64
+
65
+ Pass the file that lists the ignored advisories as the only argument. Its default export is the array.
66
+
67
+ ```js
68
+ // scripts/stopgap-audit/IGNORED_ADVISORIES.js
69
+ export default [
70
+ {
71
+ id: 'GHSA-aaaa-bbbb-cccc',
72
+ package: 'alpha-package',
73
+ path: 'beta-package > alpha-package',
74
+ reason: 'No patched release exists, and the package is reached through a development dependency only',
75
+ removeWhen: 'alpha-package publishes a release outside the affected range',
76
+ },
77
+ ]
78
+ ```
79
+
80
+ ```json
81
+ {
82
+ "scripts": {
83
+ "audit:npm": "node node_modules/@openreachtech/mentsu-npm-audit-gate/scripts/npm-audit-gate.js scripts/stopgap-audit/IGNORED_ADVISORIES.js"
84
+ }
85
+ }
86
+ ```
87
+
88
+ The file is resolved against the working directory, and the script fails when it is missing.
89
+
90
+ It ends with the same exit codes as `NpmAuditGate`, and with 1 as well when the list cannot be loaded.
91
+
92
+ The package declares no command name on purpose. A command name is resolved through `node_modules/.bin/`, which every installed package shares, and another package that declares the same name takes it over without any warning. The path names this package alone.
60
93
 
61
94
  ## API
62
95
 
@@ -0,0 +1,96 @@
1
+ import nodeUrl from 'node:url'
2
+
3
+ /**
4
+ * Load the advisories a repository lets through, from the file it keeps them in.
5
+ *
6
+ * The file is the only thing an instance holds, and its path is absolute.
7
+ */
8
+ export default class IgnoredAdvisoriesLoader {
9
+ /**
10
+ * @param {{
11
+ * ignoredAdvisoriesFilePath: string
12
+ * }} params - Parameters of this constructor.
13
+ */
14
+ constructor ({
15
+ ignoredAdvisoriesFilePath,
16
+ }) {
17
+ this.ignoredAdvisoriesFilePath = ignoredAdvisoriesFilePath
18
+ }
19
+
20
+ /**
21
+ * Factory method.
22
+ *
23
+ * @template {X extends typeof IgnoredAdvisoriesLoader ? X : never} T, X
24
+ * @param {{
25
+ * ignoredAdvisoriesFilePath: string
26
+ * }} params - Parameters for the factory method.
27
+ * @returns {InstanceType<T>} Instance of this class.
28
+ * @this {T}
29
+ * @public
30
+ */
31
+ static create ({
32
+ ignoredAdvisoriesFilePath,
33
+ }) {
34
+ return /** @type {InstanceType<T>} */ (
35
+ new this({
36
+ ignoredAdvisoriesFilePath,
37
+ })
38
+ )
39
+ }
40
+
41
+ /**
42
+ * get: `node:url` module.
43
+ *
44
+ * @returns {typeof nodeUrl} `node:url` module.
45
+ */
46
+ static get nodeUrl () {
47
+ return nodeUrl
48
+ }
49
+
50
+ /**
51
+ * get: Constructor of this class.
52
+ *
53
+ * @returns {typeof IgnoredAdvisoriesLoader} Constructor of this class.
54
+ */
55
+ get Ctor () {
56
+ return /** @type {typeof IgnoredAdvisoriesLoader} */ (this.constructor)
57
+ }
58
+
59
+ /**
60
+ * Load the ignored advisories.
61
+ *
62
+ * @returns {Promise<Array<NpmAuditType.IgnoredAdvisory> | null>} Advisories, or null when the file cannot be loaded or its default export is not an array.
63
+ * @public
64
+ */
65
+ async loadAdvisories () {
66
+ try {
67
+ const url = this.Ctor.nodeUrl.pathToFileURL(this.ignoredAdvisoriesFilePath)
68
+
69
+ const exported = await this.invokeImport({
70
+ href: url.href,
71
+ })
72
+
73
+ if (!Array.isArray(exported.default)) {
74
+ return null
75
+ }
76
+
77
+ return exported.default
78
+ } catch (error) {
79
+ return null
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Import a module by the href of its URL.
85
+ *
86
+ * @param {{
87
+ * href: string
88
+ * }} params - Parameters.
89
+ * @returns {Promise<{ default?: * }>} Module namespace.
90
+ */
91
+ async invokeImport ({
92
+ href,
93
+ }) {
94
+ return import(href)
95
+ }
96
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Resolve the arguments the current process was started with.
3
+ */
4
+ export default class ProcessArgumentsResolver {
5
+ /**
6
+ * @param {{
7
+ * actualArguments: Array<string>
8
+ * }} params - Parameters of this constructor.
9
+ */
10
+ constructor ({
11
+ actualArguments,
12
+ }) {
13
+ this.actualArguments = actualArguments
14
+ }
15
+
16
+ /**
17
+ * Factory method.
18
+ *
19
+ * The actual arguments are those that follow the script, so the executable and the script are left out.
20
+ *
21
+ * @template {X extends typeof ProcessArgumentsResolver ? X : never} T, X
22
+ * @param {{
23
+ * rawArguments: Array<string>
24
+ * }} params - Parameters for the factory method.
25
+ * @returns {InstanceType<T>} Instance of this class.
26
+ * @this {T}
27
+ * @public
28
+ */
29
+ static create ({
30
+ rawArguments,
31
+ }) {
32
+ const actualArguments = rawArguments.slice(2)
33
+
34
+ return /** @type {InstanceType<T>} */ (
35
+ new this({
36
+ actualArguments,
37
+ })
38
+ )
39
+ }
40
+
41
+ /**
42
+ * Extract the argument that names the file listing the ignored advisories.
43
+ *
44
+ * @returns {string | null} Argument, or null when it is not given.
45
+ * @public
46
+ */
47
+ extractIgnoredAdvisoriesFilePathArgument () {
48
+ return this.extractArgumentByIndex({
49
+ index: 0,
50
+ })
51
+ }
52
+
53
+ /**
54
+ * Extract the argument at an index.
55
+ *
56
+ * @param {{
57
+ * index: number
58
+ * }} params - Parameters.
59
+ * @returns {string | null} Argument, or null when there is none at the index.
60
+ */
61
+ extractArgumentByIndex ({
62
+ index,
63
+ }) {
64
+ return this.actualArguments.at(index)
65
+ ?? null
66
+ }
67
+ }
@@ -0,0 +1,7 @@
1
+ import {
2
+ RootPath,
3
+ } from '@openreachtech/mentsu-rootpath'
4
+
5
+ const rootPath = RootPath.create()
6
+
7
+ export default rootPath
package/lib/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  export { default as AuditFetcher } from './AuditFetcher.js'
2
2
  export { default as AuditReportResponse } from './AuditReportResponse.js'
3
- export { default as EXIT_CODE } from './EXIT_CODE.js'
3
+ export { default as IgnoredAdvisoriesLoader } from './IgnoredAdvisoriesLoader.js'
4
4
  export { default as NpmAuditGate } from './NpmAuditGate.js'
5
+ export { default as ProcessArgumentsResolver } from './ProcessArgumentsResolver.js'
5
6
  export { default as ProcessLogger } from './ProcessLogger.js'
7
+
8
+ export { default as EXIT_CODE } from './EXIT_CODE.js'
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "@openreachtech/mentsu-npm-audit-gate",
3
- "version": "0.0.0",
3
+ "version": "0.1.0",
4
4
  "description": "Gate that checks the result of npm audit",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "files": [
8
8
  "lib/",
9
+ "scripts/",
10
+ "!scripts/stopgap-audit/IGNORED_ADVISORIES.js",
9
11
  "types/",
10
12
  "!types/env/**"
11
13
  ],
12
14
  "types": "./types/index.d.ts",
13
15
  "scripts": {
16
+ "audit:npm": "node scripts/npm-audit-gate.js scripts/stopgap-audit/IGNORED_ADVISORIES.js",
14
17
  "l": "npm run lint",
15
18
  "lint": "eslint .",
16
19
  "test": "export NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\"; export NODE_ENV=development; jest"
@@ -30,22 +33,22 @@
30
33
  "npm": ">=11.17.0"
31
34
  },
32
35
  "dependencies": {
36
+ "@openreachtech/mentsu-rootpath": "^1.1.3"
33
37
  },
34
38
  "devDependencies": {
35
39
  "@openreachtech/eslint-config": "^4.5.0",
36
40
  "@openreachtech/jest-constructor-spy": "^1.1.2",
37
41
  "@openreachtech/jest-deep-containing": "^1.0.2",
38
42
  "@openreachtech/jest-expect-each": "^1.0.3",
39
- "@openreachtech/mentsu-rootpath": "^1.1.3",
40
- "@openreachtech/renchan-env": "^1.0.6",
43
+ "@openreachtech/renchan-env": "^1.1.0",
41
44
  "@types/jest": "^30.0.0",
42
45
  "eslint": "^9.39.5",
43
46
  "jest": "^30.5.2"
44
47
  },
45
48
  "overrides": {
46
- "brace-expansion@1": "^1.1.21",
47
49
  "brace-expansion@5": "^5.0.12",
48
- "glob": "^13.0.6"
50
+ "glob": "^14.0.2",
51
+ "minimatch": "^11.0.4"
49
52
  },
50
53
  "allowScripts": {
51
54
  "@parcel/watcher": false,
@@ -0,0 +1,31 @@
1
+ import rootPath from '../lib/globals/root-path.js'
2
+
3
+ import IgnoredAdvisoriesLoader from '../lib/IgnoredAdvisoriesLoader.js'
4
+ import NpmAuditGate from '../lib/NpmAuditGate.js'
5
+ import ProcessArgumentsResolver from '../lib/ProcessArgumentsResolver.js'
6
+
7
+ const argumentsResolver = ProcessArgumentsResolver.create({
8
+ rawArguments: process.argv,
9
+ })
10
+
11
+ const ignoredAdvisoriesPath = argumentsResolver.extractIgnoredAdvisoriesFilePathArgument()
12
+
13
+ if (ignoredAdvisoriesPath === null) {
14
+ throw new Error('An argument that specifies the ignored advisories is required')
15
+ }
16
+
17
+ const loader = IgnoredAdvisoriesLoader.create({
18
+ ignoredAdvisoriesFilePath: rootPath.to(ignoredAdvisoriesPath),
19
+ })
20
+
21
+ const ignoredAdvisories = await loader.loadAdvisories()
22
+
23
+ if (ignoredAdvisories === null) {
24
+ throw new Error(`Failed to load the ignored advisories: ${ignoredAdvisoriesPath}`)
25
+ }
26
+
27
+ const gate = NpmAuditGate.create({
28
+ ignoredAdvisories,
29
+ })
30
+
31
+ process.exitCode = gate.auditAdvisories()
package/types/index.d.ts CHANGED
@@ -1,5 +1,49 @@
1
+ import type AuditFetcherActual from '../lib/AuditFetcher.js'
2
+ import type AuditReportResponseActual from '../lib/AuditReportResponse.js'
3
+ import type IgnoredAdvisoriesLoaderActual from '../lib/IgnoredAdvisoriesLoader.js'
4
+ import type NpmAuditGateActual from '../lib/NpmAuditGate.js'
5
+ import type ProcessArgumentsResolverActual from '../lib/ProcessArgumentsResolver.js'
6
+ import type ProcessLoggerActual from '../lib/ProcessLogger.js'
7
+
8
+ import type EXIT_CODE_ACTUAL from '../lib/EXIT_CODE.js'
9
+
1
10
  export { default as AuditFetcher } from '../lib/AuditFetcher.js'
2
11
  export { default as AuditReportResponse } from '../lib/AuditReportResponse.js'
3
- export { default as EXIT_CODE } from '../lib/EXIT_CODE.js'
12
+ export { default as IgnoredAdvisoriesLoader } from '../lib/IgnoredAdvisoriesLoader.js'
4
13
  export { default as NpmAuditGate } from '../lib/NpmAuditGate.js'
14
+ export { default as ProcessArgumentsResolver } from '../lib/ProcessArgumentsResolver.js'
5
15
  export { default as ProcessLogger } from '../lib/ProcessLogger.js'
16
+
17
+ export { default as EXIT_CODE } from '../lib/EXIT_CODE.js'
18
+
19
+ declare global {
20
+ namespace NpmAuditType {
21
+ type AuditFetcher = AuditFetcherActual
22
+ type AuditFetcherCtor = typeof AuditFetcherActual
23
+
24
+ type AuditReportResponse = AuditReportResponseActual
25
+ type AuditReportResponseCtor = typeof AuditReportResponseActual
26
+
27
+ type IgnoredAdvisoriesLoader = IgnoredAdvisoriesLoaderActual
28
+ type IgnoredAdvisoriesLoaderCtor = typeof IgnoredAdvisoriesLoaderActual
29
+
30
+ type NpmAuditGate = NpmAuditGateActual
31
+ type NpmAuditGateCtor = typeof NpmAuditGateActual
32
+
33
+ type ProcessArgumentsResolver = ProcessArgumentsResolverActual
34
+ type ProcessArgumentsResolverCtor = typeof ProcessArgumentsResolverActual
35
+
36
+ type ProcessLogger = ProcessLoggerActual
37
+ type ProcessLoggerCtor = typeof ProcessLoggerActual
38
+
39
+ type EXIT_CODE = typeof EXIT_CODE_ACTUAL
40
+
41
+ type IgnoredAdvisory = {
42
+ id: string
43
+ package: string
44
+ path: string
45
+ reason: string
46
+ removeWhen: string
47
+ }
48
+ }
49
+ }
@@ -1,28 +0,0 @@
1
- import type AuditFetcher from '../lib/AuditFetcher.js'
2
- import type AuditReportResponse from '../lib/AuditReportResponse.js'
3
- import type NpmAuditGate from '../lib/NpmAuditGate.js'
4
- import type ProcessLogger from '../lib/ProcessLogger.js'
5
-
6
- declare global {
7
- namespace NpmAuditType {
8
- type Gate = NpmAuditGate
9
- type GateCtor = typeof NpmAuditGate
10
-
11
- type Fetcher = AuditFetcher
12
- type FetcherCtor = typeof AuditFetcher
13
-
14
- type ReportResponse = AuditReportResponse
15
- type ReportResponseCtor = typeof AuditReportResponse
16
-
17
- type Logger = ProcessLogger
18
- type LoggerCtor = typeof ProcessLogger
19
-
20
- type IgnoredAdvisory = {
21
- id: string
22
- package: string
23
- path: string
24
- reason: string
25
- removeWhen: string
26
- }
27
- }
28
- }