Hacker DirectoryHacker Directory

WEB / WEB-10.S04

Service worker cache, object trust and DOM clobbering

Why do offline content, own properties and named DOM references require different trust decisions?

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Offline hit

  1. Client ready

    The controlled client needs public book-A; no cache result has been selected.

    Atlas client
    No title; controlled v2
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  2. Local GET preparation

    The UI prepares its fixed read request; preparation is not network receipt.

    Atlas client
    GET book-A ready
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  3. Local fetch event

    Worker event dispatch and cache lookup are local; no packet goes to the origin.

    Atlas client
    GET book-A ready
    Worker + Cache
    Cache candidate; shape pending
    Origin server
    No request; no read
  4. Local admission gate

    Exact key, public classification, app expiry and own schema pass for this declared entry.

    Atlas client
    GET book-A ready
    Worker + Cache
    Cache response ready; schema=2
    Origin server
    No request; no read
  5. Local response return

    The stored response is supplied locally; body interpretation is still a separate step.

    Atlas client
    Local body ready; source=cache
    Worker + Cache
    Cache response ready; schema=2
    Origin server
    No request; no read
  6. Literal title outcome

    Tea & Bread is expected as plain text; an offline hit does not prove fresh server permission.

    Atlas client
    Literal Tea & Bread; offline
    Worker + Cache
    Cache response ready; schema=2
    Origin server
    No request; no read

Network fallback

  1. Client ready

    The selected client uses a controlled worker; this branch has no stored entry.

    Atlas client
    No title; controlled v2
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  2. Read prepared

    A fixed GET for public book-A is prepared; the browser has received no response.

    Atlas client
    GET book-A ready
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  3. Local cache miss

    The worker checks the miss locally and prepares an ordinary GET to the owned origin.

    Atlas client
    GET book-A ready
    Worker + Cache
    Cache miss; network GET ready
    Origin server
    No request; no read
  4. Origin received request

    The prepared GET has arrived; the server public-read gate has not completed.

    Atlas client
    GET book-A ready
    Worker + Cache
    Cache miss; network GET ready
    Origin server
    GET arrived; public gate pending
  5. Local read response

    The server prepares declared public book JSON without using a private session body.

    Atlas client
    GET book-A ready
    Worker + Cache
    Cache miss; network GET ready
    Origin server
    Public JSON response ready
  6. Response at worker

    The HTTP response has arrived at the worker; shape and cache admission remain local decisions.

    Atlas client
    GET book-A ready
    Worker + Cache
    Network body arrived; gate pending
    Origin server
    Public JSON response ready
  7. Local schema/version

    The fixed body passes the selected own-field schema; no storage write is claimed here.

    Atlas client
    GET book-A ready
    Worker + Cache
    Public title response ready; v2
    Origin server
    Public JSON response ready
  8. Local client outcome

    The worker returns its normal response locally and an ordinary title outcome is expected.

    Atlas client
    Literal Tea & Bread; network
    Worker + Cache
    Public title response ready; v2
    Origin server
    Public JSON response ready

Shape denied

  1. Offline start

    The client is offline with a stored candidate; the server has received no request.

    Atlas client
    No title; controlled v2
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  2. Local read intent

    The fixed title read is prepared without claiming an opened server connection.

    Atlas client
    GET book-A ready
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  3. Local candidate check

    The declared candidate lacks the expected own field; an inherited label cannot satisfy admission.

    Atlas client
    GET book-A ready
    Worker + Cache
    Own shape denied; offline
    Origin server
    No request; no read
  4. Local fallback

    Offline policy selects a plain unavailable result rather than accepting the candidate.

    Atlas client
    GET book-A ready
    Worker + Cache
    Offline fallback ready
    Origin server
    No request; no read
  5. Client fallback outcome

    The client gets a local unavailable outcome, not server authorization denial or a new response.

    Atlas client
    Title unavailable; no origin read
    Worker + Cache
    Offline fallback ready
    Origin server
    No request; no read

Named element

  1. Fixed DOM ledger

    The benign atlasBookForm/bookTitle declaration separates local names and value types.

    Atlas client
    No title; controlled v2
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  2. Named reference

    Expected Window/form lookup returns element references rather than a title string.

    Atlas client
    Form/control references; not text
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  3. Local type gate

    The app uses retained local configuration, not coercion of an element reference to a string.

    Atlas client
    Local config own title validated
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  4. Local explicit value

    Expected control .value and own config title are plain data with different owners.

    Atlas client
    Plain title Tea & Bread ready
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read
  5. Expected text outcome

    An ordinary text outcome is expected; no actual named DOM getter or clobbering executes.

    Atlas client
    Literal Tea & Bread; local only
    Worker + Cache
    atlas-v2; public entry
    Origin server
    No request; no read

ACTORS · STATE · DATA FLOW

Worker, cache and client hold separate state

Atlas should read the public title Tea & Bread offline. A worker is not a page script with DOM access: it participates in fetch events for controlled clients from its own worker context and can select a response. Secure-context, same-origin registration and script-directory maximum-scope rules apply; Service-Worker-Allowed can adjust the path limit. That path is not a substitute for an origin security boundary between users. Registration success, installed/waiting/active state and control of a document are separate observations. A page opened before initial activation may require reloading by default; clients.claim and skipWaiting are separate lifecycle choices.

A cache hit does not decide freshness or permission

Cache API stores request/response pairs in named caches managed by the application. It differs from the HTTP cache: Cache-Control freshness or no-store alone does not manage this storage lifecycle. The app chooses admission, versioning, updates, deletion and fallback; browser quota or eviction can remove data. Atlas-v2 admits only public JSON book-A at an exact URL with schemaVersion=2. This selected application gate does not implement Cache.match; Vary and matching options also matter in a real implementation. Parsing a cached body establishes neither fresh permission nor revocation. Mixing session-bound data into this public offline policy could disclose it after account changes or logout.

Own properties and named DOM lookups differ

Require own id/title/schemaVersion fields and expected types on the JSON book object. A readable displayMode inherited from a prototype is not necessarily an input field; Object.hasOwn distinguishes these cases. Prototype pollution concerns unsafe writes or merges changing shared/inherited state later consumed elsewhere; JSON parsing alone is not presented as performing that change. DOM clobbering concerns HTML id/name entering application lookups through legacy Window/form named access, not prototype mutation. The fixed atlasBookForm and bookTitle control produce element references through named access, not title strings. Local module configuration, explicit element lookup and expected element/value types separate these namespaces. Freezing trusted own objects is shallow and is not a general fix for window/document.

2. Red · Trust & Impact

Read every scenario without motion

Offline authority

  1. Public cache entry

    The atlas-v2 public entry with schemaVersion 2 is selected at now 100. Even with online false, the normal row expects cache; the server receives no new request for this display.

    Cache and object ledger
    Public cache / now100 < expires120
    Eligibility and ownership
    Not yet evaluated
    Consumer result
    Not yet evaluated
  2. HTTP-header distinction

    The entry carries Cache-Control no-store, but this Cache API example evaluates eligibility through appExpiresAt. HTTP freshness or automatic header enforcement is not its rule.

    Cache and object ledger
    Public cache / now100 < expires120
    Eligibility and ownership
    Application expiration ≠ HTTP cache policy
    Consumer result
    Not yet evaluated
  3. Do not count refreshed authz

    Returning a cache body is not current server authorization. A stale public title is not automatically compromise; private admission requires separate user/context lifecycle design.

    Cache and object ledger
    Public cache / now100 < expires120
    Eligibility and ownership
    Application expiration ≠ HTTP cache policy
    Consumer result
    Offline body ≠ fresh server grant
  4. Shape-denied branch

    shape-denied has entryPresent true but ownShape false and selects offline-fallback. Local rejection cannot be labelled a server-permission result; there is no network here.

    Cache and object ledger
    Entry present / shape false / fallback
    Eligibility and ownership
    Application expiration ≠ HTTP cache policy
    Consumer result
    Offline body ≠ fresh server grant
  5. Retain controller assumption

    controlledDeclared true and workerVersion 2 are starting assumptions. Scope /books/ is not a hard security boundary or evidence that an old client automatically switched to v2.

    Cache and object ledger
    Entry present / shape false / fallback
    Eligibility and ownership
    Declared controller v2 / lifecycle separate
    Consumer result
    Offline body ≠ fresh server grant
  6. Report a bounded selection

    Cache, network and fallback remain tied to application eligibility. The data ledger does not establish actual Service Worker execution, Cache API writes or durable storage.

    Cache and object ledger
    Entry present / shape false / fallback
    Eligibility and ownership
    Declared controller v2 / lifecycle separate
    Consumer result
    Finite cache selection / runtime unmeasured

Property identity

  1. Inherited option

    inheritedFixture reads displayMode cards but hasOwnDisplayMode false. A readable property does not establish membership in the own-data contract.

    Cache and object ledger
    Read cards / hasOwn false
    Eligibility and ownership
    Not yet evaluated
    Consumer result
    Not yet evaluated
  2. Closed own schema

    The book consumer checks own id, title and schemaVersion fields and types. Presence and value/type differ; Object.hasOwn grants no business permission.

    Cache and object ledger
    Read cards / hasOwn false
    Eligibility and ownership
    Own fields + value/type / no grant
    Consumer result
    Not yet evaluated
  3. Shallow-freeze effect

    outerFrozen is true and nestedFrozen false, with nestedRevisionAfterUpdate 3. Freezing the outer object does not make nested data immutable or retain review scope.

    Cache and object ledger
    Read cards / hasOwn false
    Eligibility and ownership
    Own fields + value/type / no grant
    Consumer result
    Outer frozen / nested revision3
  4. DOM named reference

    atlasBookForm lookup expects HTMLFormElement and bookTitle expects HTMLInputElement. Element references and control value Tea & Bread differ in type; a window name is not local config.

    Cache and object ledger
    Form reference / control reference / value string
    Eligibility and ownership
    Own fields + value/type / no grant
    Consumer result
    Outer frozen / nested revision3
  5. Separate two mechanisms

    DOM named lookup is not prototype mutation. jsonParseMutatesPrototype is false; inherited lookup and HTML namespace lookup have different source and consumer conditions.

    Cache and object ledger
    Form reference / control reference / value string
    Eligibility and ownership
    Prototype lookup ≠ DOM named lookup
    Consumer result
    Outer frozen / nested revision3
  6. Use explicit references

    The consumer reads title through its localConfig reference rather than trusting a truthy global named property as a string. Neither the normal form nor inherited option is presented as executed attack evidence.

    Cache and object ledger
    Form reference / control reference / value string
    Eligibility and ownership
    Prototype lookup ≠ DOM named lookup
    Consumer result
    Explicit config / typed own data

ASSUMPTION · BOUNDARY · CONTROL

Mistaking a persisted response for current authority

A normal cache hit can supply content without a network request, given a selected controlled worker/client and eligible stored entry. The broken assumption is that the server reevaluated freshness or authorization during this display. Effects include stale revisions or, with incorrect admission, private content displayed in the wrong user context; a stale public title is not automatically a compromise. A new worker version does not prove deletion of older entries or control of old clients. Keep public/private classification, content revision, key/context and account lifecycle explicit. The offline shape-denied branch selects a local fallback; no server request means no server permission-denial evidence.

A readable property may not be expected data

Accepting object.title or a Window-level name merely because it is truthy can create a trust error. Prototype paths and DOM named properties require different conditions: unsafe object-state propagation versus relevant HTML names consumed by application code. Impact depends on the confused field and its eventual sink; a named form or benign inherited option is not itself an exploit. The fixed book form is ordinary and harmless, showing only the type distinction between references and title strings. Controls include a closed own-field schema, explicit local configuration and markup namespace policy. Shallow freezing does not make nested objects immutable, and an XSS sanitizer configuration does not automatically resolve every named-property risk.

3. Blue · Secure development & defense

Read every scenario without motion

Selection controls

  1. Read four outcomes

    The rows predict cache for normal/named-dom, network for an online missing entry and fallback for shape-denied. The named-dom scene does not fetch a cache entry; it only examines the fixed form/control reference. Keep matrix expectations separate from the mechanism actually modeled.

    Selection ledger
    Cache/cache / network / fallback
    Source and type review
    Not yet evaluated
    Regression result
    Not yet evaluated
  2. Cache eligibility record

    Request URL, atlas-v2, public classification, schemaVersion 2 and application expiration are retained together. No origin request log does not mean no cache body was seen.

    Selection ledger
    Cache/cache / network / fallback
    Source and type review
    URL/version/public/schema/expiration
    Regression result
    Not yet evaluated
  3. Own-object control

    ownObject carries id book-A, title Tea & Bread and schemaVersion 2. An inherited option with hasOwn false and shallow-freeze behavior are separate ownership controls.

    Selection ledger
    Cache/cache / network / fallback
    Source and type review
    URL/version/public/schema/expiration
    Regression result
    Own fields / inherited false / shallow freeze
  4. Named-DOM expectation

    namedDOM executed is false; form/control interfaces are expected results only. The distinction prevents native object-property tests from being presented as actual DOM named lookup.

    Selection ledger
    Expected interfaces / DOM unexecuted
    Source and type review
    URL/version/public/schema/expiration
    Regression result
    Own fields / inherited false / shallow freeze
  5. Separate missing field and type

    Object.hasOwn can be true for a null value; an accepted schema checks title string and revision types separately. A normal own-object success is not assumed for inherited input.

    Selection ledger
    Expected interfaces / DOM unexecuted
    Source and type review
    Presence ≠ valid value/type
    Regression result
    Own fields / inherited false / shallow freeze
  6. Bounded regression evidence

    Finite selection and object ownership are verified separately. Without Cache API, HTML parser or Service Worker lifecycle execution, these controls prove neither offline-browser success nor current authz.

    Selection ledger
    Expected interfaces / DOM unexecuted
    Source and type review
    Presence ≠ valid value/type
    Regression result
    Finite object/cache predicates / browser separate

Controller gap

  1. Empty origin log

    The artifact carries supplied row predicates for a cache-selection function plus object/lookup ledgers. The network branch models origin receipt; normal and shape-denied never reach origin. named-dom is not another execution of its cache row: it isolates element-reference versus value-string interpretation. Declared controller/version and expected lookups are not actual browser measurements. There is no origin request record while the client shows a title. A normal cache flow can return a body without contacting origin; an empty log cannot establish absence of content.

    Selection ledger
    Title known / origin receipt absent
    Source and type review
    Not yet evaluated
    Regression result
    Not yet evaluated
  2. Missing controller identity

    Current controller, worker version and response-source records are missing. The profile v2 assumption cannot fill the gap; registration and control are separate stages.

    Selection ledger
    Title known / origin receipt absent
    Source and type review
    Current controller/version/source missing
    Regression result
    Not yet evaluated
  3. Leave source unknown

    Cache/network/fallback selection remains UNKNOWN. Displaying Tea & Bread alone establishes neither own-field schema correctness nor private-state lifecycle success.

    Selection ledger
    Title known / origin receipt absent
    Source and type review
    Current controller/version/source missing
    Regression result
    Response source/eligibility UNKNOWN
  4. Required source information

    Client controller, version, request key, cache name and response source must be associated with the same display. Fallback reason and application expiration differ from server receipt.

    Selection ledger
    Needed: controller/version/key/source/reason
    Source and type review
    Current controller/version/source missing
    Regression result
    Response source/eligibility UNKNOWN
  5. Local-lookup boundary

    Finding a named form reference does not prove local configuration changed. Config reference and consumer type evidence are needed; property lookup is not server authorization.

    Selection ledger
    Needed: controller/version/key/source/reason
    Source and type review
    Config reference/type separate from authz
    Regression result
    Response source/eligibility UNKNOWN
  6. Purpose of new review

    New evidence must answer content source and object ownership separately. This event retains the title observation without adding controller, cache-eligibility or DOM-lookup success claims.

    Selection ledger
    Needed: controller/version/key/source/reason
    Source and type review
    Config reference/type separate from authz
    Regression result
    Retain title / collect scoped source evidence

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Observe the cache decision at its owner

The artifact carries supplied row predicates for a cache-selection function plus object/lookup ledgers. The network branch models origin receipt; normal and shape-denied never reach origin. named-dom is not another execution of its cache row: it isolates element-reference versus value-string interpretation. Declared controller/version and expected lookups are not actual browser measurements. Observe client controller identity, worker/cache version, URL, response source and fallback reason together. An absent origin access-log request does not prove the client displayed no content; a cache hit does not reach the origin. Admission evidence records selected public classification and schema revision without logging secrets or private bodies. Response cloning and event.waitUntil support storage work, but a successful clone proves neither successful nor durable writing. Update regression separates old controlled clients, waiting workers, eviction/miss and application-version mismatch. Deleting one named cache does not prove full storage cleanup; any private offline state needs an explicit logout/account-switch lifecycle.

Constrain field ownership and DOM namespaces

Validate accepted own keys and types at the book consumer. Map or null-prototype dictionaries reduce inherited lookup for dynamic data but grant no business permission. Object.hasOwn can return true for null or undefined, so separate presence from value/type checks. Read trusted configuration through local references rather than global named DOM values. The fixed form/control lookup separates the expected interface from its .value string; duplicate names may produce collections or past-name behavior rather than a single string. Regression distinguishes ordinary own objects, missing own fields, benign inherited options and named element references. Pure fixtures or Node property observations do not substitute for DOM named access or Service Worker Cache API execution.

NOW APPLY IT TO THE CASE

Does a cache hit mean fresh server authority or a trusted object?

Atlas v2 public cache selects book-A at now 100 with appExpiresAt 120. A mismatching own-field shape yields offline fallback. A normal form-name lookup expects an HTMLFormElement, while inherited displayMode is an object lookup result; neither is a title string or server authority.

Order response receipt, parsing, callbacks and visible DOM outcome in the case. Try explaining the retained old title through this lesson’s browser-state distinctions.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Offline cache and object ownership ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "profile": {
    "origin": "https://atlas.example.org",
    "script": "/books/sw.js",
    "scope": "/books/",
    "controlledDeclared": true,
    "workerVersion": 2,
    "cacheName": "atlas-v2",
    "publicOnly": true,
    "actualSW": false,
    "actualCache": false,
    "actualDOM": false,
    "actualNetwork": false
  },
  "request": {
    "method": "GET",
    "url": "https://atlas.example.org/books/book-A.json"
  },
  "entry": {
    "url": "https://atlas.example.org/books/book-A.json",
    "cacheName": "atlas-v2",
    "visibility": "public",
    "body": {
      "id": "book-A",
      "title": "Tea & Bread",
      "schemaVersion": 2
    },
    "httpHeaders": {
      "Cache-Control": "no-store"
    },
    "appExpiresAt": 120
  },
  "rows": [
    {
      "case": "normal",
      "now": 100,
      "online": false,
      "entryPresent": true,
      "ownShape": true,
      "expected": "cache"
    },
    {
      "case": "network",
      "now": 100,
      "online": true,
      "entryPresent": false,
      "ownShape": true,
      "expected": "network"
    },
    {
      "case": "shape-denied",
      "now": 100,
      "online": false,
      "entryPresent": true,
      "ownShape": false,
      "expected": "offline-fallback"
    },
    {
      "case": "named-dom",
      "now": 100,
      "online": false,
      "entryPresent": true,
      "ownShape": true,
      "expected": "cache"
    }
  ],
  "objectLedger": {
    "expectedOwnFields": [
      "id",
      "title",
      "schemaVersion"
    ],
    "ownObject": {
      "id": "book-A",
      "title": "Tea & Bread",
      "schemaVersion": 2
    },
    "inheritedFixture": {
      "prototype": {
        "displayMode": "cards"
      },
      "own": {
        "title": "Tea & Bread"
      },
      "readDisplayMode": "cards",
      "hasOwnDisplayMode": false
    },
    "shallowFreeze": {
      "outerFrozen": true,
      "nestedFrozen": false,
      "nestedRevisionAfterUpdate": 3
    },
    "jsonParseMutatesPrototype": false
  },
  "namedDOM": {
    "declaration": "<form name=\"atlasBookForm\"><input name=\"bookTitle\" value=\"Tea &amp; Bread\"></form>",
    "windowLookupExpected": "HTMLFormElement",
    "formLookupExpected": "HTMLInputElement",
    "controlValueExpected": "Tea & Bread",
    "localConfig": {
      "title": "Tea & Bread"
    },
    "lookupGrantsTrust": false,
    "executed": false
  },
  "boundaries": {
    "cacheUsesHTTPFreshnessAutomatically": false,
    "pathIsHardSecurityBoundary": false,
    "offlineResultIsFreshAuthorization": false,
    "freezeIsDeep": false,
    "DOMNamedLookupIsPrototypeMutation": false
  }
}
profile
Selected controlled-worker and public-only declaration.
entry
Request key, application expiration and HTTP headers are distinct.
rows
Cache, network or local fallback choice.
objectLedger
Own/inherited fields and shallow-freeze expectations.
namedDOM
Fixed benign form/control lookup types; not browser execution.

Synthetic public response and predeclared object/DOM ledger; no Cache API or HTML parser executes.

Two reality checks

Cache-Control no-store controls all offline storage.

Cache API is separate author-managed storage requiring admission and deletion policy.

DOM clobbering is the same mutation as prototype pollution.

Named DOM references and prototype-chain state are different namespaces and mechanisms.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

An offline entry exists, but ownShape=false. Does the cache policy accept its body?

  1. Yes. Offline mode removes all schema checks.
  2. No. The outcome is offline-fallback.
Read the explained answer

No. The outcome is offline-fallback. — The shape-denied row has entryPresent=true, but withholds the cached body because the object condition failed.

displayMode is readable, but hasOwnDisplayMode=false. Why can the nested revision change while the outer object is frozen?

Selected lesson artifact fields; teaching example

objectLedger.inheritedFixture
{"prototype":{"displayMode":"cards"},"own":{"title":"Tea & Bread"},"readDisplayMode":"cards","hasOwnDisplayMode":false}
objectLedger.shallowFreeze
{"outerFrozen":true,"nestedFrozen":false,"nestedRevisionAfterUpdate":3}
  1. Lookup can find an inherited property; freeze is shallow here.
  2. Every readable property is own; freezing the outer object freezes all nested data.
Read the explained answer

Lookup can find an inherited property; freeze is shallow here. — displayMode comes from the prototype. outerFrozen=true with nestedFrozen=false permits nestedRevisionAfterUpdate=3.

The cache entry has Cache-Control: no-store. Why do explicit Cache API storage and appExpiresAt need a separate policy, and what does named DOM lookup not establish?

Read the explained answer

Cache API does not automatically apply HTTP freshness; the application checks version, expiry and public eligibility. An offline body is not fresh server authorization. Named lookup may return an HTMLFormElement/HTMLInputElement, but that does not establish trusted local config. Named DOM lookup and prototype mutation are different mechanisms. This artifact does not execute a real service worker, cache or DOM operation.

□ I did not equate Cache API policy with HTTP-cache rules.

□ I did not treat named DOM lookup as config or object trust.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope