Skip to content
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

fix: respect given algorithms instead of always using sha512 #156

Merged
merged 2 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/remote.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ const remoteFetch = (request, options) => {
if (_opts.integrity && res.status === 200) {
// we got a 200 response and the user has specified an expected
// integrity value, so wrap the response in an ssri stream to verify it
const integrityStream = ssri.integrityStream({ integrity: _opts.integrity })
const integrityStream = ssri.integrityStream({
algorithms: _opts.algorithms,
integrity: _opts.integrity,
size: _opts.size,
})
const pipeline = new CachingMinipassPipeline({
events: ['integrity', 'size'],
}, res.body, integrityStream)
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
"tap": {
"color": 1,
"files": "test/*.js",
"check-coverage": true
"check-coverage": true,
"timeout": 60
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
Expand Down
69 changes: 69 additions & 0 deletions test/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -1462,3 +1462,72 @@ t.test('generic errors streaming from cache propagate to response body', async (
t.equal(cachedRes.headers.get('x-local-cache-mode'), 'stream', 'was a streaming response')
await t.rejects(cachedRes.buffer(), { message: 'broken stream' }, 'consuming payload rejects')
})

t.test('can write content as sha1', async (t) => {
const SHA1_INTEGRITY = ssri.fromData(CONTENT, { algorithms: ['sha1'] }).toString()
const srv = nock(HOST)
.get('/test')
.reply(200, CONTENT, {
...getHeaders(CONTENT),
'x-foo': 'something',
})

const reqKey = cacheKey(new Request(`${HOST}/test`))
const dir = t.testdir()
const res = await fetch(`${HOST}/test`, {
cachePath: dir,
algorithms: ['sha1'],
integrity: SHA1_INTEGRITY,
})
t.ok(srv.isDone(), 'req is fulfilled')
t.equal(res.status, 200)
t.equal(res.url, `${HOST}/test`, 'has a url property matching the request')
t.equal(res.headers.get('cache-control'), 'max-age=300', 'kept cache-control')
t.equal(res.headers.get('content-type'), 'application/octet-stream', 'kept content-stream')
t.equal(res.headers.get('content-length'), `${CONTENT.length}`, 'kept content-length')
t.equal(res.headers.get('x-local-cache'), encodeURIComponent(dir), 'has cache dir')
t.equal(res.headers.get('x-local-cache-key'), encodeURIComponent(reqKey), 'has cache key')
t.equal(res.headers.get('x-local-cache-mode'), 'stream', 'should stream store')
t.equal(res.headers.get('x-local-cache-status'), 'miss', 'identifies as cache miss')
t.ok(res.headers.has('x-local-cache-time'), 'has cache time')
t.equal(res.headers.get('x-foo'), 'something', 'original response has all headers')
t.notOk(res.headers.has('x-local-cache-hash'), 'hash header is only set when served from cache')

const dirBeforeRead = await readdir(dir)
t.same(dirBeforeRead, [], 'should not write to the cache yet')

const buf = await res.buffer()
t.same(buf, CONTENT, 'got the correct content')
const dirAfterRead = await readdir(dir)
// note: if the content version changes this test will have to be touched
// since it asserts the presence of the sha1 directory specifically
t.ok(dirAfterRead.includes('content-v2'), 'cache has content-v2 after consuming the body')
const contentDir = await readdir(join(dir, 'content-v2'))
t.ok(contentDir.includes('sha1'), 'content was written as sha1')

// compact with a function that always returns false
// results in a list of all entries in the index
const entries = await cacache.index.compact(dir, reqKey, () => false)
t.equal(entries.length, 1, 'should only have one entry')
const entry = entries[0]
t.equal(entry.integrity, SHA1_INTEGRITY, 'integrity matches')
t.equal(entry.metadata.url, `${HOST}/test`, 'url matches')
t.same(entry.metadata.reqHeaders, {}, 'metadata has no request headers as none are relevant')
t.same(entry.metadata.resHeaders, {
'content-type': res.headers.get('content-type'),
'cache-control': res.headers.get('cache-control'),
date: res.headers.get('date'),
}, 'resHeaders has only the relevant headers for caching')

// send a second request and make sure it's a cache hit
// note: there is no nock for this. it's expected to be read from cache
const secondRes = await fetch(`${HOST}/test`, {
cachePath: dir,
algorithms: ['sha1'],
integrity: SHA1_INTEGRITY,
})
t.equal(secondRes.status, 200)
t.equal(secondRes.headers.get('x-local-cache-status'), 'hit', 'identifies as cache hit')
const secondBuf = await secondRes.buffer()
t.same(secondBuf, CONTENT, 'got the right content')
})