diff --git a/web/src/plugins/types.ts b/web/src/plugins/types.ts index dd11c35c22..51fecffbd3 100644 --- a/web/src/plugins/types.ts +++ b/web/src/plugins/types.ts @@ -22,6 +22,12 @@ export interface PluginManifest { entry: string; css?: string | null; has_api: boolean; + /** + * Optional Subresource Integrity hash (e.g. "sha384-..."). When set, + * the browser will refuse to execute the plugin bundle if its hash + * does not match. This protects against tampered plugin delivery. + */ + integrity?: string; source: string; } diff --git a/web/src/plugins/usePlugins.ts b/web/src/plugins/usePlugins.ts index 147b1f0a84..fcf9f7645a 100644 --- a/web/src/plugins/usePlugins.ts +++ b/web/src/plugins/usePlugins.ts @@ -68,6 +68,16 @@ export function usePlugins() { script.setAttribute("data-hermes-plugin", manifest.name); script.src = scriptSrc; script.async = true; + // SRI integrity verification — defense against compromised plugin + // delivery. Plugin manifests can declare an integrity hash + // (e.g. "sha384-...") which the browser verifies before executing. + // Without this, a man-in-the-middle or compromised plugin server + // can substitute the JS bundle silently. Opt-in: when no integrity + // is declared in the manifest, behavior is unchanged. + if (manifest.integrity && typeof manifest.integrity === "string") { + script.integrity = manifest.integrity; + script.crossOrigin = "anonymous"; + } script.onerror = () => { setPluginLoadError(manifest.name, "LOAD_FAILED"); console.warn(