Lookup for vulnerable packages by Package URL.

GET /api/packages/774701?format=api
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "url": "http://public2.vulnerablecode.io/api/packages/774701?format=api",
    "purl": "pkg:npm/%40lodestar/reqresp@1.4.0-dev.995d2a50c3",
    "type": "npm",
    "namespace": "@lodestar",
    "name": "reqresp",
    "version": "1.4.0-dev.995d2a50c3",
    "qualifiers": {},
    "subpath": "",
    "is_vulnerable": true,
    "next_non_vulnerable_version": "1.25.0",
    "latest_non_vulnerable_version": "1.25.0",
    "affected_by_vulnerabilities": [
        {
            "url": "http://public2.vulnerablecode.io/api/vulnerabilities/360575?format=api",
            "vulnerability_id": "VCID-cwag-sjnj-43az",
            "summary": "Lodestar snappy decompression issue\n### Impact\nUnintended permanent chain split affecting greater than or equal to 25% of the network, requiring hard fork (network partition requiring hard fork)\n\n### Description\nLodestar client may fail to decode snappy framing compressed messages.\n\n### Vulnerability Details\nIn Req/Resp protocol the message are encoded by using ssz_snappy encoding, which is basically snappy framing compression over ssz encoded message.\n\nIt's mentioned here - https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md\n\n```\nThe token of the negotiated protocol ID specifies the type of encoding to be used for the req/resp interaction. Only one value is possible at this time:\n\nssz_snappy: The contents are first SSZ-encoded and then compressed with Snappy frames compression. For objects containing a single field, only the field is SSZ-encoded not a container with a single field. For example, the BeaconBlocksByRoot request is an SSZ-encoded list of Root's. This encoding type MUST be supported by all clients.\n```\n\nIn snappy framing format there a few types of chunks.\nWe are interested in so called reserved skippable chunks. These are chunks with chunk type in range [0x80, 0xfd]\nLet's see how rust snappy handles them https://github.com/BurntSushi/rust-snappy/blob/master/src/read.rs#L137\n\n```\nimpl<R: io::Read> io::Read for FrameDecoder<R> {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n \t\t   ... \n           ...\n  \t\t    let len = len64 as usize;\n            match ty {\n                Err(b) if 0x02 <= b && b <= 0x7F => {\n                    // Spec says that chunk types 0x02-0x7F are reserved and\n                    // conformant decoders must return an error.\n                    fail!(Error::UnsupportedChunkType { byte: b });\n                }\n                Err(b) if 0x80 <= b && b <= 0xFD => {\n                    // Spec says that chunk types 0x80-0xFD are reserved but\n                    // skippable.\n                    self.r.read_exact(&mut self.src[0..len])?;\n                }\n```\n\nSimilar code can be found in golang implementation - https://github.com/golang/snappy/blob/master/decode.go#L221\n\n```\nfunc (r *Reader) fill() error {\n\t...\n\tif chunkType <= 0x7f {\n\t\t\t// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).\n\t\t\tr.err = ErrUnsupported\n\t\t\treturn r.err\n\t\t}\n\t\t// Section 4.4 Padding (chunk type 0xfe).\n\t\t// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).\n\t\tif !r.readFull(r.buf[:chunkLen], false) {\n\t\t\treturn r.err\n\t\t}\n```\n\nNow let's see how lodestar handles such chunks https://github.com/ChainSafe/lodestar/blob/unstable/packages/reqresp/src/encodingStrategies/sszSnappy/snappyFrames/uncompress.ts#L17\n\n```\nuncompress(chunk: Uint8ArrayList): Uint8ArrayList | null {\n    this.buffer.append(chunk);\n    const result = new Uint8ArrayList();\n    while (this.buffer.length > 0) {\n      if (this.buffer.length < 4) break;\n\n      const type = getChunkType(this.buffer.get(0));\n      const frameSize = getFrameSize(this.buffer, 1);\n\n      if (this.buffer.length - 4 < frameSize) {\n        break;\n      }\n\n      const data = this.buffer.subarray(4, 4 + frameSize);\n      this.buffer.consume(4 + frameSize);\n\n      if (!this.state.foundIdentifier && type !== ChunkType.IDENTIFIER) {\n        throw \"malformed input: must begin with an identifier\";\n      }\n\n      if (type === ChunkType.IDENTIFIER) {\n        if (!Buffer.prototype.equals.call(data, IDENTIFIER)) {\n          throw \"malformed input: bad identifier\";\n        }\n        this.state.foundIdentifier = true;\n        continue;\n      }\n\n      if (type === ChunkType.COMPRESSED) {\n        result.append(uncompress(data.subarray(4)));\n      }\n      if (type === ChunkType.UNCOMPRESSED) {\n        result.append(data.subarray(4));\n      }\n    }\n    if (result.length === 0) {\n      return null;\n    }\n    return result;\n  }\n\n function getChunkType(value: number): ChunkType {\n  switch (value) {\n    case ChunkType.IDENTIFIER:\n      return ChunkType.IDENTIFIER;\n    case ChunkType.COMPRESSED:\n      return ChunkType.COMPRESSED;\n    case ChunkType.UNCOMPRESSED:\n      return ChunkType.UNCOMPRESSED;\n    case ChunkType.PADDING:\n      return ChunkType.PADDING;\n    default:\n      throw new Error(\"Unsupported snappy chunk type\");\n  }\n```\n\nAs you can see, lodestar does not recognize such chunks.\n\nIf it sees such chunk, function getChunkType() throws an exception and decoding fails.\n\n### Impact Details\n\nFaulty nodes may trigger chain stall by sending messages which lodestar fails to parse, while other clients will be able to handle.\n\n### Proof of Concept\n\nHow to reproduce:\n\n1. get archive (via provided [gist link](https://gist.github.com/gln7/bdde7f4e0bdf9d47bf810a015796867a)) decode and unpack it:\n```\n$ base64 -d poc.txt > poc.tgz\n$ tar zxf poc.tgz\n```\n\n2. run dec1.go to verify that our snappy file decompressed successfully\n```\n$ go run dec1.go\n\nreading 1.snappy...\nread 124 bytes, err <nil>\n```\n\n3. run dec1.mjs to verify that lodestar fails to decode such file\n```\nchecking chunk type=255\nchecking chunk type=1\ngot uncompressed chunk..\nchecking chunk type=129\nfile:///../poc/dec1.mjs:74\n            throw new Error(\"Unsupported snappy chunk type\");\n```",
            "references": [
                {
                    "reference_url": "https://github.com/ChainSafe/lodestar",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "LOW",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/ChainSafe/lodestar"
                },
                {
                    "reference_url": "https://github.com/ChainSafe/lodestar/commit/18a0d681dbcc51fb2ac9456f31e91f4e31a18300",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "LOW",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/ChainSafe/lodestar/commit/18a0d681dbcc51fb2ac9456f31e91f4e31a18300"
                },
                {
                    "reference_url": "https://github.com/ChainSafe/lodestar/security/advisories/GHSA-53rv-hcvm-rpp9",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "LOW",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/ChainSafe/lodestar/security/advisories/GHSA-53rv-hcvm-rpp9"
                },
                {
                    "reference_url": "https://github.com/advisories/GHSA-53rv-hcvm-rpp9",
                    "reference_id": "GHSA-53rv-hcvm-rpp9",
                    "reference_type": "",
                    "scores": [],
                    "url": "https://github.com/advisories/GHSA-53rv-hcvm-rpp9"
                }
            ],
            "fixed_packages": [
                {
                    "url": "http://public2.vulnerablecode.io/api/packages/376957?format=api",
                    "purl": "pkg:npm/%40lodestar/reqresp@1.25.0",
                    "is_vulnerable": false,
                    "affected_by_vulnerabilities": [],
                    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540lodestar/reqresp@1.25.0"
                }
            ],
            "aliases": [
                "GHSA-53rv-hcvm-rpp9"
            ],
            "risk_score": 1.4,
            "exploitability": "0.5",
            "weighted_severity": "2.7",
            "resource_url": "http://public2.vulnerablecode.io/vulnerabilities/VCID-cwag-sjnj-43az"
        },
        {
            "url": "http://public2.vulnerablecode.io/api/vulnerabilities/360562?format=api",
            "vulnerability_id": "VCID-jgep-67qa-cfae",
            "summary": "Lodestar snappy checksum issue\n### Impact\nUnintended permanent chain split affecting greater than or equal to 25% of the network, requiring hard fork (network partition requiring hard fork)\n\nLodestar does not verify checksum in snappy framing uncompressed chunks.\n\n### Vulnerability Details\nIn Req/Resp protocol the messages are encoded by using ssz_snappy encoding, which is a snappy framing compression over ssz encoded message.\n\nIn snappy framing format there are uncompressed chunks, each such chunk is prefixed with a checksum.\n\nLet's see how golang implementation parses such chunks - https://github.com/golang/snappy/blob/master/decode.go#L176\n\n```\n\tcase chunkTypeUncompressedData:\n\t\t\t// Section 4.3. Uncompressed data (chunk type 0x01).\n\t\t\tif chunkLen < checksumSize {\n\t\t\t\tr.err = ErrCorrupt\n\t\t\t\treturn r.err\n\t\t\t}\n\t\t\tbuf := r.buf[:checksumSize]\n\t\t\tif !r.readFull(buf, false) {\n\t\t\t\treturn r.err\n\t\t\t}\n\t\t\tchecksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24\n\t\t\t// Read directly into r.decoded instead of via r.buf.\n\t\t\tn := chunkLen - checksumSize\n\t\t\tif n > len(r.decoded) {\n\t\t\t\tr.err = ErrCorrupt\n\t\t\t\treturn r.err\n\t\t\t}\n\t\t\tif !r.readFull(r.decoded[:n], false) {\n\t\t\t\treturn r.err\n\t\t\t}\n\t\t\tif crc(r.decoded[:n]) != checksum {\n\t\t\t\tr.err = ErrCorrupt\n\t\t\t\treturn r.err\n\t\t\t}\n\t\t\tr.i, r.j = 0, n\n\t\t\tcontinue\n```\n\nAs you can see, if checksum is incorrect, decoder fails and returns error.\n\nNow let's look at lodestar decoder https://github.com/ChainSafe/lodestar/blob/unstable/packages/reqresp/src/encodingStrategies/sszSnappy/snappyFrames/uncompress.ts#L17\n\n```\nuncompress(chunk: Uint8ArrayList): Uint8ArrayList | null {\n    this.buffer.append(chunk);\n    const result = new Uint8ArrayList();\n    while (this.buffer.length > 0) {\n      if (this.buffer.length < 4) break;\n\n      const type = getChunkType(this.buffer.get(0));\n      const frameSize = getFrameSize(this.buffer, 1);\n\n      if (this.buffer.length - 4 < frameSize) {\n        break;\n      }\n\n      const data = this.buffer.subarray(4, 4 + frameSize);\n      this.buffer.consume(4 + frameSize);\n\n      if (!this.state.foundIdentifier && type !== ChunkType.IDENTIFIER) {\n        throw \"malformed input: must begin with an identifier\";\n      }\n\n      if (type === ChunkType.IDENTIFIER) {\n        if (!Buffer.prototype.equals.call(data, IDENTIFIER)) {\n          throw \"malformed input: bad identifier\";\n        }\n        this.state.foundIdentifier = true;\n        continue;\n      }\n\n      if (type === ChunkType.COMPRESSED) {\n        result.append(uncompress(data.subarray(4)));\n      }\n      if (type === ChunkType.UNCOMPRESSED) {\n1)        result.append(data.subarray(4));\n      }\n    }\n    if (result.length === 0) {\n      return null;\n    }\n    return result;\n  }\n```\n\nAs you can see, checksum is not verified, bytes are appended to 'result'\n\n### Proof of Concept\n\nHow to reproduce:\n\nget poc via [gist link](https://gist.github.com/gln7/aab55674431b1c8d42a59ccf9d7cbf60) and run it:\n\n```\n$ node dec1.mjs \nchecking chunk type=255\nchecking chunk type=1\ngot uncompressed chunk..\nDecompressed ok 124 bytes\n```",
            "references": [
                {
                    "reference_url": "https://github.com/ChainSafe/lodestar",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "LOW",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/ChainSafe/lodestar"
                },
                {
                    "reference_url": "https://github.com/ChainSafe/lodestar/commit/18a0d681dbcc51fb2ac9456f31e91f4e31a18300",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "LOW",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/ChainSafe/lodestar/commit/18a0d681dbcc51fb2ac9456f31e91f4e31a18300"
                },
                {
                    "reference_url": "https://github.com/ChainSafe/lodestar/security/advisories/GHSA-m9c9-mc2h-9wjw",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "LOW",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/ChainSafe/lodestar/security/advisories/GHSA-m9c9-mc2h-9wjw"
                },
                {
                    "reference_url": "https://github.com/advisories/GHSA-m9c9-mc2h-9wjw",
                    "reference_id": "GHSA-m9c9-mc2h-9wjw",
                    "reference_type": "",
                    "scores": [],
                    "url": "https://github.com/advisories/GHSA-m9c9-mc2h-9wjw"
                }
            ],
            "fixed_packages": [
                {
                    "url": "http://public2.vulnerablecode.io/api/packages/376957?format=api",
                    "purl": "pkg:npm/%40lodestar/reqresp@1.25.0",
                    "is_vulnerable": false,
                    "affected_by_vulnerabilities": [],
                    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540lodestar/reqresp@1.25.0"
                }
            ],
            "aliases": [
                "GHSA-m9c9-mc2h-9wjw"
            ],
            "risk_score": 1.4,
            "exploitability": "0.5",
            "weighted_severity": "2.7",
            "resource_url": "http://public2.vulnerablecode.io/vulnerabilities/VCID-jgep-67qa-cfae"
        }
    ],
    "fixing_vulnerabilities": [],
    "risk_score": "1.4",
    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540lodestar/reqresp@1.4.0-dev.995d2a50c3"
}