fix(opencode): allow blob URLs in the app CSP for image attachments - #140

Merged
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments
Aug 8, 2026
Merged

fix(opencode): allow blob URLs in the app CSP for image attachments#140
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments

Conversation

@gennadiryan

@gennadiryangennadiryan commented Aug 8, 2026

Copy link
Copy Markdown
Member

Important

Problem

Image attachments inside the Amicode webview were unusable: a pasted screenshot staged a broken thumbnail, and sending with a staged image failed ("Failed to send prompt / failed to fetch"), stalling the chat until the image was removed. Text-only messages were unaffected.

Approach

Allow the blob scheme in the app server's shared Content-Security-Policy for the image, connection, and media source lists. The attachment pipeline stages thumbnails and sends payloads entirely through object URLs, and the policy omitted the one scheme those URLs use — so the display path and the send path failed for the same single reason.

Approaches Considered

  • Allow blob in the app policy (chosen) — one policy change; restores thumbnails and sending together, for every attachment source.
  • Remove object URLs from the pipeline (render and send via data URLs) — larger change, and it would still leave the policy hostile to future blob use; retained as a second pass if the first proves insufficient.
  • Relax the policy wholesale — rejected: only one scheme was missing, and it is same-origin and page-created.

Scope

  • In: the app server's shared policy (image, connection, media sources) and its assertion coverage; verification across clipboard screenshots, copied files, and the attachment picker.
  • Out: the clipboard bridge and paste ownership — settled separately and unrelated to this defect; the accepted-format allowlist and absent size cap — deferred; the widget-frame policy — a separate surface carrying the same omission, deliberately not bundled.

Assumptions / Open Qs

  • The diagnosis is confirmed by the two policy refusals (image and connection) the failing pipeline surfaces in the console.
  • Whether the widget-frame surface needs the same allowance is unresolved.

What was failing

The shared policy listed network schemes and the data scheme but not the blob scheme: img-src 'self' data: https:, media-src 'self' data:, connect-src * data:. The wildcard matches network schemes only; non-network schemes must be listed explicitly, which is why data: was already there and blob: was not. Every attachment thumbnail is an object URL (image directive), and every send encodes the staged blob by fetching its object URL (connection directive) — both refused, hence the broken thumbnail and the failed send.

What changed

  • The shared policy's image, media, and connection source lists now include the blob scheme. Nothing else in the policy changes.
  • The connection-source entry is appended after the existing data entry, and the policy assertion coverage now checks the blob entries explicitly.

Verification

  • App server policy tests: passing, including the explicit blob-scheme assertions.
  • Paste regression suite (global-clipboard): passing — the single-value paste behavior (one keystroke, one insert, with and without shift) is unchanged.
  • Typecheck: clean for the touched packages; the remaining repo-level typecheck errors are pre-existing (missing generated SDK modules, confirmed against the pre-change baseline).
  • Remote GUI retest: the attachment button works; a screenshot pasted via the paste key stages into the attachments set; copy/paste shows no regression. The retested binary is the stamped build attributable to this change.

Key Decisions

  • Scheme-scoped allowance, nothing else: only the three source lists change.
  • Media included although the report concerns images — the pipeline is shared, and the same omission breaks audio and video attachments identically.
  • Object URLs stay. Replacing them is viable hardening but not required to fix the defect, and would enlarge the change under verification.

Constraints & Invariants

  • The blob scheme is same-origin and page-created; no remote origin is added to any source list.
  • No change to the clipboard bridge protocol, its message kinds, or paste ownership.
  • No change to the widget-frame policy or to the host webview policy.

Source

Companion to #261 (image-pasting failure). Unrelated to PR #130 (copy/paste duplication fix) — different mechanism, fix, and verification; the addendum issue #141 records the distinction. Related seam work: harmoniqs/amicode#243. Design of record: notes/image-attachment-csp.md in the harness.

Notes

The defect's diagnosis was pinned by source-independence: identical symptoms for screenshots, copied files, and picker attachments meant the fault sat in the shared pipeline or its policy — downstream of every paste path. Earlier tool versions fail the same way, consistent with a policy that predates the paste work.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit 28ec2a0 into local/amicodeAug 8, 2026
1 of 4 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/image-attachments branch August 8, 2026 06:10
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.

2 participants

@gennadiryan@jeonghun-jj-lee
, '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

fix(opencode): allow blob URLs in the app CSP for image attachments - #140

Merged
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments
Aug 8, 2026
Merged

fix(opencode): allow blob URLs in the app CSP for image attachments#140
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments

Conversation

@gennadiryan

@gennadiryangennadiryan commented Aug 8, 2026

Copy link
Copy Markdown
Member

Important

Problem

Image attachments inside the Amicode webview were unusable: a pasted screenshot staged a broken thumbnail, and sending with a staged image failed ("Failed to send prompt / failed to fetch"), stalling the chat until the image was removed. Text-only messages were unaffected.

Approach

Allow the blob scheme in the app server's shared Content-Security-Policy for the image, connection, and media source lists. The attachment pipeline stages thumbnails and sends payloads entirely through object URLs, and the policy omitted the one scheme those URLs use — so the display path and the send path failed for the same single reason.

Approaches Considered

  • Allow blob in the app policy (chosen) — one policy change; restores thumbnails and sending together, for every attachment source.
  • Remove object URLs from the pipeline (render and send via data URLs) — larger change, and it would still leave the policy hostile to future blob use; retained as a second pass if the first proves insufficient.
  • Relax the policy wholesale — rejected: only one scheme was missing, and it is same-origin and page-created.

Scope

  • In: the app server's shared policy (image, connection, media sources) and its assertion coverage; verification across clipboard screenshots, copied files, and the attachment picker.
  • Out: the clipboard bridge and paste ownership — settled separately and unrelated to this defect; the accepted-format allowlist and absent size cap — deferred; the widget-frame policy — a separate surface carrying the same omission, deliberately not bundled.

Assumptions / Open Qs

  • The diagnosis is confirmed by the two policy refusals (image and connection) the failing pipeline surfaces in the console.
  • Whether the widget-frame surface needs the same allowance is unresolved.

What was failing

The shared policy listed network schemes and the data scheme but not the blob scheme: img-src 'self' data: https:, media-src 'self' data:, connect-src * data:. The wildcard matches network schemes only; non-network schemes must be listed explicitly, which is why data: was already there and blob: was not. Every attachment thumbnail is an object URL (image directive), and every send encodes the staged blob by fetching its object URL (connection directive) — both refused, hence the broken thumbnail and the failed send.

What changed

  • The shared policy's image, media, and connection source lists now include the blob scheme. Nothing else in the policy changes.
  • The connection-source entry is appended after the existing data entry, and the policy assertion coverage now checks the blob entries explicitly.

Verification

  • App server policy tests: passing, including the explicit blob-scheme assertions.
  • Paste regression suite (global-clipboard): passing — the single-value paste behavior (one keystroke, one insert, with and without shift) is unchanged.
  • Typecheck: clean for the touched packages; the remaining repo-level typecheck errors are pre-existing (missing generated SDK modules, confirmed against the pre-change baseline).
  • Remote GUI retest: the attachment button works; a screenshot pasted via the paste key stages into the attachments set; copy/paste shows no regression. The retested binary is the stamped build attributable to this change.

Key Decisions

  • Scheme-scoped allowance, nothing else: only the three source lists change.
  • Media included although the report concerns images — the pipeline is shared, and the same omission breaks audio and video attachments identically.
  • Object URLs stay. Replacing them is viable hardening but not required to fix the defect, and would enlarge the change under verification.

Constraints & Invariants

  • The blob scheme is same-origin and page-created; no remote origin is added to any source list.
  • No change to the clipboard bridge protocol, its message kinds, or paste ownership.
  • No change to the widget-frame policy or to the host webview policy.

Source

Companion to #261 (image-pasting failure). Unrelated to PR #130 (copy/paste duplication fix) — different mechanism, fix, and verification; the addendum issue #141 records the distinction. Related seam work: harmoniqs/amicode#243. Design of record: notes/image-attachment-csp.md in the harness.

Notes

The defect's diagnosis was pinned by source-independence: identical symptoms for screenshots, copied files, and picker attachments meant the fault sat in the shared pipeline or its policy — downstream of every paste path. Earlier tool versions fail the same way, consistent with a policy that predates the paste work.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit 28ec2a0 into local/amicodeAug 8, 2026
1 of 4 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/image-attachments branch August 8, 2026 06:10
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.

2 participants

@gennadiryan@jeonghun-jj-lee
, '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

fix(opencode): allow blob URLs in the app CSP for image attachments - #140

Merged
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments
Aug 8, 2026
Merged

fix(opencode): allow blob URLs in the app CSP for image attachments#140
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments

Conversation

@gennadiryan

@gennadiryangennadiryan commented Aug 8, 2026

Copy link
Copy Markdown
Member

Important

Problem

Image attachments inside the Amicode webview were unusable: a pasted screenshot staged a broken thumbnail, and sending with a staged image failed ("Failed to send prompt / failed to fetch"), stalling the chat until the image was removed. Text-only messages were unaffected.

Approach

Allow the blob scheme in the app server's shared Content-Security-Policy for the image, connection, and media source lists. The attachment pipeline stages thumbnails and sends payloads entirely through object URLs, and the policy omitted the one scheme those URLs use — so the display path and the send path failed for the same single reason.

Approaches Considered

  • Allow blob in the app policy (chosen) — one policy change; restores thumbnails and sending together, for every attachment source.
  • Remove object URLs from the pipeline (render and send via data URLs) — larger change, and it would still leave the policy hostile to future blob use; retained as a second pass if the first proves insufficient.
  • Relax the policy wholesale — rejected: only one scheme was missing, and it is same-origin and page-created.

Scope

  • In: the app server's shared policy (image, connection, media sources) and its assertion coverage; verification across clipboard screenshots, copied files, and the attachment picker.
  • Out: the clipboard bridge and paste ownership — settled separately and unrelated to this defect; the accepted-format allowlist and absent size cap — deferred; the widget-frame policy — a separate surface carrying the same omission, deliberately not bundled.

Assumptions / Open Qs

  • The diagnosis is confirmed by the two policy refusals (image and connection) the failing pipeline surfaces in the console.
  • Whether the widget-frame surface needs the same allowance is unresolved.

What was failing

The shared policy listed network schemes and the data scheme but not the blob scheme: img-src 'self' data: https:, media-src 'self' data:, connect-src * data:. The wildcard matches network schemes only; non-network schemes must be listed explicitly, which is why data: was already there and blob: was not. Every attachment thumbnail is an object URL (image directive), and every send encodes the staged blob by fetching its object URL (connection directive) — both refused, hence the broken thumbnail and the failed send.

What changed

  • The shared policy's image, media, and connection source lists now include the blob scheme. Nothing else in the policy changes.
  • The connection-source entry is appended after the existing data entry, and the policy assertion coverage now checks the blob entries explicitly.

Verification

  • App server policy tests: passing, including the explicit blob-scheme assertions.
  • Paste regression suite (global-clipboard): passing — the single-value paste behavior (one keystroke, one insert, with and without shift) is unchanged.
  • Typecheck: clean for the touched packages; the remaining repo-level typecheck errors are pre-existing (missing generated SDK modules, confirmed against the pre-change baseline).
  • Remote GUI retest: the attachment button works; a screenshot pasted via the paste key stages into the attachments set; copy/paste shows no regression. The retested binary is the stamped build attributable to this change.

Key Decisions

  • Scheme-scoped allowance, nothing else: only the three source lists change.
  • Media included although the report concerns images — the pipeline is shared, and the same omission breaks audio and video attachments identically.
  • Object URLs stay. Replacing them is viable hardening but not required to fix the defect, and would enlarge the change under verification.

Constraints & Invariants

  • The blob scheme is same-origin and page-created; no remote origin is added to any source list.
  • No change to the clipboard bridge protocol, its message kinds, or paste ownership.
  • No change to the widget-frame policy or to the host webview policy.

Source

Companion to #261 (image-pasting failure). Unrelated to PR #130 (copy/paste duplication fix) — different mechanism, fix, and verification; the addendum issue #141 records the distinction. Related seam work: harmoniqs/amicode#243. Design of record: notes/image-attachment-csp.md in the harness.

Notes

The defect's diagnosis was pinned by source-independence: identical symptoms for screenshots, copied files, and picker attachments meant the fault sat in the shared pipeline or its policy — downstream of every paste path. Earlier tool versions fail the same way, consistent with a policy that predates the paste work.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit 28ec2a0 into local/amicodeAug 8, 2026
1 of 4 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/image-attachments branch August 8, 2026 06:10
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.

2 participants

@gennadiryan@jeonghun-jj-lee
, '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

fix(opencode): allow blob URLs in the app CSP for image attachments - #140

Merged
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments
Aug 8, 2026
Merged

fix(opencode): allow blob URLs in the app CSP for image attachments#140
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments

Conversation

@gennadiryan

@gennadiryangennadiryan commented Aug 8, 2026

Copy link
Copy Markdown
Member

Important

Problem

Image attachments inside the Amicode webview were unusable: a pasted screenshot staged a broken thumbnail, and sending with a staged image failed ("Failed to send prompt / failed to fetch"), stalling the chat until the image was removed. Text-only messages were unaffected.

Approach

Allow the blob scheme in the app server's shared Content-Security-Policy for the image, connection, and media source lists. The attachment pipeline stages thumbnails and sends payloads entirely through object URLs, and the policy omitted the one scheme those URLs use — so the display path and the send path failed for the same single reason.

Approaches Considered

  • Allow blob in the app policy (chosen) — one policy change; restores thumbnails and sending together, for every attachment source.
  • Remove object URLs from the pipeline (render and send via data URLs) — larger change, and it would still leave the policy hostile to future blob use; retained as a second pass if the first proves insufficient.
  • Relax the policy wholesale — rejected: only one scheme was missing, and it is same-origin and page-created.

Scope

  • In: the app server's shared policy (image, connection, media sources) and its assertion coverage; verification across clipboard screenshots, copied files, and the attachment picker.
  • Out: the clipboard bridge and paste ownership — settled separately and unrelated to this defect; the accepted-format allowlist and absent size cap — deferred; the widget-frame policy — a separate surface carrying the same omission, deliberately not bundled.

Assumptions / Open Qs

  • The diagnosis is confirmed by the two policy refusals (image and connection) the failing pipeline surfaces in the console.
  • Whether the widget-frame surface needs the same allowance is unresolved.

What was failing

The shared policy listed network schemes and the data scheme but not the blob scheme: img-src 'self' data: https:, media-src 'self' data:, connect-src * data:. The wildcard matches network schemes only; non-network schemes must be listed explicitly, which is why data: was already there and blob: was not. Every attachment thumbnail is an object URL (image directive), and every send encodes the staged blob by fetching its object URL (connection directive) — both refused, hence the broken thumbnail and the failed send.

What changed

  • The shared policy's image, media, and connection source lists now include the blob scheme. Nothing else in the policy changes.
  • The connection-source entry is appended after the existing data entry, and the policy assertion coverage now checks the blob entries explicitly.

Verification

  • App server policy tests: passing, including the explicit blob-scheme assertions.
  • Paste regression suite (global-clipboard): passing — the single-value paste behavior (one keystroke, one insert, with and without shift) is unchanged.
  • Typecheck: clean for the touched packages; the remaining repo-level typecheck errors are pre-existing (missing generated SDK modules, confirmed against the pre-change baseline).
  • Remote GUI retest: the attachment button works; a screenshot pasted via the paste key stages into the attachments set; copy/paste shows no regression. The retested binary is the stamped build attributable to this change.

Key Decisions

  • Scheme-scoped allowance, nothing else: only the three source lists change.
  • Media included although the report concerns images — the pipeline is shared, and the same omission breaks audio and video attachments identically.
  • Object URLs stay. Replacing them is viable hardening but not required to fix the defect, and would enlarge the change under verification.

Constraints & Invariants

  • The blob scheme is same-origin and page-created; no remote origin is added to any source list.
  • No change to the clipboard bridge protocol, its message kinds, or paste ownership.
  • No change to the widget-frame policy or to the host webview policy.

Source

Companion to #261 (image-pasting failure). Unrelated to PR #130 (copy/paste duplication fix) — different mechanism, fix, and verification; the addendum issue #141 records the distinction. Related seam work: harmoniqs/amicode#243. Design of record: notes/image-attachment-csp.md in the harness.

Notes

The defect's diagnosis was pinned by source-independence: identical symptoms for screenshots, copied files, and picker attachments meant the fault sat in the shared pipeline or its policy — downstream of every paste path. Earlier tool versions fail the same way, consistent with a policy that predates the paste work.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit 28ec2a0 into local/amicodeAug 8, 2026
1 of 4 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/image-attachments branch August 8, 2026 06:10
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.

2 participants

@gennadiryan@jeonghun-jj-lee
, '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

fix(opencode): allow blob URLs in the app CSP for image attachments - #140

Merged
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments
Aug 8, 2026
Merged

fix(opencode): allow blob URLs in the app CSP for image attachments#140
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments

Conversation

@gennadiryan

@gennadiryangennadiryan commented Aug 8, 2026

Copy link
Copy Markdown
Member

Important

Problem

Image attachments inside the Amicode webview were unusable: a pasted screenshot staged a broken thumbnail, and sending with a staged image failed ("Failed to send prompt / failed to fetch"), stalling the chat until the image was removed. Text-only messages were unaffected.

Approach

Allow the blob scheme in the app server's shared Content-Security-Policy for the image, connection, and media source lists. The attachment pipeline stages thumbnails and sends payloads entirely through object URLs, and the policy omitted the one scheme those URLs use — so the display path and the send path failed for the same single reason.

Approaches Considered

  • Allow blob in the app policy (chosen) — one policy change; restores thumbnails and sending together, for every attachment source.
  • Remove object URLs from the pipeline (render and send via data URLs) — larger change, and it would still leave the policy hostile to future blob use; retained as a second pass if the first proves insufficient.
  • Relax the policy wholesale — rejected: only one scheme was missing, and it is same-origin and page-created.

Scope

  • In: the app server's shared policy (image, connection, media sources) and its assertion coverage; verification across clipboard screenshots, copied files, and the attachment picker.
  • Out: the clipboard bridge and paste ownership — settled separately and unrelated to this defect; the accepted-format allowlist and absent size cap — deferred; the widget-frame policy — a separate surface carrying the same omission, deliberately not bundled.

Assumptions / Open Qs

  • The diagnosis is confirmed by the two policy refusals (image and connection) the failing pipeline surfaces in the console.
  • Whether the widget-frame surface needs the same allowance is unresolved.

What was failing

The shared policy listed network schemes and the data scheme but not the blob scheme: img-src 'self' data: https:, media-src 'self' data:, connect-src * data:. The wildcard matches network schemes only; non-network schemes must be listed explicitly, which is why data: was already there and blob: was not. Every attachment thumbnail is an object URL (image directive), and every send encodes the staged blob by fetching its object URL (connection directive) — both refused, hence the broken thumbnail and the failed send.

What changed

  • The shared policy's image, media, and connection source lists now include the blob scheme. Nothing else in the policy changes.
  • The connection-source entry is appended after the existing data entry, and the policy assertion coverage now checks the blob entries explicitly.

Verification

  • App server policy tests: passing, including the explicit blob-scheme assertions.
  • Paste regression suite (global-clipboard): passing — the single-value paste behavior (one keystroke, one insert, with and without shift) is unchanged.
  • Typecheck: clean for the touched packages; the remaining repo-level typecheck errors are pre-existing (missing generated SDK modules, confirmed against the pre-change baseline).
  • Remote GUI retest: the attachment button works; a screenshot pasted via the paste key stages into the attachments set; copy/paste shows no regression. The retested binary is the stamped build attributable to this change.

Key Decisions

  • Scheme-scoped allowance, nothing else: only the three source lists change.
  • Media included although the report concerns images — the pipeline is shared, and the same omission breaks audio and video attachments identically.
  • Object URLs stay. Replacing them is viable hardening but not required to fix the defect, and would enlarge the change under verification.

Constraints & Invariants

  • The blob scheme is same-origin and page-created; no remote origin is added to any source list.
  • No change to the clipboard bridge protocol, its message kinds, or paste ownership.
  • No change to the widget-frame policy or to the host webview policy.

Source

Companion to #261 (image-pasting failure). Unrelated to PR #130 (copy/paste duplication fix) — different mechanism, fix, and verification; the addendum issue #141 records the distinction. Related seam work: harmoniqs/amicode#243. Design of record: notes/image-attachment-csp.md in the harness.

Notes

The defect's diagnosis was pinned by source-independence: identical symptoms for screenshots, copied files, and picker attachments meant the fault sat in the shared pipeline or its policy — downstream of every paste path. Earlier tool versions fail the same way, consistent with a policy that predates the paste work.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit 28ec2a0 into local/amicodeAug 8, 2026
1 of 4 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/image-attachments branch August 8, 2026 06:10
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.

2 participants

@gennadiryan@jeonghun-jj-lee
, '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

fix(opencode): allow blob URLs in the app CSP for image attachments - #140

Merged
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments
Aug 8, 2026
Merged

fix(opencode): allow blob URLs in the app CSP for image attachments#140
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments

Conversation

@gennadiryan

@gennadiryangennadiryan commented Aug 8, 2026

Copy link
Copy Markdown
Member

Important

Problem

Image attachments inside the Amicode webview were unusable: a pasted screenshot staged a broken thumbnail, and sending with a staged image failed ("Failed to send prompt / failed to fetch"), stalling the chat until the image was removed. Text-only messages were unaffected.

Approach

Allow the blob scheme in the app server's shared Content-Security-Policy for the image, connection, and media source lists. The attachment pipeline stages thumbnails and sends payloads entirely through object URLs, and the policy omitted the one scheme those URLs use — so the display path and the send path failed for the same single reason.

Approaches Considered

  • Allow blob in the app policy (chosen) — one policy change; restores thumbnails and sending together, for every attachment source.
  • Remove object URLs from the pipeline (render and send via data URLs) — larger change, and it would still leave the policy hostile to future blob use; retained as a second pass if the first proves insufficient.
  • Relax the policy wholesale — rejected: only one scheme was missing, and it is same-origin and page-created.

Scope

  • In: the app server's shared policy (image, connection, media sources) and its assertion coverage; verification across clipboard screenshots, copied files, and the attachment picker.
  • Out: the clipboard bridge and paste ownership — settled separately and unrelated to this defect; the accepted-format allowlist and absent size cap — deferred; the widget-frame policy — a separate surface carrying the same omission, deliberately not bundled.

Assumptions / Open Qs

  • The diagnosis is confirmed by the two policy refusals (image and connection) the failing pipeline surfaces in the console.
  • Whether the widget-frame surface needs the same allowance is unresolved.

What was failing

The shared policy listed network schemes and the data scheme but not the blob scheme: img-src 'self' data: https:, media-src 'self' data:, connect-src * data:. The wildcard matches network schemes only; non-network schemes must be listed explicitly, which is why data: was already there and blob: was not. Every attachment thumbnail is an object URL (image directive), and every send encodes the staged blob by fetching its object URL (connection directive) — both refused, hence the broken thumbnail and the failed send.

What changed

  • The shared policy's image, media, and connection source lists now include the blob scheme. Nothing else in the policy changes.
  • The connection-source entry is appended after the existing data entry, and the policy assertion coverage now checks the blob entries explicitly.

Verification

  • App server policy tests: passing, including the explicit blob-scheme assertions.
  • Paste regression suite (global-clipboard): passing — the single-value paste behavior (one keystroke, one insert, with and without shift) is unchanged.
  • Typecheck: clean for the touched packages; the remaining repo-level typecheck errors are pre-existing (missing generated SDK modules, confirmed against the pre-change baseline).
  • Remote GUI retest: the attachment button works; a screenshot pasted via the paste key stages into the attachments set; copy/paste shows no regression. The retested binary is the stamped build attributable to this change.

Key Decisions

  • Scheme-scoped allowance, nothing else: only the three source lists change.
  • Media included although the report concerns images — the pipeline is shared, and the same omission breaks audio and video attachments identically.
  • Object URLs stay. Replacing them is viable hardening but not required to fix the defect, and would enlarge the change under verification.

Constraints & Invariants

  • The blob scheme is same-origin and page-created; no remote origin is added to any source list.
  • No change to the clipboard bridge protocol, its message kinds, or paste ownership.
  • No change to the widget-frame policy or to the host webview policy.

Source

Companion to #261 (image-pasting failure). Unrelated to PR #130 (copy/paste duplication fix) — different mechanism, fix, and verification; the addendum issue #141 records the distinction. Related seam work: harmoniqs/amicode#243. Design of record: notes/image-attachment-csp.md in the harness.

Notes

The defect's diagnosis was pinned by source-independence: identical symptoms for screenshots, copied files, and picker attachments meant the fault sat in the shared pipeline or its policy — downstream of every paste path. Earlier tool versions fail the same way, consistent with a policy that predates the paste work.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit 28ec2a0 into local/amicodeAug 8, 2026
1 of 4 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/image-attachments branch August 8, 2026 06:10
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.

2 participants

@gennadiryan@jeonghun-jj-lee
, '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

fix(opencode): allow blob URLs in the app CSP for image attachments - #140

Merged
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments
Aug 8, 2026
Merged

fix(opencode): allow blob URLs in the app CSP for image attachments#140
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments

Conversation

@gennadiryan

@gennadiryangennadiryan commented Aug 8, 2026

Copy link
Copy Markdown
Member

Important

Problem

Image attachments inside the Amicode webview were unusable: a pasted screenshot staged a broken thumbnail, and sending with a staged image failed ("Failed to send prompt / failed to fetch"), stalling the chat until the image was removed. Text-only messages were unaffected.

Approach

Allow the blob scheme in the app server's shared Content-Security-Policy for the image, connection, and media source lists. The attachment pipeline stages thumbnails and sends payloads entirely through object URLs, and the policy omitted the one scheme those URLs use — so the display path and the send path failed for the same single reason.

Approaches Considered

  • Allow blob in the app policy (chosen) — one policy change; restores thumbnails and sending together, for every attachment source.
  • Remove object URLs from the pipeline (render and send via data URLs) — larger change, and it would still leave the policy hostile to future blob use; retained as a second pass if the first proves insufficient.
  • Relax the policy wholesale — rejected: only one scheme was missing, and it is same-origin and page-created.

Scope

  • In: the app server's shared policy (image, connection, media sources) and its assertion coverage; verification across clipboard screenshots, copied files, and the attachment picker.
  • Out: the clipboard bridge and paste ownership — settled separately and unrelated to this defect; the accepted-format allowlist and absent size cap — deferred; the widget-frame policy — a separate surface carrying the same omission, deliberately not bundled.

Assumptions / Open Qs

  • The diagnosis is confirmed by the two policy refusals (image and connection) the failing pipeline surfaces in the console.
  • Whether the widget-frame surface needs the same allowance is unresolved.

What was failing

The shared policy listed network schemes and the data scheme but not the blob scheme: img-src 'self' data: https:, media-src 'self' data:, connect-src * data:. The wildcard matches network schemes only; non-network schemes must be listed explicitly, which is why data: was already there and blob: was not. Every attachment thumbnail is an object URL (image directive), and every send encodes the staged blob by fetching its object URL (connection directive) — both refused, hence the broken thumbnail and the failed send.

What changed

  • The shared policy's image, media, and connection source lists now include the blob scheme. Nothing else in the policy changes.
  • The connection-source entry is appended after the existing data entry, and the policy assertion coverage now checks the blob entries explicitly.

Verification

  • App server policy tests: passing, including the explicit blob-scheme assertions.
  • Paste regression suite (global-clipboard): passing — the single-value paste behavior (one keystroke, one insert, with and without shift) is unchanged.
  • Typecheck: clean for the touched packages; the remaining repo-level typecheck errors are pre-existing (missing generated SDK modules, confirmed against the pre-change baseline).
  • Remote GUI retest: the attachment button works; a screenshot pasted via the paste key stages into the attachments set; copy/paste shows no regression. The retested binary is the stamped build attributable to this change.

Key Decisions

  • Scheme-scoped allowance, nothing else: only the three source lists change.
  • Media included although the report concerns images — the pipeline is shared, and the same omission breaks audio and video attachments identically.
  • Object URLs stay. Replacing them is viable hardening but not required to fix the defect, and would enlarge the change under verification.

Constraints & Invariants

  • The blob scheme is same-origin and page-created; no remote origin is added to any source list.
  • No change to the clipboard bridge protocol, its message kinds, or paste ownership.
  • No change to the widget-frame policy or to the host webview policy.

Source

Companion to #261 (image-pasting failure). Unrelated to PR #130 (copy/paste duplication fix) — different mechanism, fix, and verification; the addendum issue #141 records the distinction. Related seam work: harmoniqs/amicode#243. Design of record: notes/image-attachment-csp.md in the harness.

Notes

The defect's diagnosis was pinned by source-independence: identical symptoms for screenshots, copied files, and picker attachments meant the fault sat in the shared pipeline or its policy — downstream of every paste path. Earlier tool versions fail the same way, consistent with a policy that predates the paste work.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit 28ec2a0 into local/amicodeAug 8, 2026
1 of 4 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/image-attachments branch August 8, 2026 06:10
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.

2 participants

@gennadiryan@jeonghun-jj-lee
, '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

fix(opencode): allow blob URLs in the app CSP for image attachments - #140

Merged
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments
Aug 8, 2026
Merged

fix(opencode): allow blob URLs in the app CSP for image attachments#140
jeonghun-jj-lee merged 1 commit into
local/amicodefrom
fix/image-attachments

Conversation

@gennadiryan

@gennadiryangennadiryan commented Aug 8, 2026

Copy link
Copy Markdown
Member

Important

Problem

Image attachments inside the Amicode webview were unusable: a pasted screenshot staged a broken thumbnail, and sending with a staged image failed ("Failed to send prompt / failed to fetch"), stalling the chat until the image was removed. Text-only messages were unaffected.

Approach

Allow the blob scheme in the app server's shared Content-Security-Policy for the image, connection, and media source lists. The attachment pipeline stages thumbnails and sends payloads entirely through object URLs, and the policy omitted the one scheme those URLs use — so the display path and the send path failed for the same single reason.

Approaches Considered

  • Allow blob in the app policy (chosen) — one policy change; restores thumbnails and sending together, for every attachment source.
  • Remove object URLs from the pipeline (render and send via data URLs) — larger change, and it would still leave the policy hostile to future blob use; retained as a second pass if the first proves insufficient.
  • Relax the policy wholesale — rejected: only one scheme was missing, and it is same-origin and page-created.

Scope

  • In: the app server's shared policy (image, connection, media sources) and its assertion coverage; verification across clipboard screenshots, copied files, and the attachment picker.
  • Out: the clipboard bridge and paste ownership — settled separately and unrelated to this defect; the accepted-format allowlist and absent size cap — deferred; the widget-frame policy — a separate surface carrying the same omission, deliberately not bundled.

Assumptions / Open Qs

  • The diagnosis is confirmed by the two policy refusals (image and connection) the failing pipeline surfaces in the console.
  • Whether the widget-frame surface needs the same allowance is unresolved.

What was failing

The shared policy listed network schemes and the data scheme but not the blob scheme: img-src 'self' data: https:, media-src 'self' data:, connect-src * data:. The wildcard matches network schemes only; non-network schemes must be listed explicitly, which is why data: was already there and blob: was not. Every attachment thumbnail is an object URL (image directive), and every send encodes the staged blob by fetching its object URL (connection directive) — both refused, hence the broken thumbnail and the failed send.

What changed

  • The shared policy's image, media, and connection source lists now include the blob scheme. Nothing else in the policy changes.
  • The connection-source entry is appended after the existing data entry, and the policy assertion coverage now checks the blob entries explicitly.

Verification

  • App server policy tests: passing, including the explicit blob-scheme assertions.
  • Paste regression suite (global-clipboard): passing — the single-value paste behavior (one keystroke, one insert, with and without shift) is unchanged.
  • Typecheck: clean for the touched packages; the remaining repo-level typecheck errors are pre-existing (missing generated SDK modules, confirmed against the pre-change baseline).
  • Remote GUI retest: the attachment button works; a screenshot pasted via the paste key stages into the attachments set; copy/paste shows no regression. The retested binary is the stamped build attributable to this change.

Key Decisions

  • Scheme-scoped allowance, nothing else: only the three source lists change.
  • Media included although the report concerns images — the pipeline is shared, and the same omission breaks audio and video attachments identically.
  • Object URLs stay. Replacing them is viable hardening but not required to fix the defect, and would enlarge the change under verification.

Constraints & Invariants

  • The blob scheme is same-origin and page-created; no remote origin is added to any source list.
  • No change to the clipboard bridge protocol, its message kinds, or paste ownership.
  • No change to the widget-frame policy or to the host webview policy.

Source

Companion to #261 (image-pasting failure). Unrelated to PR #130 (copy/paste duplication fix) — different mechanism, fix, and verification; the addendum issue #141 records the distinction. Related seam work: harmoniqs/amicode#243. Design of record: notes/image-attachment-csp.md in the harness.

Notes

The defect's diagnosis was pinned by source-independence: identical symptoms for screenshots, copied files, and picker attachments meant the fault sat in the shared pipeline or its policy — downstream of every paste path. Earlier tool versions fail the same way, consistent with a policy that predates the paste work.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit 28ec2a0 into local/amicodeAug 8, 2026
1 of 4 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/image-attachments branch August 8, 2026 06:10
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.

2 participants

@gennadiryan@jeonghun-jj-lee