npq 2.5.1 → 2.5.3
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.
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
jest.mock('pacote')
|
|
2
|
+
jest.mock('node-fetch')
|
|
3
|
+
|
|
4
|
+
const fetch = require('node-fetch')
|
|
5
|
+
|
|
6
|
+
const ProvenanceMarshall = require('../lib/marshalls/provenance.marshall')
|
|
7
|
+
const pacote = require('pacote')
|
|
8
|
+
|
|
9
|
+
describe('Provenance test suites', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks()
|
|
12
|
+
jest.resetAllMocks()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('has the right title', async () => {
|
|
16
|
+
const testMarshall = new ProvenanceMarshall({
|
|
17
|
+
packageRepoUtils: {
|
|
18
|
+
getPackageInfo: (pkgInfo) => {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
resolve(pkgInfo)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
expect(testMarshall.title()).toEqual('Verifying package provenance')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('should successfully validate a package with verified attestations', async () => {
|
|
30
|
+
// Mock the response from fetch
|
|
31
|
+
const mockResponse = {
|
|
32
|
+
json: jest.fn().mockResolvedValue({
|
|
33
|
+
keys: [
|
|
34
|
+
{
|
|
35
|
+
key: 'publicKey1'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: 'publicKey2'
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
fetch.mockImplementationOnce(() => Promise.resolve(mockResponse))
|
|
44
|
+
|
|
45
|
+
pacote.manifest = jest.fn().mockResolvedValue({
|
|
46
|
+
name: 'packageName',
|
|
47
|
+
version: '1.0.0',
|
|
48
|
+
_attestations: {
|
|
49
|
+
url: 'https://registry.npmjs.org/-/npm/v1/attestations/pacote@17.0.4',
|
|
50
|
+
provenance: { predicateType: 'https://slsa.dev/provenance/v1' }
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// Call the validate method with a package object
|
|
55
|
+
const pkg = {
|
|
56
|
+
packageName: 'packageName',
|
|
57
|
+
packageVersion: '1.0.0'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const testMarshall = new ProvenanceMarshall({
|
|
61
|
+
packageRepoUtils: {
|
|
62
|
+
getPackageInfo: (pkgInfo) => {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
resolve({
|
|
65
|
+
name: pkg.packageName,
|
|
66
|
+
version: pkg.packageVersion
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
},
|
|
70
|
+
parsePackageVersion: (pkgVersion) => {
|
|
71
|
+
return {
|
|
72
|
+
version: pkgVersion
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// We assert that the validate method didn't throw an error,
|
|
79
|
+
// because the keys match the signature
|
|
80
|
+
await testMarshall.validate(pkg)
|
|
81
|
+
|
|
82
|
+
// Assert that the fetch method is called with the correct URL
|
|
83
|
+
// eslint-disable-next-line no-undef
|
|
84
|
+
expect(fetch).toHaveBeenCalledWith('https://registry.npmjs.org/-/npm/v1/keys')
|
|
85
|
+
|
|
86
|
+
// Assert that the pacote.manifest method is called with the correct arguments
|
|
87
|
+
expect(pacote.manifest).toHaveBeenCalledWith('packageName@1.0.0', {
|
|
88
|
+
verifyAttestations: true,
|
|
89
|
+
registry: 'https://registry.npmjs.org',
|
|
90
|
+
'//registry.npmjs.org/:_keys': [
|
|
91
|
+
{
|
|
92
|
+
key: 'publicKey1',
|
|
93
|
+
pemkey: '-----BEGIN PUBLIC KEY-----\npublicKey1\n-----END PUBLIC KEY-----'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: 'publicKey2',
|
|
97
|
+
pemkey: '-----BEGIN PUBLIC KEY-----\npublicKey2\n-----END PUBLIC KEY-----'
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('should throw an error if attestation verification fails and manifest() throws an error', async () => {
|
|
104
|
+
// Mock the response from fetch
|
|
105
|
+
const mockResponse = {
|
|
106
|
+
json: jest.fn().mockResolvedValue({
|
|
107
|
+
keys: [
|
|
108
|
+
{
|
|
109
|
+
key: 'publicKey1'
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
key: 'publicKey2'
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
fetch.mockImplementationOnce(() => Promise.resolve(mockResponse))
|
|
118
|
+
|
|
119
|
+
const pkg = {
|
|
120
|
+
packageName: 'packageName',
|
|
121
|
+
packageVersion: '1.0.0'
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// the manifest() method should throw an error
|
|
125
|
+
// in this jest mock to simulate a problem:
|
|
126
|
+
pacote.manifest = jest.fn().mockRejectedValue(new Error('mocked manifest error'))
|
|
127
|
+
|
|
128
|
+
const testMarshall = new ProvenanceMarshall({
|
|
129
|
+
packageRepoUtils: {
|
|
130
|
+
getPackageInfo: (pkgInfo) => {
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
resolve({
|
|
133
|
+
name: pkg.packageName,
|
|
134
|
+
version: pkg.packageVersion
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
},
|
|
138
|
+
parsePackageVersion: (pkgVersion) => {
|
|
139
|
+
return {
|
|
140
|
+
version: pkgVersion
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// We assert that the validate method didn't throw an error,
|
|
147
|
+
// because the keys match the signature
|
|
148
|
+
await expect(testMarshall.validate(pkg)).rejects.toThrow('mocked manifest error')
|
|
149
|
+
|
|
150
|
+
// Assert that the fetch method is called with the correct URL
|
|
151
|
+
// eslint-disable-next-line no-undef
|
|
152
|
+
expect(fetch).toHaveBeenCalledWith('https://registry.npmjs.org/-/npm/v1/keys')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('should throw a warning if attestations cant be found for the package', async () => {
|
|
156
|
+
// Mock the response from fetch
|
|
157
|
+
const mockResponse = {
|
|
158
|
+
json: jest.fn().mockResolvedValue({
|
|
159
|
+
keys: [
|
|
160
|
+
{
|
|
161
|
+
key: 'publicKey1'
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
key: 'publicKey2'
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
fetch.mockImplementationOnce(() => Promise.resolve(mockResponse))
|
|
170
|
+
|
|
171
|
+
const pkg = {
|
|
172
|
+
packageName: 'packageName',
|
|
173
|
+
packageVersion: '1.0.0'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
pacote.manifest = jest.fn().mockResolvedValue({
|
|
177
|
+
name: 'packageName',
|
|
178
|
+
version: '1.0.0'
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const testMarshall = new ProvenanceMarshall({
|
|
182
|
+
packageRepoUtils: {
|
|
183
|
+
getPackageInfo: (pkgInfo) => {
|
|
184
|
+
return new Promise((resolve) => {
|
|
185
|
+
resolve({
|
|
186
|
+
name: pkg.packageName,
|
|
187
|
+
version: pkg.packageVersion
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
},
|
|
191
|
+
parsePackageVersion: (pkgVersion) => {
|
|
192
|
+
return {
|
|
193
|
+
version: pkgVersion
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
// We assert that the validate method didn't throw an error,
|
|
200
|
+
// because the keys match the signature
|
|
201
|
+
await expect(testMarshall.validate(pkg)).rejects.toThrow(
|
|
202
|
+
'the package was published without any attestations. Proceed with care.'
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
// Assert that the fetch method is called with the correct URL
|
|
206
|
+
// eslint-disable-next-line no-undef
|
|
207
|
+
expect(fetch).toHaveBeenCalledWith('https://registry.npmjs.org/-/npm/v1/keys')
|
|
208
|
+
})
|
|
209
|
+
})
|
|
@@ -9,6 +9,10 @@ jest.mock('pacote', () => {
|
|
|
9
9
|
}
|
|
10
10
|
})
|
|
11
11
|
|
|
12
|
+
jest.mock('node-fetch')
|
|
13
|
+
|
|
14
|
+
const fetch = require('node-fetch')
|
|
15
|
+
|
|
12
16
|
const SignaturesMarshall = require('../lib/marshalls/signatures.marshall')
|
|
13
17
|
const pacote = require('pacote')
|
|
14
18
|
|
|
@@ -46,7 +50,7 @@ describe('Signature test suites', () => {
|
|
|
46
50
|
]
|
|
47
51
|
})
|
|
48
52
|
}
|
|
49
|
-
|
|
53
|
+
fetch.mockImplementationOnce(() => Promise.resolve(mockResponse))
|
|
50
54
|
|
|
51
55
|
const testMarshall = new SignaturesMarshall({
|
|
52
56
|
packageRepoUtils: {
|
|
@@ -103,7 +107,7 @@ describe('Signature test suites', () => {
|
|
|
103
107
|
]
|
|
104
108
|
})
|
|
105
109
|
}
|
|
106
|
-
|
|
110
|
+
fetch.mockImplementationOnce(() => Promise.resolve(mockResponse))
|
|
107
111
|
|
|
108
112
|
// the manifest() method should throw an error
|
|
109
113
|
// in this jest mock to simulate a problem:
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const BaseMarshall = require('./baseMarshall')
|
|
4
|
+
const fetch = require('node-fetch')
|
|
4
5
|
const pacote = require('pacote')
|
|
6
|
+
const Warning = require('../helpers/warning')
|
|
5
7
|
|
|
6
8
|
const MARSHALL_NAME = 'provenance'
|
|
7
9
|
|
|
@@ -44,7 +46,15 @@ class Marshall extends BaseMarshall {
|
|
|
44
46
|
})
|
|
45
47
|
})
|
|
46
48
|
.then((metadata) => {
|
|
47
|
-
|
|
49
|
+
if (!metadata || !metadata._attestations) {
|
|
50
|
+
throw new Warning(
|
|
51
|
+
'the package was published without any attestations. Proceed with care.'
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const attestations = metadata._attestations
|
|
56
|
+
|
|
57
|
+
return attestations
|
|
48
58
|
})
|
|
49
59
|
}
|
|
50
60
|
|