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.
- package/package.json +27 -20
- package/.editorconfig +0 -49
- package/.github/ISSUE_TEMPLATE.md +0 -32
- package/.github/Logo Horizontal.png +0 -0
- package/.github/Logo Vertical.png +0 -0
- package/.github/Logo.png +0 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -35
- package/.github/npq-demo-1.gif +0 -0
- package/.github/npq-demo-1.png +0 -0
- package/.github/npq-demo.gif +0 -0
- package/.github/npq.mov +0 -0
- package/.github/npq.mp4 +0 -0
- package/.github/npq.png +0 -0
- package/.github/workflows/automerge.yml +0 -39
- package/.github/workflows/main.yml +0 -58
- package/.husky/commit-msg +0 -4
- package/.husky/post-merge +0 -4
- package/.husky/pre-commit +0 -4
- package/.husky/pre-push +0 -4
- package/.prettierrc.js +0 -9
- package/CHANGELOG.md +0 -27
- package/CODE_OF_CONDUCT.md +0 -73
- package/CONTRIBUTING.md +0 -55
- package/SECURITY.md +0 -23
- package/__tests__/__fixtures__/test.marshall.js +0 -58
- package/__tests__/cli.parser.test.js +0 -121
- package/__tests__/cli.test.js +0 -110
- package/__tests__/cliPrompt.test.js +0 -409
- package/__tests__/env-var-integration.test.js +0 -109
- package/__tests__/marshalls.base.test.js +0 -113
- package/__tests__/marshalls.classMethods.test.js +0 -34
- package/__tests__/marshalls.expiredDomains.test.js +0 -100
- package/__tests__/marshalls.newBin.test.js +0 -441
- package/__tests__/marshalls.provenance.test.js +0 -383
- package/__tests__/marshalls.repo.test.js +0 -126
- package/__tests__/marshalls.signatures.test.js +0 -132
- package/__tests__/marshalls.snyk.test.js +0 -294
- package/__tests__/marshalls.tasks.test.js +0 -130
- package/__tests__/marshalls.typosquatting.test.js +0 -76
- package/__tests__/marshalls.version-maturity.test.js +0 -194
- package/__tests__/mocks/registryPackageOk.mock.json +0 -146
- package/__tests__/mocks/registryPackageUnpublished.mock.json +0 -31
- package/__tests__/packageManager.test.js +0 -93
- package/__tests__/packageRepoUtils.test.js +0 -251
- package/__tests__/reportResults.test.js +0 -772
- 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
|
-
})
|