Skip to content

[finding] If-Match: "" silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded #13576

Description

@zhuangjianguo

Filed unassigned by the domain:engine lane PM. Recording only — no severity asserted, routing is triage's. Surfaced by the clause ② contract review of PR #13569 (#13382); I verified the mechanism in source independently before filing. This predates that PR and is not caused by it.

Measured on 70fe54891e (pre-#13569main)

packages/metadata-protocol/src/protocol.ts:1378:

functionnormaliseVersionToken(v: unknown): string|null{if(v===null||v===undefined)returnnull;consts=String(v).trim();if(!s)returnnull;// ← emptiness checked HEREif(s.length>=2&&s.startsWith('"')&&s.endsWith('"')){returns.slice(1,-1);// ← …then stripped to '' HERE}returns;}

and its caller, the guarded-DELETE door at :10037:

if(!normaliseVersionToken(expectedVersion))return;// ← falsy '' reads as "no token"

For the token "" — a valid RFC-7232 entity-tag with an empty opaque value:

  1. s = '""' is non-empty, so if (!s) return null does not fire;
  2. the quote-strip returns '';
  3. nothing re-checks emptiness after the strip;
  4. the caller's falsiness test reads that '' as "the client sent no version" and returns early.

The concurrency check is skipped and the write or delete proceeds unguarded.

Why this is a defect rather than a quirk

The whole point of If-Match is to make a write conditional. A client that sends If-Match: "" is asking for a guarded write — it is not declining the guard. The current behaviour grants the opposite of what the header requests, and it does so silently: there is no refusal, no warning, and the response is indistinguishable from a successful guarded write.

Note the asymmetry: an unparseable or opaque token (v2, rowversion-7) fails toward 409, which is the safe direction for a concurrency primitive. "" fails toward accept. It is the one token shape that opts out of the guard rather than failing it.

Reachability

  • Header path: REST forwards If-Match into expectedVersion.
  • Body path: expectedVersion is declared z.string().optional(), so '""' passes schema validation, and REST's truthiness check passes the non-empty string '""' through.

Not reachable from the first-party Console: occSave / InlineEditSaveBar only attach a truthy token they received from a prior read. So the exposure is to third-party and hand-rolled clients, not to the shipped UI.

Relationship to PR #13569 — read this before acting

#13569 (the Postgres OCC repair) incidentally closes this, as a side effect of returning a wrapper object instead of a bare string: { token: '', instant: null } is truthy, so the caller stops short-circuiting and the guard runs, yielding a 409.

⚠️That PR is being changed to restore the current behaviour, deliberately — because turning this accept into a refusal is a contract decision, that PR is a p1 bug fix, and #6479 established that new rejections are not installed silently. So #13569 will land with "" still disabling the guard, and this card is what carries the question.

The question for triage / the maintainer: should If-Match: "" be able to disable optimistic concurrency at all? Three shapes, not costed here:

  1. Keep as-is"" means "no guard". Cheapest, and it is today's shipped behaviour; but it means an empty entity-tag silently turns off a concurrency primitive.
  2. Fail closed"" is a token that matches nothing ⇒ 409. Safer direction, and it is what fix(metadata-protocol): compare OCC version tokens as instants, not spellings (#13382) #13569 would have done by accident. It is a new rejection on a shipped API and needs to be announced.
  3. Refuse the shape — reject "" at the schema/ingress as a malformed entity-tag, with a message saying so. Loudest, and distinguishes "you sent something meaningless" from "you lost a race".

What this does NOT claim

I did not find a deployed client that sends If-Match: "", and I do not assert anyone has been bitten. What is measured is that the platform accepts the shape and answers it by dropping the guard. Nor do I assert which of the three options is right — that is exactly why it is filed rather than fixed.

Related

#13382 / PR #13569 (where the clause ② review surfaced it) · #6479 (precedent: new rejections are not installed silently) · ADR-0055 / ADR-0058

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
     blocks
    (function() {
    function addCopyButtons() {
    document.querySelectorAll('pre code').forEach(function(codeBlock) {
    if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
    codeBlock.parentElement.setAttribute('data-copy-added', 'true');
    var btn = document.createElement('button');
    btn.textContent = 'Copy';
    btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
    btn.onmouseover = function() { this.style.opacity = '1'; };
    btn.onmouseout = function() { this.style.opacity = '0.7'; };
    btn.onclick = function() {
    navigator.clipboard.writeText(codeBlock.textContent).then(function() {
    btn.textContent = 'Copied!';
    setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
    });
    };
    codeBlock.parentElement.style.position = 'relative';
    codeBlock.parentElement.appendChild(btn);
    });
    }
    addCopyButtons();
    // Re-run on dynamic content
    var observer = new MutationObserver(addCopyButtons);
    observer.observe(document.body, { childList: true, subtree: true });
    })();
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    [finding] `If-Match: ""` silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded · Issue #13576 · objectstack-ai/objectstack · GitHub
    Skip to content

    [finding] If-Match: "" silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded #13576

    Description

    @zhuangjianguo

    Filed unassigned by the domain:engine lane PM. Recording only — no severity asserted, routing is triage's. Surfaced by the clause ② contract review of PR #13569 (#13382); I verified the mechanism in source independently before filing. This predates that PR and is not caused by it.

    Measured on 70fe54891e (pre-#13569main)

    packages/metadata-protocol/src/protocol.ts:1378:

    functionnormaliseVersionToken(v: unknown): string|null{if(v===null||v===undefined)returnnull;consts=String(v).trim();if(!s)returnnull;// ← emptiness checked HEREif(s.length>=2&&s.startsWith('"')&&s.endsWith('"')){returns.slice(1,-1);// ← …then stripped to '' HERE}returns;}

    and its caller, the guarded-DELETE door at :10037:

    if(!normaliseVersionToken(expectedVersion))return;// ← falsy '' reads as "no token"

    For the token "" — a valid RFC-7232 entity-tag with an empty opaque value:

    1. s = '""' is non-empty, so if (!s) return null does not fire;
    2. the quote-strip returns '';
    3. nothing re-checks emptiness after the strip;
    4. the caller's falsiness test reads that '' as "the client sent no version" and returns early.

    The concurrency check is skipped and the write or delete proceeds unguarded.

    Why this is a defect rather than a quirk

    The whole point of If-Match is to make a write conditional. A client that sends If-Match: "" is asking for a guarded write — it is not declining the guard. The current behaviour grants the opposite of what the header requests, and it does so silently: there is no refusal, no warning, and the response is indistinguishable from a successful guarded write.

    Note the asymmetry: an unparseable or opaque token (v2, rowversion-7) fails toward 409, which is the safe direction for a concurrency primitive. "" fails toward accept. It is the one token shape that opts out of the guard rather than failing it.

    Reachability

    • Header path: REST forwards If-Match into expectedVersion.
    • Body path: expectedVersion is declared z.string().optional(), so '""' passes schema validation, and REST's truthiness check passes the non-empty string '""' through.

    Not reachable from the first-party Console: occSave / InlineEditSaveBar only attach a truthy token they received from a prior read. So the exposure is to third-party and hand-rolled clients, not to the shipped UI.

    Relationship to PR #13569 — read this before acting

    #13569 (the Postgres OCC repair) incidentally closes this, as a side effect of returning a wrapper object instead of a bare string: { token: '', instant: null } is truthy, so the caller stops short-circuiting and the guard runs, yielding a 409.

    ⚠️That PR is being changed to restore the current behaviour, deliberately — because turning this accept into a refusal is a contract decision, that PR is a p1 bug fix, and #6479 established that new rejections are not installed silently. So #13569 will land with "" still disabling the guard, and this card is what carries the question.

    The question for triage / the maintainer: should If-Match: "" be able to disable optimistic concurrency at all? Three shapes, not costed here:

    1. Keep as-is"" means "no guard". Cheapest, and it is today's shipped behaviour; but it means an empty entity-tag silently turns off a concurrency primitive.
    2. Fail closed"" is a token that matches nothing ⇒ 409. Safer direction, and it is what fix(metadata-protocol): compare OCC version tokens as instants, not spellings (#13382) #13569 would have done by accident. It is a new rejection on a shipped API and needs to be announced.
    3. Refuse the shape — reject "" at the schema/ingress as a malformed entity-tag, with a message saying so. Loudest, and distinguishes "you sent something meaningless" from "you lost a race".

    What this does NOT claim

    I did not find a deployed client that sends If-Match: "", and I do not assert anyone has been bitten. What is measured is that the platform accepts the shape and answers it by dropping the guard. Nor do I assert which of the three options is right — that is exactly why it is filed rather than fixed.

    Related

    #13382 / PR #13569 (where the clause ② review surfaced it) · #6479 (precedent: new rejections are not installed silently) · ADR-0055 / ADR-0058

    Metadata

    Metadata

    Assignees

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [finding] `If-Match: ""` silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded · Issue #13576 · objectstack-ai/objectstack · GitHub
      Skip to content

      [finding] If-Match: "" silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded #13576

      Description

      @zhuangjianguo

      Filed unassigned by the domain:engine lane PM. Recording only — no severity asserted, routing is triage's. Surfaced by the clause ② contract review of PR #13569 (#13382); I verified the mechanism in source independently before filing. This predates that PR and is not caused by it.

      Measured on 70fe54891e (pre-#13569main)

      packages/metadata-protocol/src/protocol.ts:1378:

      functionnormaliseVersionToken(v: unknown): string|null{if(v===null||v===undefined)returnnull;consts=String(v).trim();if(!s)returnnull;// ← emptiness checked HEREif(s.length>=2&&s.startsWith('"')&&s.endsWith('"')){returns.slice(1,-1);// ← …then stripped to '' HERE}returns;}

      and its caller, the guarded-DELETE door at :10037:

      if(!normaliseVersionToken(expectedVersion))return;// ← falsy '' reads as "no token"

      For the token "" — a valid RFC-7232 entity-tag with an empty opaque value:

      1. s = '""' is non-empty, so if (!s) return null does not fire;
      2. the quote-strip returns '';
      3. nothing re-checks emptiness after the strip;
      4. the caller's falsiness test reads that '' as "the client sent no version" and returns early.

      The concurrency check is skipped and the write or delete proceeds unguarded.

      Why this is a defect rather than a quirk

      The whole point of If-Match is to make a write conditional. A client that sends If-Match: "" is asking for a guarded write — it is not declining the guard. The current behaviour grants the opposite of what the header requests, and it does so silently: there is no refusal, no warning, and the response is indistinguishable from a successful guarded write.

      Note the asymmetry: an unparseable or opaque token (v2, rowversion-7) fails toward 409, which is the safe direction for a concurrency primitive. "" fails toward accept. It is the one token shape that opts out of the guard rather than failing it.

      Reachability

      • Header path: REST forwards If-Match into expectedVersion.
      • Body path: expectedVersion is declared z.string().optional(), so '""' passes schema validation, and REST's truthiness check passes the non-empty string '""' through.

      Not reachable from the first-party Console: occSave / InlineEditSaveBar only attach a truthy token they received from a prior read. So the exposure is to third-party and hand-rolled clients, not to the shipped UI.

      Relationship to PR #13569 — read this before acting

      #13569 (the Postgres OCC repair) incidentally closes this, as a side effect of returning a wrapper object instead of a bare string: { token: '', instant: null } is truthy, so the caller stops short-circuiting and the guard runs, yielding a 409.

      ⚠️That PR is being changed to restore the current behaviour, deliberately — because turning this accept into a refusal is a contract decision, that PR is a p1 bug fix, and #6479 established that new rejections are not installed silently. So #13569 will land with "" still disabling the guard, and this card is what carries the question.

      The question for triage / the maintainer: should If-Match: "" be able to disable optimistic concurrency at all? Three shapes, not costed here:

      1. Keep as-is"" means "no guard". Cheapest, and it is today's shipped behaviour; but it means an empty entity-tag silently turns off a concurrency primitive.
      2. Fail closed"" is a token that matches nothing ⇒ 409. Safer direction, and it is what fix(metadata-protocol): compare OCC version tokens as instants, not spellings (#13382) #13569 would have done by accident. It is a new rejection on a shipped API and needs to be announced.
      3. Refuse the shape — reject "" at the schema/ingress as a malformed entity-tag, with a message saying so. Loudest, and distinguishes "you sent something meaningless" from "you lost a race".

      What this does NOT claim

      I did not find a deployed client that sends If-Match: "", and I do not assert anyone has been bitten. What is measured is that the platform accepts the shape and answers it by dropping the guard. Nor do I assert which of the three options is right — that is exactly why it is filed rather than fixed.

      Related

      #13382 / PR #13569 (where the clause ② review surfaced it) · #6479 (precedent: new rejections are not installed silently) · ADR-0055 / ADR-0058

      Metadata

      Metadata

      Assignees

      Labels

      Type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [finding] `If-Match: ""` silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded · Issue #13576 · objectstack-ai/objectstack · GitHub
        Skip to content

        [finding] If-Match: "" silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded #13576

        Description

        @zhuangjianguo

        Filed unassigned by the domain:engine lane PM. Recording only — no severity asserted, routing is triage's. Surfaced by the clause ② contract review of PR #13569 (#13382); I verified the mechanism in source independently before filing. This predates that PR and is not caused by it.

        Measured on 70fe54891e (pre-#13569main)

        packages/metadata-protocol/src/protocol.ts:1378:

        functionnormaliseVersionToken(v: unknown): string|null{if(v===null||v===undefined)returnnull;consts=String(v).trim();if(!s)returnnull;// ← emptiness checked HEREif(s.length>=2&&s.startsWith('"')&&s.endsWith('"')){returns.slice(1,-1);// ← …then stripped to '' HERE}returns;}

        and its caller, the guarded-DELETE door at :10037:

        if(!normaliseVersionToken(expectedVersion))return;// ← falsy '' reads as "no token"

        For the token "" — a valid RFC-7232 entity-tag with an empty opaque value:

        1. s = '""' is non-empty, so if (!s) return null does not fire;
        2. the quote-strip returns '';
        3. nothing re-checks emptiness after the strip;
        4. the caller's falsiness test reads that '' as "the client sent no version" and returns early.

        The concurrency check is skipped and the write or delete proceeds unguarded.

        Why this is a defect rather than a quirk

        The whole point of If-Match is to make a write conditional. A client that sends If-Match: "" is asking for a guarded write — it is not declining the guard. The current behaviour grants the opposite of what the header requests, and it does so silently: there is no refusal, no warning, and the response is indistinguishable from a successful guarded write.

        Note the asymmetry: an unparseable or opaque token (v2, rowversion-7) fails toward 409, which is the safe direction for a concurrency primitive. "" fails toward accept. It is the one token shape that opts out of the guard rather than failing it.

        Reachability

        • Header path: REST forwards If-Match into expectedVersion.
        • Body path: expectedVersion is declared z.string().optional(), so '""' passes schema validation, and REST's truthiness check passes the non-empty string '""' through.

        Not reachable from the first-party Console: occSave / InlineEditSaveBar only attach a truthy token they received from a prior read. So the exposure is to third-party and hand-rolled clients, not to the shipped UI.

        Relationship to PR #13569 — read this before acting

        #13569 (the Postgres OCC repair) incidentally closes this, as a side effect of returning a wrapper object instead of a bare string: { token: '', instant: null } is truthy, so the caller stops short-circuiting and the guard runs, yielding a 409.

        ⚠️That PR is being changed to restore the current behaviour, deliberately — because turning this accept into a refusal is a contract decision, that PR is a p1 bug fix, and #6479 established that new rejections are not installed silently. So #13569 will land with "" still disabling the guard, and this card is what carries the question.

        The question for triage / the maintainer: should If-Match: "" be able to disable optimistic concurrency at all? Three shapes, not costed here:

        1. Keep as-is"" means "no guard". Cheapest, and it is today's shipped behaviour; but it means an empty entity-tag silently turns off a concurrency primitive.
        2. Fail closed"" is a token that matches nothing ⇒ 409. Safer direction, and it is what fix(metadata-protocol): compare OCC version tokens as instants, not spellings (#13382) #13569 would have done by accident. It is a new rejection on a shipped API and needs to be announced.
        3. Refuse the shape — reject "" at the schema/ingress as a malformed entity-tag, with a message saying so. Loudest, and distinguishes "you sent something meaningless" from "you lost a race".

        What this does NOT claim

        I did not find a deployed client that sends If-Match: "", and I do not assert anyone has been bitten. What is measured is that the platform accepts the shape and answers it by dropping the guard. Nor do I assert which of the three options is right — that is exactly why it is filed rather than fixed.

        Related

        #13382 / PR #13569 (where the clause ② review surfaced it) · #6479 (precedent: new rejections are not installed silently) · ADR-0055 / ADR-0058

        Metadata

        Metadata

        Assignees

        Labels

        Type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [finding] `If-Match: ""` silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded · Issue #13576 · objectstack-ai/objectstack · GitHub
          Skip to content

          [finding] If-Match: "" silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded #13576

          Description

          @zhuangjianguo

          Filed unassigned by the domain:engine lane PM. Recording only — no severity asserted, routing is triage's. Surfaced by the clause ② contract review of PR #13569 (#13382); I verified the mechanism in source independently before filing. This predates that PR and is not caused by it.

          Measured on 70fe54891e (pre-#13569main)

          packages/metadata-protocol/src/protocol.ts:1378:

          functionnormaliseVersionToken(v: unknown): string|null{if(v===null||v===undefined)returnnull;consts=String(v).trim();if(!s)returnnull;// ← emptiness checked HEREif(s.length>=2&&s.startsWith('"')&&s.endsWith('"')){returns.slice(1,-1);// ← …then stripped to '' HERE}returns;}

          and its caller, the guarded-DELETE door at :10037:

          if(!normaliseVersionToken(expectedVersion))return;// ← falsy '' reads as "no token"

          For the token "" — a valid RFC-7232 entity-tag with an empty opaque value:

          1. s = '""' is non-empty, so if (!s) return null does not fire;
          2. the quote-strip returns '';
          3. nothing re-checks emptiness after the strip;
          4. the caller's falsiness test reads that '' as "the client sent no version" and returns early.

          The concurrency check is skipped and the write or delete proceeds unguarded.

          Why this is a defect rather than a quirk

          The whole point of If-Match is to make a write conditional. A client that sends If-Match: "" is asking for a guarded write — it is not declining the guard. The current behaviour grants the opposite of what the header requests, and it does so silently: there is no refusal, no warning, and the response is indistinguishable from a successful guarded write.

          Note the asymmetry: an unparseable or opaque token (v2, rowversion-7) fails toward 409, which is the safe direction for a concurrency primitive. "" fails toward accept. It is the one token shape that opts out of the guard rather than failing it.

          Reachability

          • Header path: REST forwards If-Match into expectedVersion.
          • Body path: expectedVersion is declared z.string().optional(), so '""' passes schema validation, and REST's truthiness check passes the non-empty string '""' through.

          Not reachable from the first-party Console: occSave / InlineEditSaveBar only attach a truthy token they received from a prior read. So the exposure is to third-party and hand-rolled clients, not to the shipped UI.

          Relationship to PR #13569 — read this before acting

          #13569 (the Postgres OCC repair) incidentally closes this, as a side effect of returning a wrapper object instead of a bare string: { token: '', instant: null } is truthy, so the caller stops short-circuiting and the guard runs, yielding a 409.

          ⚠️That PR is being changed to restore the current behaviour, deliberately — because turning this accept into a refusal is a contract decision, that PR is a p1 bug fix, and #6479 established that new rejections are not installed silently. So #13569 will land with "" still disabling the guard, and this card is what carries the question.

          The question for triage / the maintainer: should If-Match: "" be able to disable optimistic concurrency at all? Three shapes, not costed here:

          1. Keep as-is"" means "no guard". Cheapest, and it is today's shipped behaviour; but it means an empty entity-tag silently turns off a concurrency primitive.
          2. Fail closed"" is a token that matches nothing ⇒ 409. Safer direction, and it is what fix(metadata-protocol): compare OCC version tokens as instants, not spellings (#13382) #13569 would have done by accident. It is a new rejection on a shipped API and needs to be announced.
          3. Refuse the shape — reject "" at the schema/ingress as a malformed entity-tag, with a message saying so. Loudest, and distinguishes "you sent something meaningless" from "you lost a race".

          What this does NOT claim

          I did not find a deployed client that sends If-Match: "", and I do not assert anyone has been bitten. What is measured is that the platform accepts the shape and answers it by dropping the guard. Nor do I assert which of the three options is right — that is exactly why it is filed rather than fixed.

          Related

          #13382 / PR #13569 (where the clause ② review surfaced it) · #6479 (precedent: new rejections are not installed silently) · ADR-0055 / ADR-0058

          Metadata

          Metadata

          Assignees

          Labels

          Type

          Projects

          No projects

            Milestone

            No milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [finding] `If-Match: ""` silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded · Issue #13576 · objectstack-ai/objectstack · GitHub
            Skip to content

            [finding] If-Match: "" silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded #13576

            Description

            @zhuangjianguo

            Filed unassigned by the domain:engine lane PM. Recording only — no severity asserted, routing is triage's. Surfaced by the clause ② contract review of PR #13569 (#13382); I verified the mechanism in source independently before filing. This predates that PR and is not caused by it.

            Measured on 70fe54891e (pre-#13569main)

            packages/metadata-protocol/src/protocol.ts:1378:

            functionnormaliseVersionToken(v: unknown): string|null{if(v===null||v===undefined)returnnull;consts=String(v).trim();if(!s)returnnull;// ← emptiness checked HEREif(s.length>=2&&s.startsWith('"')&&s.endsWith('"')){returns.slice(1,-1);// ← …then stripped to '' HERE}returns;}

            and its caller, the guarded-DELETE door at :10037:

            if(!normaliseVersionToken(expectedVersion))return;// ← falsy '' reads as "no token"

            For the token "" — a valid RFC-7232 entity-tag with an empty opaque value:

            1. s = '""' is non-empty, so if (!s) return null does not fire;
            2. the quote-strip returns '';
            3. nothing re-checks emptiness after the strip;
            4. the caller's falsiness test reads that '' as "the client sent no version" and returns early.

            The concurrency check is skipped and the write or delete proceeds unguarded.

            Why this is a defect rather than a quirk

            The whole point of If-Match is to make a write conditional. A client that sends If-Match: "" is asking for a guarded write — it is not declining the guard. The current behaviour grants the opposite of what the header requests, and it does so silently: there is no refusal, no warning, and the response is indistinguishable from a successful guarded write.

            Note the asymmetry: an unparseable or opaque token (v2, rowversion-7) fails toward 409, which is the safe direction for a concurrency primitive. "" fails toward accept. It is the one token shape that opts out of the guard rather than failing it.

            Reachability

            • Header path: REST forwards If-Match into expectedVersion.
            • Body path: expectedVersion is declared z.string().optional(), so '""' passes schema validation, and REST's truthiness check passes the non-empty string '""' through.

            Not reachable from the first-party Console: occSave / InlineEditSaveBar only attach a truthy token they received from a prior read. So the exposure is to third-party and hand-rolled clients, not to the shipped UI.

            Relationship to PR #13569 — read this before acting

            #13569 (the Postgres OCC repair) incidentally closes this, as a side effect of returning a wrapper object instead of a bare string: { token: '', instant: null } is truthy, so the caller stops short-circuiting and the guard runs, yielding a 409.

            ⚠️That PR is being changed to restore the current behaviour, deliberately — because turning this accept into a refusal is a contract decision, that PR is a p1 bug fix, and #6479 established that new rejections are not installed silently. So #13569 will land with "" still disabling the guard, and this card is what carries the question.

            The question for triage / the maintainer: should If-Match: "" be able to disable optimistic concurrency at all? Three shapes, not costed here:

            1. Keep as-is"" means "no guard". Cheapest, and it is today's shipped behaviour; but it means an empty entity-tag silently turns off a concurrency primitive.
            2. Fail closed"" is a token that matches nothing ⇒ 409. Safer direction, and it is what fix(metadata-protocol): compare OCC version tokens as instants, not spellings (#13382) #13569 would have done by accident. It is a new rejection on a shipped API and needs to be announced.
            3. Refuse the shape — reject "" at the schema/ingress as a malformed entity-tag, with a message saying so. Loudest, and distinguishes "you sent something meaningless" from "you lost a race".

            What this does NOT claim

            I did not find a deployed client that sends If-Match: "", and I do not assert anyone has been bitten. What is measured is that the platform accepts the shape and answers it by dropping the guard. Nor do I assert which of the three options is right — that is exactly why it is filed rather than fixed.

            Related

            #13382 / PR #13569 (where the clause ② review surfaced it) · #6479 (precedent: new rejections are not installed silently) · ADR-0055 / ADR-0058

            Metadata

            Metadata

            Assignees

            Labels

            Type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [finding] `If-Match: ""` silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded · Issue #13576 · objectstack-ai/objectstack · GitHub
              Skip to content

              [finding] If-Match: "" silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded #13576

              Description

              @zhuangjianguo

              Filed unassigned by the domain:engine lane PM. Recording only — no severity asserted, routing is triage's. Surfaced by the clause ② contract review of PR #13569 (#13382); I verified the mechanism in source independently before filing. This predates that PR and is not caused by it.

              Measured on 70fe54891e (pre-#13569main)

              packages/metadata-protocol/src/protocol.ts:1378:

              functionnormaliseVersionToken(v: unknown): string|null{if(v===null||v===undefined)returnnull;consts=String(v).trim();if(!s)returnnull;// ← emptiness checked HEREif(s.length>=2&&s.startsWith('"')&&s.endsWith('"')){returns.slice(1,-1);// ← …then stripped to '' HERE}returns;}

              and its caller, the guarded-DELETE door at :10037:

              if(!normaliseVersionToken(expectedVersion))return;// ← falsy '' reads as "no token"

              For the token "" — a valid RFC-7232 entity-tag with an empty opaque value:

              1. s = '""' is non-empty, so if (!s) return null does not fire;
              2. the quote-strip returns '';
              3. nothing re-checks emptiness after the strip;
              4. the caller's falsiness test reads that '' as "the client sent no version" and returns early.

              The concurrency check is skipped and the write or delete proceeds unguarded.

              Why this is a defect rather than a quirk

              The whole point of If-Match is to make a write conditional. A client that sends If-Match: "" is asking for a guarded write — it is not declining the guard. The current behaviour grants the opposite of what the header requests, and it does so silently: there is no refusal, no warning, and the response is indistinguishable from a successful guarded write.

              Note the asymmetry: an unparseable or opaque token (v2, rowversion-7) fails toward 409, which is the safe direction for a concurrency primitive. "" fails toward accept. It is the one token shape that opts out of the guard rather than failing it.

              Reachability

              • Header path: REST forwards If-Match into expectedVersion.
              • Body path: expectedVersion is declared z.string().optional(), so '""' passes schema validation, and REST's truthiness check passes the non-empty string '""' through.

              Not reachable from the first-party Console: occSave / InlineEditSaveBar only attach a truthy token they received from a prior read. So the exposure is to third-party and hand-rolled clients, not to the shipped UI.

              Relationship to PR #13569 — read this before acting

              #13569 (the Postgres OCC repair) incidentally closes this, as a side effect of returning a wrapper object instead of a bare string: { token: '', instant: null } is truthy, so the caller stops short-circuiting and the guard runs, yielding a 409.

              ⚠️That PR is being changed to restore the current behaviour, deliberately — because turning this accept into a refusal is a contract decision, that PR is a p1 bug fix, and #6479 established that new rejections are not installed silently. So #13569 will land with "" still disabling the guard, and this card is what carries the question.

              The question for triage / the maintainer: should If-Match: "" be able to disable optimistic concurrency at all? Three shapes, not costed here:

              1. Keep as-is"" means "no guard". Cheapest, and it is today's shipped behaviour; but it means an empty entity-tag silently turns off a concurrency primitive.
              2. Fail closed"" is a token that matches nothing ⇒ 409. Safer direction, and it is what fix(metadata-protocol): compare OCC version tokens as instants, not spellings (#13382) #13569 would have done by accident. It is a new rejection on a shipped API and needs to be announced.
              3. Refuse the shape — reject "" at the schema/ingress as a malformed entity-tag, with a message saying so. Loudest, and distinguishes "you sent something meaningless" from "you lost a race".

              What this does NOT claim

              I did not find a deployed client that sends If-Match: "", and I do not assert anyone has been bitten. What is measured is that the platform accepts the shape and answers it by dropping the guard. Nor do I assert which of the three options is right — that is exactly why it is filed rather than fixed.

              Related

              #13382 / PR #13569 (where the clause ② review surfaced it) · #6479 (precedent: new rejections are not installed silently) · ADR-0055 / ADR-0058

              Metadata

              Metadata

              Assignees

              Labels

              Type

              Projects

              No projects

                Milestone

                No milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [finding] `If-Match: ""` silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded · Issue #13576 · objectstack-ai/objectstack · GitHub
                Skip to content

                [finding] If-Match: "" silently DISABLES optimistic concurrency — a quoted-empty entity-tag is read as "no token" and the guarded write proceeds unguarded #13576

                Description

                @zhuangjianguo

                Filed unassigned by the domain:engine lane PM. Recording only — no severity asserted, routing is triage's. Surfaced by the clause ② contract review of PR #13569 (#13382); I verified the mechanism in source independently before filing. This predates that PR and is not caused by it.

                Measured on 70fe54891e (pre-#13569main)

                packages/metadata-protocol/src/protocol.ts:1378:

                functionnormaliseVersionToken(v: unknown): string|null{if(v===null||v===undefined)returnnull;consts=String(v).trim();if(!s)returnnull;// ← emptiness checked HEREif(s.length>=2&&s.startsWith('"')&&s.endsWith('"')){returns.slice(1,-1);// ← …then stripped to '' HERE}returns;}

                and its caller, the guarded-DELETE door at :10037:

                if(!normaliseVersionToken(expectedVersion))return;// ← falsy '' reads as "no token"

                For the token "" — a valid RFC-7232 entity-tag with an empty opaque value:

                1. s = '""' is non-empty, so if (!s) return null does not fire;
                2. the quote-strip returns '';
                3. nothing re-checks emptiness after the strip;
                4. the caller's falsiness test reads that '' as "the client sent no version" and returns early.

                The concurrency check is skipped and the write or delete proceeds unguarded.

                Why this is a defect rather than a quirk

                The whole point of If-Match is to make a write conditional. A client that sends If-Match: "" is asking for a guarded write — it is not declining the guard. The current behaviour grants the opposite of what the header requests, and it does so silently: there is no refusal, no warning, and the response is indistinguishable from a successful guarded write.

                Note the asymmetry: an unparseable or opaque token (v2, rowversion-7) fails toward 409, which is the safe direction for a concurrency primitive. "" fails toward accept. It is the one token shape that opts out of the guard rather than failing it.

                Reachability

                • Header path: REST forwards If-Match into expectedVersion.
                • Body path: expectedVersion is declared z.string().optional(), so '""' passes schema validation, and REST's truthiness check passes the non-empty string '""' through.

                Not reachable from the first-party Console: occSave / InlineEditSaveBar only attach a truthy token they received from a prior read. So the exposure is to third-party and hand-rolled clients, not to the shipped UI.

                Relationship to PR #13569 — read this before acting

                #13569 (the Postgres OCC repair) incidentally closes this, as a side effect of returning a wrapper object instead of a bare string: { token: '', instant: null } is truthy, so the caller stops short-circuiting and the guard runs, yielding a 409.

                ⚠️That PR is being changed to restore the current behaviour, deliberately — because turning this accept into a refusal is a contract decision, that PR is a p1 bug fix, and #6479 established that new rejections are not installed silently. So #13569 will land with "" still disabling the guard, and this card is what carries the question.

                The question for triage / the maintainer: should If-Match: "" be able to disable optimistic concurrency at all? Three shapes, not costed here:

                1. Keep as-is"" means "no guard". Cheapest, and it is today's shipped behaviour; but it means an empty entity-tag silently turns off a concurrency primitive.
                2. Fail closed"" is a token that matches nothing ⇒ 409. Safer direction, and it is what fix(metadata-protocol): compare OCC version tokens as instants, not spellings (#13382) #13569 would have done by accident. It is a new rejection on a shipped API and needs to be announced.
                3. Refuse the shape — reject "" at the schema/ingress as a malformed entity-tag, with a message saying so. Loudest, and distinguishes "you sent something meaningless" from "you lost a race".

                What this does NOT claim

                I did not find a deployed client that sends If-Match: "", and I do not assert anyone has been bitten. What is measured is that the platform accepts the shape and answers it by dropping the guard. Nor do I assert which of the three options is right — that is exactly why it is filed rather than fixed.

                Related

                #13382 / PR #13569 (where the clause ② review surfaced it) · #6479 (precedent: new rejections are not installed silently) · ADR-0055 / ADR-0058

                Metadata

                Metadata

                Assignees

                Labels

                Type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions