72eba67af1
* Minor adaptation of VerifyCidTransform implementation * refactor: factorize content-encoding negotiation into new lib * bsky: Use undici to stream blob * fixup! bsky: Use undici to stream blob * disable ssrf bsky protection in dev-env * remove http requests to self to host "/img/" * drop axios from tests * fixes * fix tests * reviex changes * properly handle HEAD requests * handle client disconnection * fix tests * drop unrelated change * tidy * tidy * tidy * remove axios from dev-env * remove axios from identity package * use undici 6 * remove axios dependency from ozone * tidy * remove axios from PDS package * avoid killing bsky-pds connections * improve debugging data * Better handle invalid CID * tidy * tidy * refactor "allFulfilled" util in @atproto/common * tidy --------- Co-authored-by: devin ivy <devinivy@gmail.com>
126 lines
3.8 KiB
TypeScript
126 lines
3.8 KiB
TypeScript
import { cidForCbor, verifyCidForBytes } from '@atproto/common'
|
|
import { randomBytes } from '@atproto/crypto'
|
|
import { TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
import { CID } from 'multiformats/cid'
|
|
import { request } from 'undici'
|
|
|
|
describe('blob resolver', () => {
|
|
let network: TestNetwork
|
|
let fileDid: string
|
|
let fileCid: CID
|
|
let fileSize: number
|
|
|
|
beforeAll(async () => {
|
|
network = await TestNetwork.create({
|
|
dbPostgresSchema: 'bsky_blob_resolver',
|
|
})
|
|
const sc = network.getSeedClient()
|
|
await basicSeed(sc)
|
|
await network.processAll()
|
|
fileDid = sc.dids.carol
|
|
fileCid = sc.posts[fileDid][0].images[0].image.ref
|
|
fileSize = sc.posts[fileDid][0].images[0].image.size
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await network.close()
|
|
})
|
|
|
|
it('resolves blob with good signature check.', async () => {
|
|
const response = await request(
|
|
new URL(`/blob/${fileDid}/${fileCid.toString()}`, network.bsky.url),
|
|
)
|
|
expect(response.statusCode).toEqual(200)
|
|
expect(response.headers['content-type']).toEqual('image/jpeg')
|
|
expect(response.headers['content-security-policy']).toEqual(
|
|
`default-src 'none'; sandbox`,
|
|
)
|
|
expect(response.headers['x-content-type-options']).toEqual('nosniff')
|
|
|
|
const bytes = new Uint8Array(await response.body.arrayBuffer())
|
|
await expect(verifyCidForBytes(fileCid, bytes)).resolves.toBeUndefined()
|
|
})
|
|
|
|
it('404s on missing blob.', async () => {
|
|
const badCid = await cidForCbor({ unknown: true })
|
|
const response = await request(
|
|
new URL(`/blob/${fileDid}/${badCid.toString()}`, network.bsky.url),
|
|
)
|
|
expect(response.statusCode).toEqual(404)
|
|
await expect(response.body.json()).resolves.toEqual({
|
|
error: 'NotFoundError',
|
|
message: 'Blob not found',
|
|
})
|
|
})
|
|
|
|
it('404s on missing identity.', async () => {
|
|
const nonExistingDid = `did:plc:${'a'.repeat(24)}`
|
|
|
|
const response = await request(
|
|
new URL(
|
|
`/blob/${nonExistingDid}/${fileCid.toString()}`,
|
|
network.bsky.url,
|
|
),
|
|
)
|
|
expect(response.statusCode).toEqual(404)
|
|
await expect(response.body.json()).resolves.toEqual({
|
|
error: 'NotFoundError',
|
|
message: 'Origin not found',
|
|
})
|
|
})
|
|
|
|
it('400s on invalid did.', async () => {
|
|
const response = await request(
|
|
new URL(`/blob/did::/${fileCid.toString()}`, network.bsky.url),
|
|
)
|
|
expect(response.statusCode).toEqual(400)
|
|
await expect(response.body.json()).resolves.toEqual({
|
|
error: 'BadRequestError',
|
|
message: 'Invalid did',
|
|
})
|
|
})
|
|
|
|
it('400s on invalid cid.', async () => {
|
|
const response = await request(
|
|
new URL(`/blob/${fileDid}/barfy`, network.bsky.url),
|
|
)
|
|
expect(response.statusCode).toEqual(400)
|
|
await expect(response.body.json()).resolves.toEqual({
|
|
error: 'BadRequestError',
|
|
message: 'Invalid cid',
|
|
})
|
|
})
|
|
|
|
it('400s on missing file.', async () => {
|
|
const missingCid = await cidForCbor('missing-file')
|
|
|
|
const response = await request(
|
|
new URL(`/blob/${fileDid}/${missingCid}`, network.bsky.url),
|
|
)
|
|
expect(response.statusCode).toEqual(404)
|
|
await expect(response.body.json()).resolves.toEqual({
|
|
error: 'NotFoundError',
|
|
message: 'Blob not found',
|
|
})
|
|
})
|
|
|
|
it('replaces the file with invalid bytes.', async () => {
|
|
await network.pds.ctx.blobstore(fileDid).delete(fileCid)
|
|
await network.pds.ctx
|
|
.blobstore(fileDid)
|
|
.putPermanent(fileCid, randomBytes(fileSize))
|
|
})
|
|
|
|
it('fails to fetch bytes on blob with bad signature check.', async () => {
|
|
const response = await request(
|
|
new URL(`/blob/${fileDid}/${fileCid.toString()}`, network.bsky.url),
|
|
)
|
|
|
|
expect(response.statusCode).toEqual(404)
|
|
await expect(response.body.json()).resolves.toEqual({
|
|
error: 'NotFoundError',
|
|
message: 'Bad cid check',
|
|
})
|
|
})
|
|
})
|