npq 3.10.1 → 3.11.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.
Files changed (46) hide show
  1. package/package.json +27 -20
  2. package/.editorconfig +0 -49
  3. package/.github/ISSUE_TEMPLATE.md +0 -32
  4. package/.github/Logo Horizontal.png +0 -0
  5. package/.github/Logo Vertical.png +0 -0
  6. package/.github/Logo.png +0 -0
  7. package/.github/PULL_REQUEST_TEMPLATE.md +0 -35
  8. package/.github/npq-demo-1.gif +0 -0
  9. package/.github/npq-demo-1.png +0 -0
  10. package/.github/npq-demo.gif +0 -0
  11. package/.github/npq.mov +0 -0
  12. package/.github/npq.mp4 +0 -0
  13. package/.github/npq.png +0 -0
  14. package/.github/workflows/automerge.yml +0 -39
  15. package/.github/workflows/main.yml +0 -58
  16. package/.husky/commit-msg +0 -4
  17. package/.husky/post-merge +0 -4
  18. package/.husky/pre-commit +0 -4
  19. package/.husky/pre-push +0 -4
  20. package/.prettierrc.js +0 -9
  21. package/CHANGELOG.md +0 -27
  22. package/CODE_OF_CONDUCT.md +0 -73
  23. package/CONTRIBUTING.md +0 -55
  24. package/SECURITY.md +0 -23
  25. package/__tests__/__fixtures__/test.marshall.js +0 -58
  26. package/__tests__/cli.parser.test.js +0 -121
  27. package/__tests__/cli.test.js +0 -110
  28. package/__tests__/cliPrompt.test.js +0 -409
  29. package/__tests__/env-var-integration.test.js +0 -109
  30. package/__tests__/marshalls.base.test.js +0 -113
  31. package/__tests__/marshalls.classMethods.test.js +0 -34
  32. package/__tests__/marshalls.expiredDomains.test.js +0 -100
  33. package/__tests__/marshalls.newBin.test.js +0 -441
  34. package/__tests__/marshalls.provenance.test.js +0 -383
  35. package/__tests__/marshalls.repo.test.js +0 -126
  36. package/__tests__/marshalls.signatures.test.js +0 -132
  37. package/__tests__/marshalls.snyk.test.js +0 -294
  38. package/__tests__/marshalls.tasks.test.js +0 -130
  39. package/__tests__/marshalls.typosquatting.test.js +0 -76
  40. package/__tests__/marshalls.version-maturity.test.js +0 -194
  41. package/__tests__/mocks/registryPackageOk.mock.json +0 -146
  42. package/__tests__/mocks/registryPackageUnpublished.mock.json +0 -31
  43. package/__tests__/packageManager.test.js +0 -93
  44. package/__tests__/packageRepoUtils.test.js +0 -251
  45. package/__tests__/reportResults.test.js +0 -772
  46. package/__tests__/scripts.test.js +0 -146
@@ -1,383 +0,0 @@
1
- jest.mock('pacote')
2
-
3
- const ProvenanceMarshall = require('../lib/marshalls/provenance.marshall')
4
- const pacote = require('pacote')
5
-
6
- describe('Provenance test suites', () => {
7
- beforeEach(() => {
8
- jest.clearAllMocks()
9
- jest.resetAllMocks()
10
- })
11
-
12
- test('has the right title', async () => {
13
- const testMarshall = new ProvenanceMarshall({
14
- packageRepoUtils: {
15
- getPackageInfo: (pkgInfo) => {
16
- return new Promise((resolve) => {
17
- resolve(pkgInfo)
18
- })
19
- }
20
- }
21
- })
22
-
23
- expect(testMarshall.title()).toEqual('Verifying package provenance')
24
- })
25
-
26
- test('should successfully validate a package with verified attestations', async () => {
27
- // Mock the response from fetch
28
- const mockResponse = {
29
- json: jest.fn().mockResolvedValue({
30
- keys: [
31
- {
32
- key: 'publicKey1'
33
- },
34
- {
35
- key: 'publicKey2'
36
- }
37
- ]
38
- })
39
- }
40
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.resolve(mockResponse))
41
-
42
- pacote.manifest = jest.fn().mockResolvedValue({
43
- name: 'packageName',
44
- version: '1.0.0',
45
- _attestations: {
46
- url: 'https://registry.npmjs.org/-/npm/v1/attestations/pacote@17.0.4',
47
- provenance: { predicateType: 'https://slsa.dev/provenance/v1' }
48
- }
49
- })
50
-
51
- // Call the validate method with a package object
52
- const pkg = {
53
- packageName: 'packageName',
54
- packageVersion: '1.0.0'
55
- }
56
-
57
- const testMarshall = new ProvenanceMarshall({
58
- packageRepoUtils: {
59
- getPackageInfo: (pkgInfo) => {
60
- return new Promise((resolve) => {
61
- resolve({
62
- name: pkg.packageName,
63
- version: pkg.packageVersion
64
- })
65
- })
66
- },
67
- parsePackageVersion: (pkgVersion) => {
68
- return {
69
- version: pkgVersion
70
- }
71
- }
72
- }
73
- })
74
-
75
- // We assert that the validate method didn't throw an error,
76
- // because the keys match the signature
77
- await testMarshall.validate(pkg)
78
-
79
- // Assert that the fetch method is called with the correct URL
80
- // eslint-disable-next-line no-undef
81
- expect(fetch).toHaveBeenCalledWith('https://registry.npmjs.org/-/npm/v1/keys')
82
-
83
- // Assert that the pacote.manifest method is called with the correct arguments
84
- expect(pacote.manifest).toHaveBeenCalledWith('packageName@1.0.0', {
85
- verifyAttestations: true,
86
- registry: 'https://registry.npmjs.org',
87
- '//registry.npmjs.org/:_keys': [
88
- {
89
- key: 'publicKey1',
90
- pemkey: '-----BEGIN PUBLIC KEY-----\npublicKey1\n-----END PUBLIC KEY-----'
91
- },
92
- {
93
- key: 'publicKey2',
94
- pemkey: '-----BEGIN PUBLIC KEY-----\npublicKey2\n-----END PUBLIC KEY-----'
95
- }
96
- ]
97
- })
98
- })
99
-
100
- test('should throw an error if attestation verification fails and manifest() throws an error', async () => {
101
- // Mock the response from fetch
102
- const mockResponse = {
103
- json: jest.fn().mockResolvedValue({
104
- keys: [
105
- {
106
- key: 'publicKey1'
107
- },
108
- {
109
- key: 'publicKey2'
110
- }
111
- ]
112
- })
113
- }
114
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.resolve(mockResponse))
115
-
116
- const pkg = {
117
- packageName: 'packageName',
118
- packageVersion: '1.0.0'
119
- }
120
-
121
- // the manifest() method should throw an error
122
- // in this jest mock to simulate a problem:
123
- pacote.manifest = jest.fn().mockRejectedValue(new Error('mocked manifest error'))
124
-
125
- const testMarshall = new ProvenanceMarshall({
126
- packageRepoUtils: {
127
- getPackageInfo: (pkgInfo) => {
128
- return new Promise((resolve) => {
129
- resolve({
130
- name: pkg.packageName,
131
- version: pkg.packageVersion
132
- })
133
- })
134
- },
135
- parsePackageVersion: (pkgVersion) => {
136
- return {
137
- version: pkgVersion
138
- }
139
- }
140
- }
141
- })
142
-
143
- // We assert that the validate method throws an error,
144
- // but we can't assert the exact error message because we don't log
145
- // it unless debugging is enabled
146
- await expect(testMarshall.validate(pkg)).rejects.toThrow()
147
-
148
- // Assert that the fetch method is called with the correct URL
149
- // eslint-disable-next-line no-undef
150
- expect(fetch).toHaveBeenCalledWith('https://registry.npmjs.org/-/npm/v1/keys')
151
- })
152
-
153
- test('should throw a warning if attestations cant be found for the package', async () => {
154
- // Mock the response from fetch
155
- const mockResponse = {
156
- json: jest.fn().mockResolvedValue({
157
- keys: [
158
- {
159
- key: 'publicKey1'
160
- },
161
- {
162
- key: 'publicKey2'
163
- }
164
- ]
165
- })
166
- }
167
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.resolve(mockResponse))
168
-
169
- const pkg = {
170
- packageName: 'packageName',
171
- packageVersion: '1.0.0'
172
- }
173
-
174
- pacote.manifest = jest.fn().mockResolvedValue({
175
- name: 'packageName',
176
- version: '1.0.0'
177
- })
178
-
179
- const testMarshall = new ProvenanceMarshall({
180
- packageRepoUtils: {
181
- getPackageInfo: (pkgInfo) => {
182
- return new Promise((resolve) => {
183
- resolve({
184
- name: pkg.packageName,
185
- version: pkg.packageVersion
186
- })
187
- })
188
- },
189
- parsePackageVersion: (pkgVersion) => {
190
- return {
191
- version: pkgVersion
192
- }
193
- }
194
- }
195
- })
196
-
197
- // We assert that the validate method didn't throw an error,
198
- // because the keys match the signature
199
- await expect(testMarshall.validate(pkg)).rejects.toThrow(
200
- 'Unable to verify provenance: the package was published without any attestations'
201
- )
202
-
203
- // Assert that the fetch method is called with the correct URL
204
- // eslint-disable-next-line no-undef
205
- expect(fetch).toHaveBeenCalledWith('https://registry.npmjs.org/-/npm/v1/keys')
206
- })
207
-
208
- test.skip('should throw error when provenance regression is detected', async () => {
209
- // Mock fetch to return registry keys
210
- const mockResponse = {
211
- json: jest.fn().mockResolvedValue({
212
- keys: [{ key: 'publicKey1' }]
213
- })
214
- }
215
- global.fetch = jest.fn().mockResolvedValue(mockResponse)
216
-
217
- const pkg = {
218
- packageName: 'test-package',
219
- packageVersion: '2.0.0'
220
- }
221
-
222
- // Mock pacote.manifest to simulate:
223
- // - version 1.0.0 has attestations (older version with provenance)
224
- // - version 2.0.0 has no attestations (newer version without provenance)
225
- pacote.manifest = jest
226
- .fn()
227
- .mockImplementationOnce((manifest) => {
228
- if (manifest.includes('1.0.0')) {
229
- return Promise.resolve({
230
- name: 'test-package',
231
- version: '1.0.0',
232
- _attestations: { provenance: 'test' }
233
- })
234
- }
235
- return Promise.reject(new Error('No attestations'))
236
- })
237
- .mockImplementationOnce((manifest) => {
238
- if (manifest.includes('2.0.0')) {
239
- return Promise.resolve({
240
- name: 'test-package',
241
- version: '2.0.0'
242
- // No _attestations property
243
- })
244
- }
245
- return Promise.reject(new Error('No attestations'))
246
- })
247
-
248
- const testMarshall = new ProvenanceMarshall({
249
- packageRepoUtils: {
250
- getPackageInfo: () => {
251
- return Promise.resolve({
252
- name: 'test-package',
253
- versions: {
254
- '1.0.0': { name: 'test-package', version: '1.0.0' },
255
- '2.0.0': { name: 'test-package', version: '2.0.0' }
256
- }
257
- })
258
- },
259
- parsePackageVersion: (version) => ({ version })
260
- }
261
- })
262
-
263
- await expect(testMarshall.validate(pkg)).rejects.toThrow(
264
- 'Provenance regression detected: Previous version 1.0.0 had provenance attestations, but version 2.0.0 does not. This represents a security downgrade.'
265
- )
266
- })
267
-
268
- test('should pass when no older versions exist', async () => {
269
- const mockResponse = {
270
- json: jest.fn().mockResolvedValue({
271
- keys: [{ key: 'publicKey1' }]
272
- })
273
- }
274
- global.fetch = jest.fn().mockResolvedValue(mockResponse)
275
-
276
- const pkg = {
277
- packageName: 'new-package',
278
- packageVersion: '1.0.0'
279
- }
280
-
281
- pacote.manifest = jest.fn().mockResolvedValue({
282
- name: 'new-package',
283
- version: '1.0.0',
284
- _attestations: { provenance: 'test' }
285
- })
286
-
287
- const testMarshall = new ProvenanceMarshall({
288
- packageRepoUtils: {
289
- getPackageInfo: () => {
290
- return Promise.resolve({
291
- name: 'new-package',
292
- versions: {
293
- '1.0.0': { name: 'new-package', version: '1.0.0' }
294
- }
295
- })
296
- },
297
- parsePackageVersion: (version) => ({ version })
298
- }
299
- })
300
-
301
- const result = await testMarshall.validate(pkg)
302
- expect(result).toBeDefined()
303
- })
304
-
305
- test('should pass when no older versions had provenance', async () => {
306
- const mockResponse = {
307
- json: jest.fn().mockResolvedValue({
308
- keys: [{ key: 'publicKey1' }]
309
- })
310
- }
311
- global.fetch = jest.fn().mockResolvedValue(mockResponse)
312
-
313
- const pkg = {
314
- packageName: 'test-package',
315
- packageVersion: '2.0.0'
316
- }
317
-
318
- // Both versions lack attestations
319
- pacote.manifest = jest.fn().mockResolvedValue({
320
- name: 'test-package',
321
- version: '2.0.0'
322
- // No _attestations
323
- })
324
-
325
- const testMarshall = new ProvenanceMarshall({
326
- packageRepoUtils: {
327
- getPackageInfo: () => {
328
- return Promise.resolve({
329
- name: 'test-package',
330
- versions: {
331
- '1.0.0': { name: 'test-package', version: '1.0.0' },
332
- '2.0.0': { name: 'test-package', version: '2.0.0' }
333
- }
334
- })
335
- },
336
- parsePackageVersion: (version) => ({ version })
337
- }
338
- })
339
-
340
- await expect(testMarshall.validate(pkg)).rejects.toThrow(
341
- 'the package was published without any attestations'
342
- )
343
- })
344
-
345
- test('should pass when current version maintains provenance', async () => {
346
- const mockResponse = {
347
- json: jest.fn().mockResolvedValue({
348
- keys: [{ key: 'publicKey1' }]
349
- })
350
- }
351
- global.fetch = jest.fn().mockResolvedValue(mockResponse)
352
-
353
- const pkg = {
354
- packageName: 'test-package',
355
- packageVersion: '2.0.0'
356
- }
357
-
358
- // Both versions have attestations
359
- pacote.manifest = jest.fn().mockResolvedValue({
360
- name: 'test-package',
361
- version: '2.0.0',
362
- _attestations: { provenance: 'test' }
363
- })
364
-
365
- const testMarshall = new ProvenanceMarshall({
366
- packageRepoUtils: {
367
- getPackageInfo: () => {
368
- return Promise.resolve({
369
- name: 'test-package',
370
- versions: {
371
- '1.0.0': { name: 'test-package', version: '1.0.0' },
372
- '2.0.0': { name: 'test-package', version: '2.0.0' }
373
- }
374
- })
375
- },
376
- parsePackageVersion: (version) => ({ version })
377
- }
378
- })
379
-
380
- const result = await testMarshall.validate(pkg)
381
- expect(result).toBeDefined()
382
- })
383
- })
@@ -1,126 +0,0 @@
1
- const RepoMarshall = require('../lib/marshalls/repo.marshall')
2
-
3
- const testMarshall = new RepoMarshall({
4
- packageRepoUtils: {
5
- getPackageInfo: (pkgInfo) => {
6
- return new Promise((resolve) => {
7
- resolve(pkgInfo)
8
- })
9
- }
10
- }
11
- })
12
-
13
- const fullPkgData = {
14
- packageName: {
15
- 'dist-tags': {
16
- latest: '1.0.0'
17
- },
18
- versions: {
19
- '1.0.0': {
20
- repository: {
21
- url: 'url'
22
- }
23
- }
24
- }
25
- }
26
- }
27
-
28
- describe('Repo test suites', () => {
29
- beforeEach(() => {
30
- jest.clearAllMocks()
31
- jest.resetAllMocks()
32
- })
33
-
34
- test('has the right title', async () => {
35
- expect(testMarshall.title()).toEqual('Identifying package repository...')
36
- })
37
-
38
- test('throws the right error when there is no pkg data available', async () => {
39
- await expect(testMarshall.validate({ packageName: {} })).rejects.toThrow(
40
- 'The package has no associated repository or homepage.'
41
- )
42
- })
43
-
44
- test('throws the right error when there is no repo in the pkg data', async () => {
45
- const pkgData = {
46
- packageName: {
47
- 'dist-tags': {
48
- latest: '1.0.0'
49
- },
50
- versions: {
51
- '1.0.0': {}
52
- }
53
- }
54
- }
55
-
56
- await expect(testMarshall.validate(pkgData)).rejects.toThrow(
57
- 'The package has no associated repository or homepage.'
58
- )
59
- })
60
-
61
- test('throws the right error when there is no repo URL in the pkg data', async () => {
62
- const pkgData = {
63
- packageName: {
64
- 'dist-tags': {
65
- latest: '1.0.0'
66
- },
67
- versions: {
68
- '1.0.0': {
69
- repository: {}
70
- }
71
- }
72
- }
73
- }
74
-
75
- await expect(testMarshall.validate(pkgData)).rejects.toThrow(
76
- 'The package has no associated repository or homepage.'
77
- )
78
- })
79
-
80
- test('throws the right error when the repository url does not exist', async () => {
81
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.reject(new Error('error')))
82
-
83
- await expect(testMarshall.validate(fullPkgData)).rejects.toThrow(
84
- 'No valid repository is associated with the package'
85
- )
86
- })
87
-
88
- test('throws the right error when the repository url is unreachable', async () => {
89
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.reject(new Error('error')))
90
-
91
- fullPkgData.packageName.versions['1.0.0'].repository.url =
92
- 'https://dsfsdfsdfs.abcdeugwecwekjasda.com/'
93
- await expect(testMarshall.validate(fullPkgData)).rejects.toThrow(
94
- 'The repository associated with the package (https://dsfsdfsdfs.abcdeugwecwekjasda.com/) does not exist or is unreachable at the moment.'
95
- )
96
- })
97
-
98
- test('throws the right error when the homepage url does not exist', async () => {
99
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.reject(new Error('error')))
100
-
101
- const pkgData = {
102
- packageName: {
103
- 'dist-tags': {
104
- latest: '1.0.0'
105
- },
106
- versions: {
107
- '1.0.0': {
108
- repository: {},
109
- homepage: 'homepage-url'
110
- }
111
- }
112
- }
113
- }
114
-
115
- await expect(testMarshall.validate(pkgData)).rejects.toThrow(
116
- 'The homepage associated with the package (homepage-url) does not exist or is unreachable at the moment.'
117
- )
118
- })
119
-
120
- test('does not throw any errors if the url exists', async () => {
121
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.resolve('success'))
122
-
123
- fullPkgData.packageName.versions['1.0.0'].repository.url = 'https://google.com'
124
- await expect(testMarshall.validate(fullPkgData)).resolves.toEqual(expect.anything())
125
- })
126
- })
@@ -1,132 +0,0 @@
1
- jest.mock('pacote', () => {
2
- return {
3
- // manifest method should be a promise that resolves to a value:
4
- manifest: jest.fn().mockResolvedValue({
5
- name: 'packageName',
6
- version: '1.0.0'
7
- // Add other relevant properties
8
- })
9
- }
10
- })
11
-
12
- const SignaturesMarshall = require('../lib/marshalls/signatures.marshall')
13
- const pacote = require('pacote')
14
-
15
- describe('Signature test suites', () => {
16
- beforeEach(() => {
17
- jest.clearAllMocks()
18
- jest.resetAllMocks()
19
- })
20
-
21
- test('has the right title', async () => {
22
- const testMarshall = new SignaturesMarshall({
23
- packageRepoUtils: {
24
- getPackageInfo: (pkgInfo) => {
25
- return new Promise((resolve) => {
26
- resolve(pkgInfo)
27
- })
28
- }
29
- }
30
- })
31
-
32
- expect(testMarshall.title()).toEqual('Verifying registry signatures for package')
33
- })
34
-
35
- test('should successfully validate a package with correct signature', async () => {
36
- // Mock the response from fetch
37
- const mockResponse = {
38
- json: jest.fn().mockResolvedValue({
39
- keys: [
40
- {
41
- key: 'publicKey1'
42
- },
43
- {
44
- key: 'publicKey2'
45
- }
46
- ]
47
- })
48
- }
49
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.resolve(mockResponse))
50
-
51
- const testMarshall = new SignaturesMarshall({
52
- packageRepoUtils: {
53
- getPackageInfo: (pkgInfo) => {
54
- return new Promise((resolve) => {
55
- resolve(pkgInfo)
56
- })
57
- }
58
- }
59
- })
60
-
61
- // Call the validate method with a package object
62
- const pkg = {
63
- packageName: 'packageName',
64
- packageVersion: '1.0.0'
65
- }
66
-
67
- // We assert that the validate method didn't throw an error,
68
- // because the keys match the signature
69
- await testMarshall.validate(pkg)
70
-
71
- // Assert that the fetch method is called with the correct URL
72
- // eslint-disable-next-line no-undef
73
- expect(fetch).toHaveBeenCalledWith('https://registry.npmjs.org/-/npm/v1/keys')
74
-
75
- // Assert that the pacote.manifest method is called with the correct arguments
76
- expect(pacote.manifest).toHaveBeenCalledWith('packageName@1.0.0', {
77
- verifySignatures: true,
78
- registry: 'https://registry.npmjs.org',
79
- '//registry.npmjs.org/:_keys': [
80
- {
81
- key: 'publicKey1',
82
- pemkey: '-----BEGIN PUBLIC KEY-----\npublicKey1\n-----END PUBLIC KEY-----'
83
- },
84
- {
85
- key: 'publicKey2',
86
- pemkey: '-----BEGIN PUBLIC KEY-----\npublicKey2\n-----END PUBLIC KEY-----'
87
- }
88
- ]
89
- })
90
- })
91
-
92
- test('should throw an error if keys dont match and manifest() throws an error', async () => {
93
- // Mock the response from fetch
94
- const mockResponse = {
95
- json: jest.fn().mockResolvedValue({
96
- keys: [
97
- {
98
- key: 'publicKey1'
99
- },
100
- {
101
- key: 'publicKey2'
102
- }
103
- ]
104
- })
105
- }
106
- global.fetch = jest.fn().mockImplementationOnce(() => Promise.resolve(mockResponse))
107
-
108
- // the manifest() method should throw an error
109
- // in this jest mock to simulate a problem:
110
- pacote.manifest = jest.fn().mockRejectedValue(new Error('mocked manifest error'))
111
-
112
- const testMarshall = new SignaturesMarshall({
113
- packageRepoUtils: {
114
- getPackageInfo: (pkgInfo) => {
115
- return new Promise((resolve) => {
116
- resolve(pkgInfo)
117
- })
118
- }
119
- }
120
- })
121
-
122
- // Call the validate method with a package object
123
- const pkg = {
124
- packageName: 'packageName',
125
- packageVersion: '1.0.0'
126
- }
127
-
128
- // We assert that the validate method didn't throw an error,
129
- // because the keys match the signature
130
- await expect(testMarshall.validate(pkg)).rejects.toThrow('mocked manifest error')
131
- })
132
- })