-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(releasing): validate tarball sha1 before publishing to homebrew MONGOSH-2059 #2407
Merged
+179
−34
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,96 @@ | ||
import { expect } from 'chai'; | ||
import { httpsSha256 } from './utils'; | ||
import { npmPackageSha256 } from './utils'; | ||
import sinon from 'sinon'; | ||
import crypto from 'crypto'; | ||
|
||
describe('Homebrew utils', function () { | ||
describe('httpsSha256', function () { | ||
describe('npmPackageSha256', function () { | ||
it('computes the correct sha', async function () { | ||
const url = | ||
'https://registry.npmjs.org/@mongosh/cli-repl/-/cli-repl-0.6.1.tgz'; | ||
const url = 'https://registry.npmjs.org/@mongosh/cli-repl/0.6.1'; | ||
const expectedSha = | ||
'3721ea662cd3775373d4d70f7593993564563d9379704896478db1d63f6c8470'; | ||
|
||
expect(await httpsSha256(url)).to.equal(expectedSha); | ||
expect(await npmPackageSha256(url)).to.equal(expectedSha); | ||
}); | ||
|
||
describe('when response sha mismatches', function () { | ||
const fakeTarball = Buffer.from('mongosh-2.4.2.tgz'); | ||
const fakeTarballShasum = crypto | ||
.createHash('sha1') | ||
.update(fakeTarball) | ||
.digest('hex'); | ||
|
||
it('retries', async function () { | ||
const httpGet = sinon.stub(); | ||
httpGet | ||
.withArgs( | ||
'https://registry.npmjs.org/@mongosh/cli-repl/2.4.2', | ||
'json' | ||
) | ||
.resolves({ | ||
dist: { | ||
tarball: | ||
'https://registry.npmjs.org/@mongosh/cli-repl/-/cli-repl-2.4.2.tgz', | ||
shasum: fakeTarballShasum, | ||
}, | ||
}); | ||
|
||
httpGet | ||
.withArgs( | ||
'https://registry.npmjs.org/@mongosh/cli-repl/-/cli-repl-2.4.2.tgz', | ||
'binary' | ||
) | ||
.onFirstCall() | ||
.resolves(Buffer.from('mongosh-2.4.2-incomplete.tgz')) // Simulate incomplete/wrong binary download | ||
.onSecondCall() | ||
.resolves(fakeTarball); | ||
|
||
const sha = await npmPackageSha256( | ||
'https://registry.npmjs.org/@mongosh/cli-repl/2.4.2', | ||
httpGet | ||
); | ||
|
||
expect(sha).to.equal( | ||
crypto.createHash('sha256').update(fakeTarball).digest('hex') | ||
); | ||
}); | ||
|
||
it('throws if retries are exhausted', async function () { | ||
const httpGet = sinon.stub(); | ||
httpGet | ||
.withArgs( | ||
'https://registry.npmjs.org/@mongosh/cli-repl/2.4.2', | ||
'json' | ||
) | ||
.resolves({ | ||
dist: { | ||
tarball: | ||
'https://registry.npmjs.org/@mongosh/cli-repl/-/cli-repl-2.4.2.tgz', | ||
shasum: fakeTarballShasum, | ||
}, | ||
}); | ||
|
||
httpGet | ||
.withArgs( | ||
'https://registry.npmjs.org/@mongosh/cli-repl/-/cli-repl-2.4.2.tgz', | ||
'binary' | ||
) | ||
.resolves(Buffer.from('mongosh-2.4.2-incomplete.tgz')); // Simulate incomplete/wrong binary download | ||
|
||
const incompleteTarballShasum = crypto | ||
.createHash('sha1') | ||
.update(Buffer.from('mongosh-2.4.2-incomplete.tgz')) | ||
.digest('hex'); | ||
|
||
const err = await npmPackageSha256( | ||
'https://registry.npmjs.org/@mongosh/cli-repl/2.4.2', | ||
httpGet | ||
).catch((e) => e); | ||
|
||
expect(err.message).to.equal( | ||
`shasum mismatch: expected '${fakeTarballShasum}', got '${incompleteTarballShasum}'` | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,72 @@ | ||
import crypto from 'crypto'; | ||
import https from 'https'; | ||
|
||
export function httpsSha256(url: string): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
export async function npmPackageSha256( | ||
packageUrl: string, | ||
httpGetFn: typeof httpGet = httpGet | ||
): Promise<string> { | ||
const json = await httpGetFn(packageUrl, 'json'); | ||
nirinchev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const tarballUrl = json.dist.tarball; | ||
const shasum = json.dist.shasum; | ||
|
||
const tarball = await getTarballWithRetries(tarballUrl, shasum, httpGetFn); | ||
const hash = crypto.createHash('sha256'); | ||
hash.update(tarball); | ||
return hash.digest('hex'); | ||
} | ||
|
||
async function getTarballWithRetries( | ||
url: string, | ||
shasum: string, | ||
httpGetFn: typeof httpGet, | ||
attempts = 3 | ||
): Promise<Buffer> { | ||
try { | ||
const tarball = await httpGetFn(url, 'binary'); | ||
const hash = crypto.createHash('sha1').update(tarball).digest('hex'); | ||
if (hash !== shasum) { | ||
throw new Error(`shasum mismatch: expected '${shasum}', got '${hash}'`); | ||
} | ||
|
||
return tarball; | ||
} catch (err) { | ||
if (attempts === 0) { | ||
throw err; | ||
} | ||
|
||
return getTarballWithRetries(url, shasum, httpGetFn, attempts - 1); | ||
} | ||
} | ||
|
||
export function httpGet(url: string, response: 'json'): Promise<any>; | ||
export function httpGet(url: string, response: 'binary'): Promise<Buffer>; | ||
|
||
export async function httpGet( | ||
url: string, | ||
responseType: 'json' | 'binary' | ||
): Promise<any | Buffer> { | ||
addaleax marked this conversation as resolved.
Show resolved
Hide resolved
nirinchev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const response = await new Promise<string | Buffer[]>((resolve, reject) => { | ||
https.get(url, (stream) => { | ||
const hash = crypto.createHash('sha256'); | ||
if (responseType === 'json') { | ||
stream.setEncoding('utf8'); | ||
} | ||
|
||
let data: string | Buffer[] = responseType === 'json' ? '' : []; | ||
stream.on('error', (err) => reject(err)); | ||
stream.on('data', (chunk) => hash.update(chunk)); | ||
stream.on('end', () => resolve(hash.digest('hex'))); | ||
stream.on('data', (chunk) => { | ||
if (typeof data === 'string') { | ||
data += chunk; | ||
nirinchev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
data.push(chunk); | ||
} | ||
}); | ||
stream.on('end', () => resolve(data)); | ||
}); | ||
}); | ||
|
||
if (typeof response === 'string') { | ||
return JSON.parse(response); | ||
} | ||
|
||
return Buffer.concat(response); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unrelated to MONGOSH-2059, but I noticed we don't correctly validate the signature of the package on windows, so figured I'll fix it as a drive by. Happy to move to a different PR if folks prefer a cleaner separation of changes.