Files
electron/shell/browser/preload_code_cache.h
T
claude[bot]andClaude cba5248898 docs: document preload code cache threat model (#54654)
Explain why SetFromRenderer stores the renderer-produced blob unverified and that the (BrowserContext, ProcessLock, id) scope is the security boundary.


Claude-Session: https://claude.ai/code/session_018A4Mt59Nr4h4WSFjyNNkxb

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-07 00:47:31 +09:00

93 lines
3.8 KiB
C++

// Copyright (c) 2026 Anthropic, PBC.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_BROWSER_PRELOAD_CODE_CACHE_H_
#define ELECTRON_SHELL_BROWSER_PRELOAD_CODE_CACHE_H_
#include <array>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "crypto/hash.h"
namespace content {
class RenderFrameHost;
}
namespace electron::preload_code_cache {
// sha256 of a preload script's source bytes.
using SourceHash = std::array<uint8_t, crypto::hash::kSha256Size>;
// One preload served to a frame: its cache id and the sha256 of the exact
// bytes the browser served.
using ServedPreload = std::pair<std::string, SourceHash>;
// Cache id for a webPreferences.preload script (stable across launches).
std::string IdForWebPreferencesPreload(const base::FilePath& path);
// Who a cache entry belongs to: the frame's BrowserContext (its data dir,
// under which the disk tier lives) plus its renderer's process lock (site,
// origin keying, sandboxing, guest and cross-origin-isolation status,
// storage partition). Entries are only ever served back within an equal
// scope, so a blob produced by one principal's renderer is never handed to
// another. Scope is the security boundary for cached blobs; see the note in
// SetFromRenderer(). |dir| is empty for off-the-record contexts (memory tier
// only).
struct Scope {
Scope();
Scope(const Scope&);
Scope(Scope&&);
~Scope();
base::FilePath dir;
std::string context_id;
std::string process_lock;
};
Scope ScopeForFrame(content::RenderFrameHost* rfh);
// Returns the V8 code cache blob recorded for |id| within |scope| and bound
// to source bytes whose sha256 is |source_hash| — or empty. Checks an
// in-memory tier first, then |scope.dir|. Disk I/O is synchronous (call behind
// a ScopedAllowBlocking) and happens at most once per (scope, id) per session.
//
// Entries are bound to the source hash: V8's own CachedData source check
// hashes only the source *length*, so without this a same-length source
// change would execute stale bytecode. V8 still validates version/flags at
// consume time.
std::vector<uint8_t> Get(const Scope& scope,
const std::string& id,
const SourceHash& source_hash);
// Records the preloads — id plus the sha256 of the exact bytes — that the
// browser served to |rfh| in a RendererStartupData push, replacing any
// previous record for that frame. SetFromRenderer() only accepts cache
// writes for tuples recorded here: the browser, not the renderer, is the
// authority for which source bytes a cache entry validates against. The
// record's lifetime is tied to the frame (cleared when the frame or its
// WebContents goes away).
void RecordServedPreloads(content::RenderFrameHost* rfh,
std::vector<ServedPreload> served);
// Stores a code cache blob reported by |rfh|'s renderer for a preload that
// was served to it. The write is dropped unless RecordServedPreloads()
// recorded the id for this exact frame and |claimed_source_hash| equals the
// browser-recorded hash of the served bytes. The claimed hash is only a
// cross-check (it detects a write racing a newer push after the preload
// changed on disk); what is persisted is the browser-recorded hash, keyed by
// the frame's Scope — the renderer never supplies the metadata that future
// loads validate against. The in-memory tier is written immediately; the
// disk write is fire-and-forget on the thread pool.
void SetFromRenderer(content::RenderFrameHost* rfh,
const std::string& id,
base::span<const uint8_t> claimed_source_hash,
base::span<const uint8_t> blob);
} // namespace electron::preload_code_cache
#endif // ELECTRON_SHELL_BROWSER_PRELOAD_CODE_CACHE_H_