limit 62: an equivalent path spelling walks around the self-mod gate - #43

Open
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings
Open

limit 62: an equivalent path spelling walks around the self-mod gate#43
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings

Conversation

@githubscum

Copy link
Copy Markdown
Owner

WO (lotor-lane run 10): probe the self-mod matcher for spelling escapes, the way runs 6, 8 and 9 found their defects — by executing it, not reading it.

The finding

normalizePath() converts backslashes, lowercases, strips a trailing slash. That is all. The fragment list is then matched as a plain substring — cmdNorm.includes(frag) for commands, pathContainsFragment() for Edit. A doubled separator or a . segment defeats both and changes nothing about which file opens.

Fourteen spellings through the shipped matcher: five controls gated, seven misses.

SpellingGatedOpens the protected file
<repo>/src/policy/index.jsyesyes
<repo>/src//policy/index.jsnoyes
<repo>/src/./policy/index.jsnoyes
<repo>/src/./chain/index.jsnoyes
<repo>/bin//charter.jsnoyes
<repo>/./bin/./charter.jsnoyes

The right column is measured, not argued: the test writes a canary file and reads it back through all three spellings.

The Edit half is the one that matters.src/policy, src/chain, src/store, src/grant and every script in bin/ stay at gate in every mode including loose. This walks around that with one extra character, through the tool an agent actually uses to change code.

Not a new class. Limit 22 was tilde/$HOME. LOTOR-C2 was brace expansion. This is the third spelling escape in the same matcher, and all three were found by executing it rather than reading it.

What this PR ships

  • KNOWN-LIMITS 62 — the confession, with the measured table, the unverified case marked unverified (src/policy./index.js is ungated; Windows strips trailing dots, but existsSync refused that spelling here, so whether a real write lands is not proven), and a caution for whoever writes the fix: normalizePath is applied to whole command strings, so collapsing /+ globally also rewrites https://x and needs its blast radius checked across every caller.
  • test/policy-selfmod-separator-spellings.test.js — a tripwire, 13 assertions. It asserts the current, defective behaviour, with the plain spellings beside it as controls so the block cannot pass by the matcher dying. When the matcher is fixed, this file fails. The repair is to invert the assertions and amend entry 62 in the same change, never to delete the block.

Tested

npm test on this branch: 954 pass / 0 fail (941 on main plus the 13 new). The new file alone: 13/13.

Not fixed here, and why

The fix is normalizePath in src/policy — non-delegable core. It was attempted first and the gate refused it unsigned. Correct behaviour, and worth stating plainly: the bypass proved in this PR was not used to land the fix for the bypass. Queued for a signing sitting alongside limits 35 and 36.

What a reviewer should doubt

  1. A green suite now contains nine passing tests named "is FREE". Read cold that can look like the project blessing the gap. Same cost run 9 accepted for the limit-44 tripwire, and a reviewer may legitimately want these red instead. That call is not this lane's to make alone.
  2. The tripwire is not proven to trip. Demonstrating it needs the core edit the gate blocks. The claim is structural, not demonstrated. Do not describe it as proven fail-first.
  3. The command-side misses may be judged out of scope. Limit 34 concedes a shell command cannot be resolved, so the fragment list is arguably as correct as it gets there. The Edit-side misses have no such defence.
  4. REPO in the test is derived from import.meta.url with a Windows drive-letter fixup. It only builds path strings for the matcher and never touches the repo, but it is the least portable line in the file.
  5. Is one clause enough? The entry proposes collapsing /+ and dropping ./ segments. It does not claim that exhausts the spelling space: .. traversal is gated today only when the traversed-through directory happens to be protected too.

🤖 Generated with Claude Code

Found by running fourteen spellings through the shipped matcher, not by
reading it. normalizePath folds backslashes and case and stops there, so a
doubled separator or a dot segment contains no protected fragment
contiguously and is not gated, while opening exactly the same file.
Seven misses, five controls gated. The Edit half is the half that matters:
Edit carries a file_path, and limit 34 already says a path can be proven
contained. This is what that deferral costs.
Ships the confession and a tripwire that asserts the current, defective
behaviour with the plain spellings beside it as controls. When the matcher is
fixed the block fails; the repair is to invert it and amend entry 62, never to
delete it.
The fix is src/policy and non-delegable core. It was attempted and the gate
refused it unsigned, correctly. The bypass proved here was not used to land
the fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@githubscum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n 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;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

limit 62: an equivalent path spelling walks around the self-mod gate - #43

Open
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings
Open

limit 62: an equivalent path spelling walks around the self-mod gate#43
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings

Conversation

@githubscum

Copy link
Copy Markdown
Owner

WO (lotor-lane run 10): probe the self-mod matcher for spelling escapes, the way runs 6, 8 and 9 found their defects — by executing it, not reading it.

The finding

normalizePath() converts backslashes, lowercases, strips a trailing slash. That is all. The fragment list is then matched as a plain substring — cmdNorm.includes(frag) for commands, pathContainsFragment() for Edit. A doubled separator or a . segment defeats both and changes nothing about which file opens.

Fourteen spellings through the shipped matcher: five controls gated, seven misses.

SpellingGatedOpens the protected file
<repo>/src/policy/index.jsyesyes
<repo>/src//policy/index.jsnoyes
<repo>/src/./policy/index.jsnoyes
<repo>/src/./chain/index.jsnoyes
<repo>/bin//charter.jsnoyes
<repo>/./bin/./charter.jsnoyes

The right column is measured, not argued: the test writes a canary file and reads it back through all three spellings.

The Edit half is the one that matters.src/policy, src/chain, src/store, src/grant and every script in bin/ stay at gate in every mode including loose. This walks around that with one extra character, through the tool an agent actually uses to change code.

Not a new class. Limit 22 was tilde/$HOME. LOTOR-C2 was brace expansion. This is the third spelling escape in the same matcher, and all three were found by executing it rather than reading it.

What this PR ships

  • KNOWN-LIMITS 62 — the confession, with the measured table, the unverified case marked unverified (src/policy./index.js is ungated; Windows strips trailing dots, but existsSync refused that spelling here, so whether a real write lands is not proven), and a caution for whoever writes the fix: normalizePath is applied to whole command strings, so collapsing /+ globally also rewrites https://x and needs its blast radius checked across every caller.
  • test/policy-selfmod-separator-spellings.test.js — a tripwire, 13 assertions. It asserts the current, defective behaviour, with the plain spellings beside it as controls so the block cannot pass by the matcher dying. When the matcher is fixed, this file fails. The repair is to invert the assertions and amend entry 62 in the same change, never to delete the block.

Tested

npm test on this branch: 954 pass / 0 fail (941 on main plus the 13 new). The new file alone: 13/13.

Not fixed here, and why

The fix is normalizePath in src/policy — non-delegable core. It was attempted first and the gate refused it unsigned. Correct behaviour, and worth stating plainly: the bypass proved in this PR was not used to land the fix for the bypass. Queued for a signing sitting alongside limits 35 and 36.

What a reviewer should doubt

  1. A green suite now contains nine passing tests named "is FREE". Read cold that can look like the project blessing the gap. Same cost run 9 accepted for the limit-44 tripwire, and a reviewer may legitimately want these red instead. That call is not this lane's to make alone.
  2. The tripwire is not proven to trip. Demonstrating it needs the core edit the gate blocks. The claim is structural, not demonstrated. Do not describe it as proven fail-first.
  3. The command-side misses may be judged out of scope. Limit 34 concedes a shell command cannot be resolved, so the fragment list is arguably as correct as it gets there. The Edit-side misses have no such defence.
  4. REPO in the test is derived from import.meta.url with a Windows drive-letter fixup. It only builds path strings for the matcher and never touches the repo, but it is the least portable line in the file.
  5. Is one clause enough? The entry proposes collapsing /+ and dropping ./ segments. It does not claim that exhausts the spelling space: .. traversal is gated today only when the traversed-through directory happens to be protected too.

🤖 Generated with Claude Code

Found by running fourteen spellings through the shipped matcher, not by
reading it. normalizePath folds backslashes and case and stops there, so a
doubled separator or a dot segment contains no protected fragment
contiguously and is not gated, while opening exactly the same file.
Seven misses, five controls gated. The Edit half is the half that matters:
Edit carries a file_path, and limit 34 already says a path can be proven
contained. This is what that deferral costs.
Ships the confession and a tripwire that asserts the current, defective
behaviour with the plain spellings beside it as controls. When the matcher is
fixed the block fails; the repair is to invert it and amend entry 62, never to
delete it.
The fix is src/policy and non-delegable core. It was attempted and the gate
refused it unsigned, correctly. The bypass proved here was not used to land
the fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@githubscum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

limit 62: an equivalent path spelling walks around the self-mod gate - #43

Open
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings
Open

limit 62: an equivalent path spelling walks around the self-mod gate#43
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings

Conversation

@githubscum

Copy link
Copy Markdown
Owner

WO (lotor-lane run 10): probe the self-mod matcher for spelling escapes, the way runs 6, 8 and 9 found their defects — by executing it, not reading it.

The finding

normalizePath() converts backslashes, lowercases, strips a trailing slash. That is all. The fragment list is then matched as a plain substring — cmdNorm.includes(frag) for commands, pathContainsFragment() for Edit. A doubled separator or a . segment defeats both and changes nothing about which file opens.

Fourteen spellings through the shipped matcher: five controls gated, seven misses.

SpellingGatedOpens the protected file
<repo>/src/policy/index.jsyesyes
<repo>/src//policy/index.jsnoyes
<repo>/src/./policy/index.jsnoyes
<repo>/src/./chain/index.jsnoyes
<repo>/bin//charter.jsnoyes
<repo>/./bin/./charter.jsnoyes

The right column is measured, not argued: the test writes a canary file and reads it back through all three spellings.

The Edit half is the one that matters.src/policy, src/chain, src/store, src/grant and every script in bin/ stay at gate in every mode including loose. This walks around that with one extra character, through the tool an agent actually uses to change code.

Not a new class. Limit 22 was tilde/$HOME. LOTOR-C2 was brace expansion. This is the third spelling escape in the same matcher, and all three were found by executing it rather than reading it.

What this PR ships

  • KNOWN-LIMITS 62 — the confession, with the measured table, the unverified case marked unverified (src/policy./index.js is ungated; Windows strips trailing dots, but existsSync refused that spelling here, so whether a real write lands is not proven), and a caution for whoever writes the fix: normalizePath is applied to whole command strings, so collapsing /+ globally also rewrites https://x and needs its blast radius checked across every caller.
  • test/policy-selfmod-separator-spellings.test.js — a tripwire, 13 assertions. It asserts the current, defective behaviour, with the plain spellings beside it as controls so the block cannot pass by the matcher dying. When the matcher is fixed, this file fails. The repair is to invert the assertions and amend entry 62 in the same change, never to delete the block.

Tested

npm test on this branch: 954 pass / 0 fail (941 on main plus the 13 new). The new file alone: 13/13.

Not fixed here, and why

The fix is normalizePath in src/policy — non-delegable core. It was attempted first and the gate refused it unsigned. Correct behaviour, and worth stating plainly: the bypass proved in this PR was not used to land the fix for the bypass. Queued for a signing sitting alongside limits 35 and 36.

What a reviewer should doubt

  1. A green suite now contains nine passing tests named "is FREE". Read cold that can look like the project blessing the gap. Same cost run 9 accepted for the limit-44 tripwire, and a reviewer may legitimately want these red instead. That call is not this lane's to make alone.
  2. The tripwire is not proven to trip. Demonstrating it needs the core edit the gate blocks. The claim is structural, not demonstrated. Do not describe it as proven fail-first.
  3. The command-side misses may be judged out of scope. Limit 34 concedes a shell command cannot be resolved, so the fragment list is arguably as correct as it gets there. The Edit-side misses have no such defence.
  4. REPO in the test is derived from import.meta.url with a Windows drive-letter fixup. It only builds path strings for the matcher and never touches the repo, but it is the least portable line in the file.
  5. Is one clause enough? The entry proposes collapsing /+ and dropping ./ segments. It does not claim that exhausts the spelling space: .. traversal is gated today only when the traversed-through directory happens to be protected too.

🤖 Generated with Claude Code

Found by running fourteen spellings through the shipped matcher, not by
reading it. normalizePath folds backslashes and case and stops there, so a
doubled separator or a dot segment contains no protected fragment
contiguously and is not gated, while opening exactly the same file.
Seven misses, five controls gated. The Edit half is the half that matters:
Edit carries a file_path, and limit 34 already says a path can be proven
contained. This is what that deferral costs.
Ships the confession and a tripwire that asserts the current, defective
behaviour with the plain spellings beside it as controls. When the matcher is
fixed the block fails; the repair is to invert it and amend entry 62, never to
delete it.
The fix is src/policy and non-delegable core. It was attempted and the gate
refused it unsigned, correctly. The bypass proved here was not used to land
the fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

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

limit 62: an equivalent path spelling walks around the self-mod gate - #43

Open
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings
Open

limit 62: an equivalent path spelling walks around the self-mod gate#43
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings

Conversation

@githubscum

Copy link
Copy Markdown
Owner

WO (lotor-lane run 10): probe the self-mod matcher for spelling escapes, the way runs 6, 8 and 9 found their defects — by executing it, not reading it.

The finding

normalizePath() converts backslashes, lowercases, strips a trailing slash. That is all. The fragment list is then matched as a plain substring — cmdNorm.includes(frag) for commands, pathContainsFragment() for Edit. A doubled separator or a . segment defeats both and changes nothing about which file opens.

Fourteen spellings through the shipped matcher: five controls gated, seven misses.

SpellingGatedOpens the protected file
<repo>/src/policy/index.jsyesyes
<repo>/src//policy/index.jsnoyes
<repo>/src/./policy/index.jsnoyes
<repo>/src/./chain/index.jsnoyes
<repo>/bin//charter.jsnoyes
<repo>/./bin/./charter.jsnoyes

The right column is measured, not argued: the test writes a canary file and reads it back through all three spellings.

The Edit half is the one that matters.src/policy, src/chain, src/store, src/grant and every script in bin/ stay at gate in every mode including loose. This walks around that with one extra character, through the tool an agent actually uses to change code.

Not a new class. Limit 22 was tilde/$HOME. LOTOR-C2 was brace expansion. This is the third spelling escape in the same matcher, and all three were found by executing it rather than reading it.

What this PR ships

  • KNOWN-LIMITS 62 — the confession, with the measured table, the unverified case marked unverified (src/policy./index.js is ungated; Windows strips trailing dots, but existsSync refused that spelling here, so whether a real write lands is not proven), and a caution for whoever writes the fix: normalizePath is applied to whole command strings, so collapsing /+ globally also rewrites https://x and needs its blast radius checked across every caller.
  • test/policy-selfmod-separator-spellings.test.js — a tripwire, 13 assertions. It asserts the current, defective behaviour, with the plain spellings beside it as controls so the block cannot pass by the matcher dying. When the matcher is fixed, this file fails. The repair is to invert the assertions and amend entry 62 in the same change, never to delete the block.

Tested

npm test on this branch: 954 pass / 0 fail (941 on main plus the 13 new). The new file alone: 13/13.

Not fixed here, and why

The fix is normalizePath in src/policy — non-delegable core. It was attempted first and the gate refused it unsigned. Correct behaviour, and worth stating plainly: the bypass proved in this PR was not used to land the fix for the bypass. Queued for a signing sitting alongside limits 35 and 36.

What a reviewer should doubt

  1. A green suite now contains nine passing tests named "is FREE". Read cold that can look like the project blessing the gap. Same cost run 9 accepted for the limit-44 tripwire, and a reviewer may legitimately want these red instead. That call is not this lane's to make alone.
  2. The tripwire is not proven to trip. Demonstrating it needs the core edit the gate blocks. The claim is structural, not demonstrated. Do not describe it as proven fail-first.
  3. The command-side misses may be judged out of scope. Limit 34 concedes a shell command cannot be resolved, so the fragment list is arguably as correct as it gets there. The Edit-side misses have no such defence.
  4. REPO in the test is derived from import.meta.url with a Windows drive-letter fixup. It only builds path strings for the matcher and never touches the repo, but it is the least portable line in the file.
  5. Is one clause enough? The entry proposes collapsing /+ and dropping ./ segments. It does not claim that exhausts the spelling space: .. traversal is gated today only when the traversed-through directory happens to be protected too.

🤖 Generated with Claude Code

Found by running fourteen spellings through the shipped matcher, not by
reading it. normalizePath folds backslashes and case and stops there, so a
doubled separator or a dot segment contains no protected fragment
contiguously and is not gated, while opening exactly the same file.
Seven misses, five controls gated. The Edit half is the half that matters:
Edit carries a file_path, and limit 34 already says a path can be proven
contained. This is what that deferral costs.
Ships the confession and a tripwire that asserts the current, defective
behaviour with the plain spellings beside it as controls. When the matcher is
fixed the block fails; the repair is to invert it and amend entry 62, never to
delete it.
The fix is src/policy and non-delegable core. It was attempted and the gate
refused it unsigned, correctly. The bypass proved here was not used to land
the fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@githubscum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

limit 62: an equivalent path spelling walks around the self-mod gate - #43

Open
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings
Open

limit 62: an equivalent path spelling walks around the self-mod gate#43
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings

Conversation

@githubscum

Copy link
Copy Markdown
Owner

WO (lotor-lane run 10): probe the self-mod matcher for spelling escapes, the way runs 6, 8 and 9 found their defects — by executing it, not reading it.

The finding

normalizePath() converts backslashes, lowercases, strips a trailing slash. That is all. The fragment list is then matched as a plain substring — cmdNorm.includes(frag) for commands, pathContainsFragment() for Edit. A doubled separator or a . segment defeats both and changes nothing about which file opens.

Fourteen spellings through the shipped matcher: five controls gated, seven misses.

SpellingGatedOpens the protected file
<repo>/src/policy/index.jsyesyes
<repo>/src//policy/index.jsnoyes
<repo>/src/./policy/index.jsnoyes
<repo>/src/./chain/index.jsnoyes
<repo>/bin//charter.jsnoyes
<repo>/./bin/./charter.jsnoyes

The right column is measured, not argued: the test writes a canary file and reads it back through all three spellings.

The Edit half is the one that matters.src/policy, src/chain, src/store, src/grant and every script in bin/ stay at gate in every mode including loose. This walks around that with one extra character, through the tool an agent actually uses to change code.

Not a new class. Limit 22 was tilde/$HOME. LOTOR-C2 was brace expansion. This is the third spelling escape in the same matcher, and all three were found by executing it rather than reading it.

What this PR ships

  • KNOWN-LIMITS 62 — the confession, with the measured table, the unverified case marked unverified (src/policy./index.js is ungated; Windows strips trailing dots, but existsSync refused that spelling here, so whether a real write lands is not proven), and a caution for whoever writes the fix: normalizePath is applied to whole command strings, so collapsing /+ globally also rewrites https://x and needs its blast radius checked across every caller.
  • test/policy-selfmod-separator-spellings.test.js — a tripwire, 13 assertions. It asserts the current, defective behaviour, with the plain spellings beside it as controls so the block cannot pass by the matcher dying. When the matcher is fixed, this file fails. The repair is to invert the assertions and amend entry 62 in the same change, never to delete the block.

Tested

npm test on this branch: 954 pass / 0 fail (941 on main plus the 13 new). The new file alone: 13/13.

Not fixed here, and why

The fix is normalizePath in src/policy — non-delegable core. It was attempted first and the gate refused it unsigned. Correct behaviour, and worth stating plainly: the bypass proved in this PR was not used to land the fix for the bypass. Queued for a signing sitting alongside limits 35 and 36.

What a reviewer should doubt

  1. A green suite now contains nine passing tests named "is FREE". Read cold that can look like the project blessing the gap. Same cost run 9 accepted for the limit-44 tripwire, and a reviewer may legitimately want these red instead. That call is not this lane's to make alone.
  2. The tripwire is not proven to trip. Demonstrating it needs the core edit the gate blocks. The claim is structural, not demonstrated. Do not describe it as proven fail-first.
  3. The command-side misses may be judged out of scope. Limit 34 concedes a shell command cannot be resolved, so the fragment list is arguably as correct as it gets there. The Edit-side misses have no such defence.
  4. REPO in the test is derived from import.meta.url with a Windows drive-letter fixup. It only builds path strings for the matcher and never touches the repo, but it is the least portable line in the file.
  5. Is one clause enough? The entry proposes collapsing /+ and dropping ./ segments. It does not claim that exhausts the spelling space: .. traversal is gated today only when the traversed-through directory happens to be protected too.

🤖 Generated with Claude Code

Found by running fourteen spellings through the shipped matcher, not by
reading it. normalizePath folds backslashes and case and stops there, so a
doubled separator or a dot segment contains no protected fragment
contiguously and is not gated, while opening exactly the same file.
Seven misses, five controls gated. The Edit half is the half that matters:
Edit carries a file_path, and limit 34 already says a path can be proven
contained. This is what that deferral costs.
Ships the confession and a tripwire that asserts the current, defective
behaviour with the plain spellings beside it as controls. When the matcher is
fixed the block fails; the repair is to invert it and amend entry 62, never to
delete it.
The fix is src/policy and non-delegable core. It was attempted and the gate
refused it unsigned, correctly. The bypass proved here was not used to land
the fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@githubscum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

limit 62: an equivalent path spelling walks around the self-mod gate - #43

Open
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings
Open

limit 62: an equivalent path spelling walks around the self-mod gate#43
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings

Conversation

@githubscum

Copy link
Copy Markdown
Owner

WO (lotor-lane run 10): probe the self-mod matcher for spelling escapes, the way runs 6, 8 and 9 found their defects — by executing it, not reading it.

The finding

normalizePath() converts backslashes, lowercases, strips a trailing slash. That is all. The fragment list is then matched as a plain substring — cmdNorm.includes(frag) for commands, pathContainsFragment() for Edit. A doubled separator or a . segment defeats both and changes nothing about which file opens.

Fourteen spellings through the shipped matcher: five controls gated, seven misses.

SpellingGatedOpens the protected file
<repo>/src/policy/index.jsyesyes
<repo>/src//policy/index.jsnoyes
<repo>/src/./policy/index.jsnoyes
<repo>/src/./chain/index.jsnoyes
<repo>/bin//charter.jsnoyes
<repo>/./bin/./charter.jsnoyes

The right column is measured, not argued: the test writes a canary file and reads it back through all three spellings.

The Edit half is the one that matters.src/policy, src/chain, src/store, src/grant and every script in bin/ stay at gate in every mode including loose. This walks around that with one extra character, through the tool an agent actually uses to change code.

Not a new class. Limit 22 was tilde/$HOME. LOTOR-C2 was brace expansion. This is the third spelling escape in the same matcher, and all three were found by executing it rather than reading it.

What this PR ships

  • KNOWN-LIMITS 62 — the confession, with the measured table, the unverified case marked unverified (src/policy./index.js is ungated; Windows strips trailing dots, but existsSync refused that spelling here, so whether a real write lands is not proven), and a caution for whoever writes the fix: normalizePath is applied to whole command strings, so collapsing /+ globally also rewrites https://x and needs its blast radius checked across every caller.
  • test/policy-selfmod-separator-spellings.test.js — a tripwire, 13 assertions. It asserts the current, defective behaviour, with the plain spellings beside it as controls so the block cannot pass by the matcher dying. When the matcher is fixed, this file fails. The repair is to invert the assertions and amend entry 62 in the same change, never to delete the block.

Tested

npm test on this branch: 954 pass / 0 fail (941 on main plus the 13 new). The new file alone: 13/13.

Not fixed here, and why

The fix is normalizePath in src/policy — non-delegable core. It was attempted first and the gate refused it unsigned. Correct behaviour, and worth stating plainly: the bypass proved in this PR was not used to land the fix for the bypass. Queued for a signing sitting alongside limits 35 and 36.

What a reviewer should doubt

  1. A green suite now contains nine passing tests named "is FREE". Read cold that can look like the project blessing the gap. Same cost run 9 accepted for the limit-44 tripwire, and a reviewer may legitimately want these red instead. That call is not this lane's to make alone.
  2. The tripwire is not proven to trip. Demonstrating it needs the core edit the gate blocks. The claim is structural, not demonstrated. Do not describe it as proven fail-first.
  3. The command-side misses may be judged out of scope. Limit 34 concedes a shell command cannot be resolved, so the fragment list is arguably as correct as it gets there. The Edit-side misses have no such defence.
  4. REPO in the test is derived from import.meta.url with a Windows drive-letter fixup. It only builds path strings for the matcher and never touches the repo, but it is the least portable line in the file.
  5. Is one clause enough? The entry proposes collapsing /+ and dropping ./ segments. It does not claim that exhausts the spelling space: .. traversal is gated today only when the traversed-through directory happens to be protected too.

🤖 Generated with Claude Code

Found by running fourteen spellings through the shipped matcher, not by
reading it. normalizePath folds backslashes and case and stops there, so a
doubled separator or a dot segment contains no protected fragment
contiguously and is not gated, while opening exactly the same file.
Seven misses, five controls gated. The Edit half is the half that matters:
Edit carries a file_path, and limit 34 already says a path can be proven
contained. This is what that deferral costs.
Ships the confession and a tripwire that asserts the current, defective
behaviour with the plain spellings beside it as controls. When the matcher is
fixed the block fails; the repair is to invert it and amend entry 62, never to
delete it.
The fix is src/policy and non-delegable core. It was attempted and the gate
refused it unsigned, correctly. The bypass proved here was not used to land
the fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@githubscum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

limit 62: an equivalent path spelling walks around the self-mod gate - #43

Open
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings
Open

limit 62: an equivalent path spelling walks around the self-mod gate#43
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings

Conversation

@githubscum

Copy link
Copy Markdown
Owner

WO (lotor-lane run 10): probe the self-mod matcher for spelling escapes, the way runs 6, 8 and 9 found their defects — by executing it, not reading it.

The finding

normalizePath() converts backslashes, lowercases, strips a trailing slash. That is all. The fragment list is then matched as a plain substring — cmdNorm.includes(frag) for commands, pathContainsFragment() for Edit. A doubled separator or a . segment defeats both and changes nothing about which file opens.

Fourteen spellings through the shipped matcher: five controls gated, seven misses.

SpellingGatedOpens the protected file
<repo>/src/policy/index.jsyesyes
<repo>/src//policy/index.jsnoyes
<repo>/src/./policy/index.jsnoyes
<repo>/src/./chain/index.jsnoyes
<repo>/bin//charter.jsnoyes
<repo>/./bin/./charter.jsnoyes

The right column is measured, not argued: the test writes a canary file and reads it back through all three spellings.

The Edit half is the one that matters.src/policy, src/chain, src/store, src/grant and every script in bin/ stay at gate in every mode including loose. This walks around that with one extra character, through the tool an agent actually uses to change code.

Not a new class. Limit 22 was tilde/$HOME. LOTOR-C2 was brace expansion. This is the third spelling escape in the same matcher, and all three were found by executing it rather than reading it.

What this PR ships

  • KNOWN-LIMITS 62 — the confession, with the measured table, the unverified case marked unverified (src/policy./index.js is ungated; Windows strips trailing dots, but existsSync refused that spelling here, so whether a real write lands is not proven), and a caution for whoever writes the fix: normalizePath is applied to whole command strings, so collapsing /+ globally also rewrites https://x and needs its blast radius checked across every caller.
  • test/policy-selfmod-separator-spellings.test.js — a tripwire, 13 assertions. It asserts the current, defective behaviour, with the plain spellings beside it as controls so the block cannot pass by the matcher dying. When the matcher is fixed, this file fails. The repair is to invert the assertions and amend entry 62 in the same change, never to delete the block.

Tested

npm test on this branch: 954 pass / 0 fail (941 on main plus the 13 new). The new file alone: 13/13.

Not fixed here, and why

The fix is normalizePath in src/policy — non-delegable core. It was attempted first and the gate refused it unsigned. Correct behaviour, and worth stating plainly: the bypass proved in this PR was not used to land the fix for the bypass. Queued for a signing sitting alongside limits 35 and 36.

What a reviewer should doubt

  1. A green suite now contains nine passing tests named "is FREE". Read cold that can look like the project blessing the gap. Same cost run 9 accepted for the limit-44 tripwire, and a reviewer may legitimately want these red instead. That call is not this lane's to make alone.
  2. The tripwire is not proven to trip. Demonstrating it needs the core edit the gate blocks. The claim is structural, not demonstrated. Do not describe it as proven fail-first.
  3. The command-side misses may be judged out of scope. Limit 34 concedes a shell command cannot be resolved, so the fragment list is arguably as correct as it gets there. The Edit-side misses have no such defence.
  4. REPO in the test is derived from import.meta.url with a Windows drive-letter fixup. It only builds path strings for the matcher and never touches the repo, but it is the least portable line in the file.
  5. Is one clause enough? The entry proposes collapsing /+ and dropping ./ segments. It does not claim that exhausts the spelling space: .. traversal is gated today only when the traversed-through directory happens to be protected too.

🤖 Generated with Claude Code

Found by running fourteen spellings through the shipped matcher, not by
reading it. normalizePath folds backslashes and case and stops there, so a
doubled separator or a dot segment contains no protected fragment
contiguously and is not gated, while opening exactly the same file.
Seven misses, five controls gated. The Edit half is the half that matters:
Edit carries a file_path, and limit 34 already says a path can be proven
contained. This is what that deferral costs.
Ships the confession and a tripwire that asserts the current, defective
behaviour with the plain spellings beside it as controls. When the matcher is
fixed the block fails; the repair is to invert it and amend entry 62, never to
delete it.
The fix is src/policy and non-delegable core. It was attempted and the gate
refused it unsigned, correctly. The bypass proved here was not used to land
the fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

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

limit 62: an equivalent path spelling walks around the self-mod gate - #43

Open
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings
Open

limit 62: an equivalent path spelling walks around the self-mod gate#43
githubscum wants to merge 1 commit into
mainfrom
lotor-lane/limit-62-separator-spellings

Conversation

@githubscum

Copy link
Copy Markdown
Owner

WO (lotor-lane run 10): probe the self-mod matcher for spelling escapes, the way runs 6, 8 and 9 found their defects — by executing it, not reading it.

The finding

normalizePath() converts backslashes, lowercases, strips a trailing slash. That is all. The fragment list is then matched as a plain substring — cmdNorm.includes(frag) for commands, pathContainsFragment() for Edit. A doubled separator or a . segment defeats both and changes nothing about which file opens.

Fourteen spellings through the shipped matcher: five controls gated, seven misses.

SpellingGatedOpens the protected file
<repo>/src/policy/index.jsyesyes
<repo>/src//policy/index.jsnoyes
<repo>/src/./policy/index.jsnoyes
<repo>/src/./chain/index.jsnoyes
<repo>/bin//charter.jsnoyes
<repo>/./bin/./charter.jsnoyes

The right column is measured, not argued: the test writes a canary file and reads it back through all three spellings.

The Edit half is the one that matters.src/policy, src/chain, src/store, src/grant and every script in bin/ stay at gate in every mode including loose. This walks around that with one extra character, through the tool an agent actually uses to change code.

Not a new class. Limit 22 was tilde/$HOME. LOTOR-C2 was brace expansion. This is the third spelling escape in the same matcher, and all three were found by executing it rather than reading it.

What this PR ships

  • KNOWN-LIMITS 62 — the confession, with the measured table, the unverified case marked unverified (src/policy./index.js is ungated; Windows strips trailing dots, but existsSync refused that spelling here, so whether a real write lands is not proven), and a caution for whoever writes the fix: normalizePath is applied to whole command strings, so collapsing /+ globally also rewrites https://x and needs its blast radius checked across every caller.
  • test/policy-selfmod-separator-spellings.test.js — a tripwire, 13 assertions. It asserts the current, defective behaviour, with the plain spellings beside it as controls so the block cannot pass by the matcher dying. When the matcher is fixed, this file fails. The repair is to invert the assertions and amend entry 62 in the same change, never to delete the block.

Tested

npm test on this branch: 954 pass / 0 fail (941 on main plus the 13 new). The new file alone: 13/13.

Not fixed here, and why

The fix is normalizePath in src/policy — non-delegable core. It was attempted first and the gate refused it unsigned. Correct behaviour, and worth stating plainly: the bypass proved in this PR was not used to land the fix for the bypass. Queued for a signing sitting alongside limits 35 and 36.

What a reviewer should doubt

  1. A green suite now contains nine passing tests named "is FREE". Read cold that can look like the project blessing the gap. Same cost run 9 accepted for the limit-44 tripwire, and a reviewer may legitimately want these red instead. That call is not this lane's to make alone.
  2. The tripwire is not proven to trip. Demonstrating it needs the core edit the gate blocks. The claim is structural, not demonstrated. Do not describe it as proven fail-first.
  3. The command-side misses may be judged out of scope. Limit 34 concedes a shell command cannot be resolved, so the fragment list is arguably as correct as it gets there. The Edit-side misses have no such defence.
  4. REPO in the test is derived from import.meta.url with a Windows drive-letter fixup. It only builds path strings for the matcher and never touches the repo, but it is the least portable line in the file.
  5. Is one clause enough? The entry proposes collapsing /+ and dropping ./ segments. It does not claim that exhausts the spelling space: .. traversal is gated today only when the traversed-through directory happens to be protected too.

🤖 Generated with Claude Code

Found by running fourteen spellings through the shipped matcher, not by
reading it. normalizePath folds backslashes and case and stops there, so a
doubled separator or a dot segment contains no protected fragment
contiguously and is not gated, while opening exactly the same file.
Seven misses, five controls gated. The Edit half is the half that matters:
Edit carries a file_path, and limit 34 already says a path can be proven
contained. This is what that deferral costs.
Ships the confession and a tripwire that asserts the current, defective
behaviour with the plain spellings beside it as controls. When the matcher is
fixed the block fails; the repair is to invert it and amend entry 62, never to
delete it.
The fix is src/policy and non-delegable core. It was attempted and the gate
refused it unsigned, correctly. The bypass proved here was not used to land
the fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@githubscum