Skip to content

Commit

Permalink
fix: verify CAR blocks with all supported hashes (#2460)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Shaw authored Oct 11, 2023
1 parent 0e34b13 commit 4526369
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 27 deletions.
1 change: 1 addition & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@nftstorage/ipfs-cluster": "^5.0.1",
"@noble/ed25519": "^1.6.1",
"@supabase/postgrest-js": "^0.34.1",
"@web3-storage/car-block-validator": "^1.2.0",
"cardex": "^1.0.0",
"ipfs-car": "^0.6.1",
"it-last": "^2.0.0",
Expand Down
16 changes: 8 additions & 8 deletions packages/api/src/routes/nfts-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as raw from 'multiformats/codecs/raw'
import * as cbor from '@ipld/dag-cbor'
import * as pb from '@ipld/dag-pb'
import { Block } from 'multiformats/block'
import { sha256 } from 'multiformats/hashes/sha2'
import { validateBlock } from '@web3-storage/car-block-validator'
import { HTTPError, InvalidCarError } from '../errors.js'
import { createCarCid } from '../utils/car.js'
import { JSONResponse } from '../utils/json-response.js'
Expand Down Expand Up @@ -270,13 +270,13 @@ export async function carStat(carBlob, { structure } = {}) {
if (blockSize > MAX_BLOCK_SIZE) {
throw new Error(`block too big: ${blockSize} > ${MAX_BLOCK_SIZE}`)
}
if (block.cid.multihash.code === sha256.code) {
const ourHash = await sha256.digest(block.bytes)
if (!equals(ourHash.digest, block.cid.multihash.digest)) {
throw new InvalidCarError(
`block data does not match CID for ${block.cid.toString()}`
)
}
try {
// @ts-expect-error CID type mismatch
await validateBlock(block)
} catch (/** @type {any} */ err) {
throw new InvalidCarError(
`failed hash verification: ${block.cid.toString()}: ${err.message}`
)
}
if (!rawRootBlock && block.cid.equals(rootCid)) {
rawRootBlock = block
Expand Down
Binary file added packages/api/test/fixtures/corrupt.car
Binary file not shown.
31 changes: 31 additions & 0 deletions packages/api/test/nfts-upload.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ import {
} from './scripts/test-context.js'
import { File } from 'nft.storage/src/platform.js'
import crypto from 'node:crypto'
import fs from 'node:fs'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import { FormData } from 'undici'
import { createCarCid } from '../src/utils/car.js'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

test.before(async (t) => {
const linkdexUrl = 'http://fake.api.net'
await setupMiniflareContext(t, { overrides: { LINKDEX_URL: linkdexUrl } })
Expand Down Expand Up @@ -806,6 +811,32 @@ test.serial('should write dudewhere index', async (t) => {
t.is(r2Objects.objects[0].key, `${root}/${carCid}`)
})

test.serial('should fail upload for corrupt CAR', async (t) => {
const client = await createClientWithUser(t)
const mf = getMiniflareContext(t)
const carBytes = await fs.promises.readFile(
path.join(__dirname, 'fixtures', 'corrupt.car')
)
const res = await mf.dispatchFetch('http://miniflare.test/upload', {
method: 'POST',
headers: {
Authorization: `Bearer ${client.token}`,
'Content-Type': 'application/car',
},
body: carBytes,
})

t.is(res.status, 400)

const { ok, error } = await res.json()
t.is(ok, false)
t.is(error.code, 'ERROR_INVALID_CAR')
t.is(
error.message,
'Invalid CAR file received: failed hash verification: bafk2bzaceda5oo2f5wmcwfmqbqkxzdwzszc2bhqnj4lpygliqna7bmbkmzwh4: CID hash does not match bytes'
)
})

/**
* @param {Uint8Array} data
*/
Expand Down
62 changes: 43 additions & 19 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3006,14 +3006,30 @@
resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121"
integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==

"@multiformats/murmur3@^1.0.3":
"@multiformats/blake2@^1.0.13":
version "1.0.13"
resolved "https://registry.yarnpkg.com/@multiformats/blake2/-/blake2-1.0.13.tgz#ab0c471da28df55eb7c16339e25ba9571259c2d7"
integrity sha512-T1Kzya0wjj85CaVeRSpJ858EnSvW1pw94GSitxYf84VsNdv5XYbJ6QG8y26Ft1bVALzrUCmqkQrR53QHSyu6RA==
dependencies:
blakejs "^1.1.1"
multiformats "^9.5.4"

"@multiformats/murmur3@^1.0.3", "@multiformats/murmur3@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@multiformats/murmur3/-/murmur3-1.1.3.tgz#70349166992e5f981f1ddff0200fa775b2bf6606"
integrity sha512-wAPLUErGR8g6Lt+bAZn6218k9YQPym+sjszsXL6o4zfxbA22P+gxWZuuD9wDbwL55xrKO5idpcuQUX7/E3oHcw==
dependencies:
multiformats "^9.5.4"
murmurhash3js-revisited "^3.0.0"

"@multiformats/sha3@^2.0.15":
version "2.0.17"
resolved "https://registry.yarnpkg.com/@multiformats/sha3/-/sha3-2.0.17.tgz#bcfd2d6d7c44a61ced79a2c4dd45acd9ebbfb8d7"
integrity sha512-7ik6pk178qLO2cpNucgf48UnAOBMkq/2H92DP4SprZOJqM9zqbVaKS7XyYW6UvhRsDJ3wi921fYv1ihTtQHLtA==
dependencies:
js-sha3 "^0.8.0"
multiformats "^9.5.4"

"@next/bundle-analyzer@^12.0.7":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-12.2.5.tgz#6aca94359b1a1f933dfb0a89d5f4d0b1fb86e3e5"
Expand Down Expand Up @@ -5249,6 +5265,17 @@
dependencies:
web-streams-polyfill "^3.1.1"

"@web3-storage/car-block-validator@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@web3-storage/car-block-validator/-/car-block-validator-1.2.0.tgz#533ea6b22059606ed19e906434a50c0a16c07283"
integrity sha512-KKQ/M5WtpH/JlkX+bQYKzdG4azmSF495T7vpewje2xh7MBh1d94/BLblxCcLM/larWvXDxOkbAyTTdlECAAuUw==
dependencies:
"@multiformats/blake2" "^1.0.13"
"@multiformats/murmur3" "^1.1.3"
"@multiformats/sha3" "^2.0.15"
multiformats "9.9.0"
uint8arrays "^3.1.1"

"@web3-storage/multipart-parser@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@web3-storage/multipart-parser/-/multipart-parser-1.0.0.tgz#6b69dc2a32a5b207ba43e556c25cc136a56659c4"
Expand Down Expand Up @@ -6414,7 +6441,7 @@ blake3-wasm@^2.1.5:
resolved "https://registry.yarnpkg.com/blake3-wasm/-/blake3-wasm-2.1.5.tgz#b22dbb84bc9419ed0159caa76af4b1b132e6ba52"
integrity sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==

blakejs@^1.1.0:
blakejs@^1.1.0, blakejs@^1.1.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814"
integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==
Expand Down Expand Up @@ -14817,16 +14844,16 @@ multicodec@^3.0.1, multicodec@^3.2.1:
uint8arrays "^3.0.0"
varint "^6.0.0"

multiformats@9.9.0, multiformats@^9.6.5:
version "9.9.0"
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37"
integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==

multiformats@^9.0.0, multiformats@^9.0.4, multiformats@^9.4.13, multiformats@^9.4.2, multiformats@^9.4.5, multiformats@^9.4.7, multiformats@^9.5.4, multiformats@^9.6.3, multiformats@^9.6.4:
version "9.7.1"
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.7.1.tgz#ab348e5fd6f8e7fb3fd56033211bda48854e2173"
integrity sha512-TaVmGEBt0fhxiNJMGphBfB+oGvUxFs8KgGvgl8d3C+GWtrFcvXdJ2196eg+dYhmSFClmgFfSfJEklo+SZzdNuw==

multiformats@^9.6.5:
version "9.9.0"
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37"
integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==

multihashes@^4.0.1, multihashes@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05"
Expand Down Expand Up @@ -16848,16 +16875,11 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"

prettier@2.5.1, prettier@^2.5.1:
prettier@2.5.1, "prettier@>=2.2.1 <=2.3.0", prettier@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==

"prettier@>=2.2.1 <=2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18"
integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==

pretty-error@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6"
Expand Down Expand Up @@ -20022,12 +20044,7 @@ typedoc@^0.22.14:
minimatch "^5.1.0"
shiki "^0.10.1"

typescript@4.4.4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"
integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==

typescript@4.5.3:
typescript@4.4.4, typescript@4.5.3:
version "4.5.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c"
integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ==
Expand Down Expand Up @@ -20070,6 +20087,13 @@ uint8arrays@^3.0.0:
dependencies:
multiformats "^9.4.2"

uint8arrays@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0"
integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==
dependencies:
multiformats "^9.4.2"

unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
Expand Down

0 comments on commit 4526369

Please sign in to comment.