Last active
December 2, 2023 21:21
-
-
Save Aakash1103Jha/493c088e3725eadaa7f0ced93643d014 to your computer and use it in GitHub Desktop.
A custom react hook to generate integrity metadata required when adding subresource integrity attribute to your <script> or <link>
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* useHashGenerator.tsx | |
* @example | |
* const integrityValue = await generateContentHash({ | |
url: "https://somecdn.com/assets/dist/styles/acumin.css", | |
hashAlgorithm: "sha512", | |
}) | |
* generateContentHash({ | |
url: "https://somecdn.com/assets/dist/styles/acumin.css", | |
hashAlgorithm: "sha512", | |
}).then(integrityValue => { // logic to use integrityValue }) | |
*/ | |
export type HashAlgorithmType = "sha256" | "sha384" | "sha512"; | |
export type GenerateContentHashType = { | |
url: string; | |
hashAlgorithm?: HashAlgorithmType; | |
}; | |
export default function useHashGenerator() { | |
/** | |
* The function `getHashAlgorithm` takes in a `HashAlgorithmType` and returns the corresponding | |
* string representation of the hash algorithm. | |
* @param {HashAlgorithmType} algo - The `algo` parameter is of type `HashAlgorithmType`. | |
* @returns the corresponding hash algorithm name based on the input `algo`. | |
*/ | |
function getHashAlgorithm(algo: HashAlgorithmType) { | |
switch (algo) { | |
case "sha256": | |
return "SHA-256"; | |
case "sha384": | |
return "SHA-384"; | |
case "sha512": | |
return "SHA-512"; | |
} | |
} | |
/** | |
* The function `getContentType` determines the type of content based on the provided content header. | |
* @param {string | null} contentHeader - The `contentHeader` parameter is a string that represents | |
* the content type of a file. It can be `null` if the content type is not provided. | |
* @returns either an error message or a string indicating the type of content. | |
*/ | |
function getContentType(contentHeader: string | null) { | |
if (!contentHeader) return new Error("Failed to determine file type"); | |
if (contentHeader.includes("text/css")) return "style"; | |
if (contentHeader.includes("javascript")) return "script"; | |
} | |
/** | |
* The function `getFileData` is an asynchronous function that fetches file data from a given URL and | |
* returns the file type and buffer. | |
* @param {string} url - The `url` parameter is a string that represents the URL of the file you want | |
* to fetch. | |
* @returns The function `getFileData` returns a promise that resolves to an object with properties | |
* `type` and `buffer`, or an `Error` object if there was an error fetching the file content. | |
*/ | |
async function getFileData(url: string) { | |
try { | |
const res = await fetch(url); | |
if (res.status !== 200) return new Error("Failed to get file content"); | |
const type = getContentType(res.headers.get("content-type")); | |
const buffer = await res.arrayBuffer(); | |
return { type, buffer }; | |
} catch (error) { | |
console.error("Failed to get file content: ", error); | |
return error as Error; | |
} | |
} | |
/** | |
* The function generates integrity metadata by encoding a hash value using a specified algorithm. | |
* @param {HashAlgorithmType} algorithm - The `algorithm` parameter is of type `HashAlgorithmType` | |
* and represents the algorithm used for hashing. It could be a string value such as "SHA-256" or | |
* "SHA-512". | |
* @param {ArrayBuffer} hashValue - The `hashValue` parameter is an `ArrayBuffer` that represents the | |
* hash value generated using the specified `algorithm`. | |
* @returns a string in the format `-`. | |
*/ | |
function generateIntegrityMeta(algorithm: HashAlgorithmType, hashValue: ArrayBuffer) { | |
try { | |
const base64string = btoa(String.fromCharCode(...new Uint8Array(hashValue))); | |
return `${algorithm}-${base64string}`; | |
} catch (error) { | |
console.error("Failed to create integrity metadata: ", error); | |
return error as Error; | |
} | |
} | |
/** | |
* The function `createContentHash` asynchronously creates a hash of the given buffer using the | |
* specified algorithm. | |
* @param {ArrayBuffer} buffer - The `buffer` parameter is an `ArrayBuffer` that contains the data | |
* for which you want to create a content hash. An `ArrayBuffer` is a fixed-length, low-level binary | |
* data buffer that represents a generic, fixed-length raw binary data buffer. It is commonly used to | |
* handle binary data | |
* @param {HashAlgorithmType} algorithm - The `algorithm` parameter is a string that represents the | |
* hash algorithm to be used for creating the content hash. It can be one of the following values: | |
* @returns a Promise that resolves to the content hash of the provided buffer. If an error occurs | |
* during the hashing process, the function will return the error object. | |
*/ | |
async function createContentHash(buffer: ArrayBuffer, algorithm: HashAlgorithmType) { | |
try { | |
return await crypto.subtle.digest(getHashAlgorithm(algorithm), buffer); | |
} catch (error) { | |
console.error("Failed to create content hash: ", error); | |
return error as Error; | |
} | |
} | |
/** | |
* The function generates a content hash for a given URL using a specified hash algorithm and returns | |
* the integrity meta data. | |
* @param {GenerateContentHashType["url"]}`url` - The URL of the file for which the content hash needs to be generated. | |
* @param {GenerateContentHashType["hashAlgorithm"]} `hashAlgorithm` - The hash algorithm to be used. Defaults to `sha256` | |
* @returns The function `generateContentHash` returns a Promise that resolves to the integrity meta string generated from the content hash. If there is an error during the process, it returns the error message as a string or an Error object. | |
*/ | |
async function generateContentHash({ url, hashAlgorithm = "sha256" }: GenerateContentHashType) { | |
try { | |
const _fetchResult = await getFileData(url); | |
if (_fetchResult instanceof Error) return _fetchResult.message; | |
const _hashResult = await createContentHash(_fetchResult.buffer, hashAlgorithm); | |
if (_hashResult instanceof Error) return _hashResult.message; | |
const _integrityResult = generateIntegrityMeta(hashAlgorithm, _hashResult); | |
if (_integrityResult instanceof Error) return _integrityResult.message; | |
return _integrityResult; | |
} catch (error) { | |
console.error(error); | |
return error as Error; | |
} | |
} | |
return { generateContentHash }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment