Skip to content

Instantly share code, notes, and snippets.

@Aakash1103Jha
Last active December 2, 2023 21:21
Show Gist options
  • Save Aakash1103Jha/493c088e3725eadaa7f0ced93643d014 to your computer and use it in GitHub Desktop.
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>
/**
* 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