v147 · Isolated Web Apps · Security

Compatibility Lab

Checks whether the exact Controlled Frame webRequest.onHeadersReceived SecurityInfo hook is available, then exercises the documented details.securityInfo shape with fallback states for non-IWA pages.

IWA-only API: chrome.webRequest and ControlledFrame are only accessible inside Isolated Web Apps. This lab probes the surrounding security primitives that are accessible from any secure context.

API probes

SecurityInfo field availability

SecurityInfo fieldContextChrome 147Description
chrome.webRequest.onHeadersReceivedIWA onlyResponse-header phase where SecurityInfo is exposed
extraInfoSpec: securityInfoIWA onlyRequests details.securityInfo
extraInfoSpec: securityInfoRawDerIWA onlyRequests certificate raw DER bytes
securityInfo.stateIWA only"secure" | "insecure" | "broken" | "weak"
securityInfo.certificates[].fingerprint.sha256IWA only✓ Chrome 147Browser-verified certificate fingerprint
PerformanceResourceTiming.nextHopProtocolAny HTTPSProtocol only; not a certificate API

Live security context probe

TLS-adjacent APIs (accessible outside IWA)
Click "Run probe" to inspect available TLS-adjacent APIs…
event phase not run
extraInfoSpec not run
SecurityInfo shape not run

IWA SecurityInfo pattern

/* WebRequest.SecurityInfo in ControlledFrame — IWA pattern */ /* Available in Isolated Web Apps only. The pattern: intercept HTTPS request → get cert fingerprint → verify the same fingerprint in a Direct Socket TLS handshake. */ /* In manifest.json (IWA): */ // "permissions": ["webRequest", "webRequestBlocking"] /* In app code: intercept and extract SecurityInfo */ chrome.webRequest.onHeadersReceived.addListener( (details) => { // details.securityInfo is present only when 'securityInfo' // or 'securityInfoRawDer' is requested in extraInfoSpec. const info = details.securityInfo; if (info?.state === 'secure' && info.certificates?.length) { const cert = info.certificates[0]; const fp256 = cert.fingerprint?.sha256; // hex string // Store for Direct Socket certificate pinning trustedFingerprints.set(new URL(details.url).hostname, fp256); } }, { urls: ['https://*/*'] }, ['securityInfo'] // or ['securityInfoRawDer'] for raw DER bytes ); /* Direct Socket connection — verify against stored fingerprint */ async function connectVerified(hostname, port) { const stored = trustedFingerprints.get(hostname); if (!stored) throw new Error('No fingerprint — do HTTPS preflight first'); const socket = new TCPSocket(hostname, port, { useSecureTransport: true }); const { readable } = await socket.opened; // Compare TLS cert fingerprint from the socket with stored value // to confirm you're connecting to the same server return socket; } /* For non-IWA contexts: use PerformanceResourceTiming for protocol info */ function getTLSInfo(url) { const entries = performance.getEntriesByName(url, 'resource'); if (!entries.length) return null; const e = entries[0]; return { protocol: e.nextHopProtocol, secureConnStart: e.secureConnectionStart, connectDuration: e.connectEnd - e.connectStart, tlsHandshakeDuration: e.requestStart - e.secureConnectionStart, }; }

references