Skip to content

Add error-triggered feedback diagnostics#163

Merged
friuns2 merged 9 commits into
mainfrom
codex/error-feedback-diagnostics
May 12, 2026
Merged

Add error-triggered feedback diagnostics#163
friuns2 merged 9 commits into
mainfrom
codex/error-feedback-diagnostics

Conversation

@friuns2
Copy link
Copy Markdown
Owner

@friuns2 friuns2 commented May 12, 2026

Summary

  • show a chat-visible error when the Codex CLI is missing
  • collect runtime, unhandled rejection, failed fetch/API, and visible-error diagnostics
  • expose feedback from Settings after failures and directly on visible error states

Verification

  • pnpm exec vitest run src/composables/useFeedbackDiagnostics.test.ts src/composables/useDesktopState.test.ts
  • pnpm run build:frontend
  • Playwright forced visible Skills Hub error: Send feedback appeared with mailto diagnostics
  • PROFILE_BASE_URL=http://127.0.0.1:4175 PROFILE_WAIT_MS=7000 pnpm run profile:browser

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 12, 2026

Review Summary by Qodo

(Agentic_describe updated until commit 585b98e)

Add error-triggered feedback diagnostics with visible error handling

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add error-triggered feedback diagnostics collection system capturing runtime, unhandled rejection,
  fetch/API, and visible-error events
• Display chat-visible error banner when Codex CLI is missing with feedback action
• Expose feedback mailto action in Settings after failures and directly on visible error states
• Integrate feedback diagnostics into multiple error surfaces (Skills Hub, branch dropdown, live
  overlay, folder picker)
Diagram
flowchart LR
  A["Error Events<br/>window/fetch/rejection"] -->|recordFeedbackDiagnostic| B["Diagnostics Store"]
  C["Visible Errors<br/>CLI/API/UI"] -->|recordVisibleFailure| B
  B -->|buildFeedbackMailto| D["Prefilled Email<br/>with Context"]
  B -->|hasFeedbackDiagnostics| E["Settings Feedback Row"]
  C -->|inline feedback| F["Error Banners<br/>with Send Feedback"]
  D --> G["User Sends Report"]
  E --> G
  F --> G
Loading

Grey Divider

File Changes

1. src/composables/useFeedbackDiagnostics.ts ✨ Enhancement +189/-0

New feedback diagnostics composable with event listeners

src/composables/useFeedbackDiagnostics.ts


2. src/composables/useFeedbackDiagnostics.test.ts 🧪 Tests +71/-0

Test suite for feedback diagnostics and mailto builder

src/composables/useFeedbackDiagnostics.test.ts


3. src/composables/useDesktopState.ts ✨ Enhancement +16/-1

Add Codex CLI missing error detection and state tracking

src/composables/useDesktopState.ts


View more (8)
4. src/composables/useDesktopState.test.ts 🧪 Tests +13/-0

Test Codex CLI availability error surfacing

src/composables/useDesktopState.test.ts


5. src/main.ts ✨ Enhancement +3/-0

Initialize feedback diagnostics on app startup

src/main.ts


6. src/App.vue ✨ Enhancement +83/-7

Add feedback actions to error states and Settings panel

src/App.vue


7. src/components/content/HeaderGitBranchDropdown.vue ✨ Enhancement +15/-4

Add feedback action to branch dropdown error messages

src/components/content/HeaderGitBranchDropdown.vue


8. src/components/content/SkillsHub.vue ✨ Enhancement +43/-7

Add feedback actions to Skills Hub error surfaces

src/components/content/SkillsHub.vue


9. src/components/content/ThreadConversation.vue ✨ Enhancement +17/-2

Add feedback action to live overlay error display

src/components/content/ThreadConversation.vue


10. src/style.css ✨ Enhancement +16/-0

Add dark theme styles for feedback error components

src/style.css


11. tests.md 📝 Documentation +59/-0

Document error-triggered feedback and missing CLI features

tests.md


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 12, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Secrets in feedback mailto 🐞 Bug ⛨ Security
Description
buildFeedbackMailto() captures full localStorage/sessionStorage contents and visible page text and
encodes them into the mailto body, which can expose auth tokens/PII in the email draft and directly
in rendered link href attributes. The test suite explicitly verifies a token-like key
("codex-token") is included, confirming the leakage path.
Code

src/composables/useFeedbackDiagnostics.ts[R53-97]

+function readVisiblePageText(): string {
+  if (typeof document === 'undefined') return 'unknown'
+  const text = document.body?.innerText
+    .replace(/\r\n/g, '\n')
+    .replace(/[ \t]+/g, ' ')
+    .replace(/\n{3,}/g, '\n\n')
+    .trim() ?? ''
+  if (!text) return 'No visible page text captured.'
+  return text
+}
+
+function normalizeStorageValue(value: string): string {
+  return value
+    .replace(/\r\n/g, '\n')
+    .replace(/[ \t]+/g, ' ')
+    .trim()
+}
+
+function readStorageSnapshot(storage: Storage | undefined, label: string): string {
+  if (!storage) return `${label}: unavailable`
+  try {
+    const rows: string[] = []
+    for (let index = 0; index < storage.length; index += 1) {
+      const key = storage.key(index)
+      if (!key) continue
+      rows.push(`${key}=${normalizeStorageValue(storage.getItem(key) ?? '')}`)
+    }
+    return `${label}:\n${rows.join('\n') || 'empty'}`
+  } catch (error) {
+    return `${label}: unavailable (${normalizeSubjectMessage(normalizeMessage(error))})`
+  }
+}
+
+function readBrowserStateSnapshot(): string {
+  if (typeof window === 'undefined') return 'unknown'
+  return [
+    `Path: ${window.location.pathname || '/'}`,
+    `Hash: ${window.location.hash || '(none)'}`,
+    `Search: ${window.location.search || '(none)'}`,
+    `Online: ${typeof navigator === 'undefined' ? 'unknown' : String(navigator.onLine)}`,
+    `Language: ${typeof navigator === 'undefined' ? 'unknown' : navigator.language}`,
+    `Platform: ${typeof navigator === 'undefined' ? 'unknown' : navigator.platform}`,
+    readStorageSnapshot(window.localStorage, 'localStorage'),
+    readStorageSnapshot(window.sessionStorage, 'sessionStorage'),
+  ].join('\n')
Evidence
The diagnostics builder enumerates and includes every storage key/value and visible page text in the
email body, and the tests confirm token-like data is expected to be included; components bind the
resulting mailto into anchor hrefs.

src/composables/useFeedbackDiagnostics.ts[53-98]
src/composables/useFeedbackDiagnostics.ts[123-168]
src/composables/useFeedbackDiagnostics.test.ts[27-89]
src/components/content/SkillsHub.vue[28-38]
src/components/content/ThreadConversation.vue[700-703]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`buildFeedbackMailto()` currently includes **all** `localStorage`/`sessionStorage` key-values and `document.body.innerText` in the mailto body. This can leak secrets (tokens) and PII into the DOM (`href`) and into user email drafts.
### Issue Context
- Storage snapshots are iterated without filtering/redaction.
- Visible page text capture can include user content.
- Multiple components bind `:href="feedbackMailto"`, so the full encoded payload exists in the DOM even before a click.
- Tests currently assert token inclusion, so they must be updated after redaction.
### Fix Focus Areas
- src/composables/useFeedbackDiagnostics.ts[53-168]
- src/composables/useFeedbackDiagnostics.test.ts[1-90]
- src/components/content/SkillsHub.vue[28-38]
- src/components/content/ThreadConversation.vue[700-704]
### Suggested changes
1. **Redact storage values**:
 - Prefer allowlisting safe keys/prefixes (e.g., `codex-web-local.`), and/or
 - Forbidlist common secret patterns in keys (`token`, `auth`, `secret`, `key`, `session`) and replace values with `[REDACTED]`.
 - Consider emitting only key names + value length instead of full values.
2. **Reduce DOM exposure**:
 - Avoid binding full diagnostics mailto to `href` reactively; use a minimal `href="mailto:..."` and set the full href only in the click handler right before navigation.
3. Update tests to assert redaction behavior (e.g., `codex-token=[REDACTED]` or that the key is omitted).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. .header-git-feedback lacks dark styling ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new Send feedback UI in the git-branch dropdown uses a hard-coded light surface (bg-white)
without a corresponding dark-theme override, likely causing an obvious dark-mode regression. Shared
header UI dark overrides should be centralized in src/style.css.
Code

src/components/content/HeaderGitBranchDropdown.vue[R379-380]

+.header-git-feedback {
+  @apply shrink-0 rounded-full border border-red-200 bg-white px-2 py-0.5 text-[0.65rem] font-semibold text-red-700 transition hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-red-200;
Evidence
Rule 2 requires new/changed UI to render correctly in dark theme, but .header-git-feedback is
introduced with bg-white and no dark override. Rule 3 prefers placing decisive dark-theme
overrides for shared UI surfaces in src/style.css, but this new shared header control has no
corresponding global :root.dark styling.

AGENTS.md
AGENTS.md
src/components/content/HeaderGitBranchDropdown.vue[31-32]
src/components/content/HeaderGitBranchDropdown.vue[72-75]
src/components/content/HeaderGitBranchDropdown.vue[379-380]
src/style.css[290-305]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new shared header feedback button (`.header-git-feedback`) is styled with light-theme colors (`bg-white`, `text-red-700`) but has no dark-theme override, which can render as a bright/light pill on dark backgrounds.
## Issue Context
This PR already adds global dark-theme overrides in `src/style.css` for other feedback buttons (e.g., `.visible-error-feedback`, `.skills-error-feedback`, `.live-overlay-feedback`). The header feedback button should follow the same pattern and be handled globally for consistent route/header theming.
## Fix Focus Areas
- src/components/content/HeaderGitBranchDropdown.vue[379-380]
- src/style.css[290-305]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Oversized mailto payload 🐞 Bug ☼ Reliability
Description
buildFeedbackMailto() no longer truncates the generated body and now appends storage dumps plus
visible page text, which can produce very large mailto URLs that may be truncated or fail to open in
some environments. Because several UIs compute feedbackMailto reactively, generating these large
strings can also add avoidable overhead when diagnostics update.
Code

src/composables/useFeedbackDiagnostics.ts[R150-167]

   `App version: ${appVersion}`,
   `Worktree: ${worktreeName}`,
   '',
+    'Browser/app state',
+    readBrowserStateSnapshot(),
+    '',
   'Recent diagnostics',
   recentDiagnostics || 'No diagnostics captured.',
-  ].join('\n').slice(0, MAX_BODY_CHARS)
+    '',
+    'Visible page text',
+    readVisiblePageText(),
+  ].join('\n')

 const params = new URLSearchParams({
-    subject: `Codex Web feedback: ${entries[0]?.message.slice(0, 80) || 'issue report'}`,
+    subject: `Codex Web feedback: ${normalizeSubjectMessage(entries[0]?.message)}`,
   body,
 })
 return `mailto:${FEEDBACK_EMAIL}?${params.toString()}`
Evidence
The mailto body concatenates full storage/page snapshots without any truncation, and components
compute/bind that output for anchor hrefs.

src/composables/useFeedbackDiagnostics.ts[123-168]
src/components/content/SkillsHub.vue[176-218]
src/components/content/ThreadConversation.vue[902-911]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The mailto body is built with unbounded content (storage snapshot + visible page text) and is not truncated before URL encoding. This risks exceeding practical `mailto:` URL limits and can degrade responsiveness when recomputed.
### Issue Context
`buildFeedbackMailto()` is used via computed values in multiple components (e.g., `:href="feedbackMailto"`), so it can be recomputed whenever diagnostics change.
### Fix Focus Areas
- src/composables/useFeedbackDiagnostics.ts[123-168]
- src/components/content/SkillsHub.vue[176-218]
- src/components/content/ThreadConversation.vue[902-911]
### Suggested changes
1. Reintroduce a conservative size cap (either on raw text or on encoded URL length), e.g.:
 - Limit visible page text to N chars/lines.
 - Limit storage snapshot to M keys and/or per-value max length.
 - Apply a final `body.slice(0, MAX_BODY_CHARS)` (or more robustly, cap after encoding).
2. Prefer computing the full mailto only on click (avoid large reactive `href` values).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Stale CLI error banner ✓ Resolved 🐞 Bug ≡ Correctness
Description
useDesktopState() sets codexCliMissingError when a missing-CLI failure happens, but later
non-CLI failures don’t clear it, so the composer can keep showing “Codex CLI not found…” even when
the current failure is unrelated. This produces incorrect UI state and misleading diagnostics links
until a subsequent successful refresh happens to clear it.
Code

src/composables/useDesktopState.ts[R4420-4424]

} catch (unknownError) {
 error.value = unknownError instanceof Error ? unknownError.message : 'Unknown application error'
+      if (isCodexCliMissingError(unknownError)) {
+        codexCliMissingError.value = CODEX_CLI_MISSING_MESSAGE
+      }
Evidence
refreshAll() sets codexCliMissingError only inside the missing-CLI conditional and never clears
it otherwise, while App.vue renders the banner whenever codexCliMissingError is non-empty.
Therefore, once set, the banner can persist across later unrelated failures until a successful
refresh path clears it.

src/composables/useDesktopState.ts[4396-4425]
src/composables/useDesktopState.ts[1912-1925]
src/App.vue[904-909]
src/server/codexAppServerBridge.ts[4573-4578]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`codexCliMissingError` is only assigned when `isCodexCliMissingError(unknownError)` is true, and is not reset for other errors. This allows a previous missing-CLI error to persist in state and keep the UI banner visible even after the failure mode changes.
### Issue Context
- `refreshAll()` clears `error.value` at the start, but not `codexCliMissingError`.
- `refreshModelPreferences()` clears `codexCliMissingError` only at the *end* of the try block, so if it throws early the stale value can also remain.
### Fix Focus Areas
- src/composables/useDesktopState.ts[1919-1925]
- src/composables/useDesktopState.ts[4396-4425]
### Expected fix
- Clear `codexCliMissingError.value` at the start of `refreshAll()` (and/or ensure the catch block clears it when the error is not the missing-CLI error).
- Move the `codexCliMissingError.value = ''` reset in `refreshModelPreferences()` to before the `try` so it always resets for a new attempt, and in the `catch` explicitly clear it when the error is not a missing-CLI error.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Component-scoped .skills-error-feedback dark override ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
SkillsHub.vue adds a dark-theme override for .skills-error-feedback inside the component instead
of placing the shared dark-theme styling in src/style.css, which can lead to fragmented theme
behavior across route surfaces. This conflicts with the preference for global dark-theme overrides
for larger/shared UIs.
Code

src/components/content/SkillsHub.vue[R531-533]

+:global(:root.dark) .skills-error-feedback {
+  @apply border-rose-800/80 bg-rose-950 text-rose-100 hover:bg-rose-900 focus:ring-rose-700;
}
Evidence
The checklist asks for dark-theme overrides for shared/large UI surfaces to live in src/style.css;
however the PR adds :global(:root.dark) .skills-error-feedback within SkillsHub.vue instead. The
global stylesheet changes in this PR cover other feedback UI (e.g., .visible-error-feedback) but
do not centralize the Skills Hub feedback button override, indicating reliance on component-scoped
theming for this UI.

AGENTS.md
src/components/content/SkillsHub.vue[531-533]
src/style.css[290-296]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A dark-theme override for `.skills-error-feedback` is implemented in a component-scoped style block (`:global(:root.dark) ...`) instead of the global theme stylesheet, which the checklist prefers for shared route surfaces/large feature UI styling.
## Issue Context
Other feedback CTA styling already uses global dark overrides in `src/style.css` (e.g., `.visible-error-feedback`). Consolidating `.skills-error-feedback` dark styling in the same place reduces fragmentation and makes theme behavior easier to audit and keep consistent.
## Fix Focus Areas
- src/components/content/SkillsHub.vue[531-533]
- src/style.css[290-296]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
6. Duplicate visible diagnostics ✓ Resolved 🐞 Bug ≡ Correctness
Description
App.vue records *all* non-empty visible error strings on every change to any tracked error ref, so
persistent errors get re-added repeatedly and quickly evict older diagnostics from the capped list.
This degrades the usefulness of the generated feedback email and can cause the subject to reflect an
unrelated error due to repeated reordering.
Code

src/App.vue[R2015-2022]

+watch(visibleFeedbackErrors, (values) => {
+  for (const value of values) {
+    const message = value.trim()
+    if (message) {
+      recordVisibleFailure(message)
+    }
+  }
+})
Evidence
The watcher iterates every watched error value and records it each time it runs, while diagnostics
storage always prepends and slices without deduplication, so repeated recordings are inevitable once
any tracked ref changes while another stays non-empty.

src/App.vue[1563-1574]
src/App.vue[2015-2022]
src/composables/useFeedbackDiagnostics.ts[49-59]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`watch(visibleFeedbackErrors, ...)` loops over *all* current error values and calls `recordVisibleFailure` for each non-empty one every time *any* watched ref changes. Because `recordFeedbackDiagnostic` always prepends and does not dedupe, this produces repeated identical diagnostics and can push out more relevant items.
### Issue Context
- `visibleFeedbackErrors` is an array of refs.
- Vue `watch` on an array of sources passes `(newValues, oldValues)` arrays.
### Fix Focus Areas
- Compare `newValues[i]` to `oldValues[i]` and only record when a value transitions (e.g., from empty -> non-empty, or message changed).
- Optionally dedupe at the diagnostics layer (e.g., skip if the newest diagnostic has same kind+message).
### Fix Focus Areas (code references)
- src/App.vue[1563-1574]
- src/App.vue[2015-2022]
- src/composables/useFeedbackDiagnostics.ts[49-59]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Unrecorded SkillsHub errors ✓ Resolved 🐞 Bug ◔ Observability
Description
Skills Hub shows “Send feedback” alongside syncStatus.startup.lastError / syncActionError, but
these messages are not recorded as diagnostics when they originate from a successful status payload,
so the generated mailto can contain “No diagnostics captured” and omit the on-screen error. This
breaks the intent of having the visible error state produce reportable diagnostics.
Code

src/components/content/SkillsHub.vue[R26-38]

<span>{{ t('Action') }}: {{ syncStatus.startup.lastAction }}</span>
</div>
<div v-if="syncStatus.startup.lastError" class="skills-sync-error">
-        {{ syncStatus.startup.lastError }}
+        <span>{{ syncStatus.startup.lastError }}</span>
+        <a class="skills-error-feedback" :href="feedbackMailto">{{ t('Send feedback') }}</a>
</div>
<div v-if="syncActionStatus" class="skills-sync-meta">
<span>{{ t('Manual sync') }}: {{ syncActionStatus }}</span>
</div>
<div v-if="syncActionError" class="skills-sync-error">
-        {{ syncActionError }}
+        <span>{{ syncActionError }}</span>
+        <a class="skills-error-feedback" :href="feedbackMailto">{{ t('Send feedback') }}</a>
</div>
Evidence
SkillsHub displays startup/sync errors with a feedback link, but the Skills Sync status loader can
populate startup.lastError from a successful response (no fetch diagnostic recorded), and the
global visible-error watcher in App.vue doesn’t track these SkillsHub error refs; therefore the
feedback mailto can be generated without including that visible error message.

src/components/content/SkillsHub.vue[24-38]
src/components/content/SkillsHub.vue[147-179]
src/composables/useGithubSkillsSync.ts[5-13]
src/composables/useGithubSkillsSync.ts[78-86]
src/App.vue[1563-1574]
src/composables/useFeedbackDiagnostics.ts[134-146]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
SkillsHub renders a feedback link for `syncStatus.startup.lastError` and `syncActionError`, but those strings may be present without any fetch failure (e.g., `loadSyncStatus` sets them from a successful response payload). In that case, the global fetch wrapper won’t record anything and `App.vue`’s visible-error watcher doesn’t include these refs, so the feedback email can be missing the visible error text.
### Issue Context
- `loadSyncStatus` assigns `syncStatus.value = payload.data` when `resp.ok`.
- `installFeedbackDiagnostics` only records for fetch throw / `!response.ok`.
- `App.vue`’s `visibleFeedbackErrors` list does not include SkillsHub’s `syncStatus.startup.lastError` / `syncActionError`.
### Fix Focus Areas
- In `SkillsHub.vue`, import `recordVisibleFailure` from `useFeedbackDiagnostics()` and watch the relevant error sources (`syncStatus.startup.lastError`, `syncActionError`, `skillSearchError`, `error`) and record when they become non-empty.
- Alternatively, wrap the feedback action so clicking “Send feedback” first calls `recordVisibleFailure(theVisibleErrorString)` before navigating.
- Ensure you only record on transitions/changes to avoid duplicates (same pattern as App.vue fix).
### Fix Focus Areas (code references)
- src/components/content/SkillsHub.vue[24-38]
- src/components/content/SkillsHub.vue[147-179]
- src/composables/useGithubSkillsSync.ts[5-13]
- src/composables/useGithubSkillsSync.ts[78-86]
- src/App.vue[1563-1574]
- src/composables/useFeedbackDiagnostics.ts[134-146]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

8. Multiline mailto subject 🐞 Bug ☼ Reliability
Description
buildFeedbackMailto() uses entries[0].message directly in the email subject; because diagnostics
can record multiline strings (e.g., error stacks), the subject can contain encoded newlines and
produce messy/less-readable subjects in some mail clients. This is avoidable by normalizing the
subject to a single line before slicing.
Code

src/composables/useFeedbackDiagnostics.ts[R106-110]

+  const params = new URLSearchParams({
+    subject: `Codex Web feedback: ${entries[0]?.message.slice(0, 80) || 'issue report'}`,
+    body,
+  })
+  return `mailto:${FEEDBACK_EMAIL}?${params.toString()}`
Evidence
The diagnostics pipeline can store multiline strings because it records Error.stack when present,
and buildFeedbackMailto() uses the diagnostic message directly in the subject. Therefore the
subject can include line breaks (after decoding) and become hard to read.

src/composables/useFeedbackDiagnostics.ts[24-32]
src/composables/useFeedbackDiagnostics.ts[72-111]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The generated `mailto:` subject is built from the newest diagnostic message without stripping `\r`/`\n`. Since diagnostics may store multiline messages, the subject may include encoded line breaks, leading to poor subject rendering in some mail clients.
### Issue Context
- `normalizeMessage()` prefers `Error.stack`, which is commonly multiline.
- `buildFeedbackMailto()` slices the message but does not first normalize it to a single-line subject.
### Fix Focus Areas
- src/composables/useFeedbackDiagnostics.ts[24-32]
- src/composables/useFeedbackDiagnostics.ts[72-111]
### Expected fix
- Derive a `subjectMessage` from `entries[0]?.message` by taking the first line (split on `/\r?\n/`) and collapsing whitespace, then slice to 80 chars.
- Keep the body as-is (multiline is desirable there).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@friuns2 friuns2 marked this pull request as draft May 12, 2026 13:01
@friuns2 friuns2 marked this pull request as ready for review May 12, 2026 13:01
@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 12, 2026

Persistent review updated to latest commit 585b98e

Comment thread src/components/content/HeaderGitBranchDropdown.vue
@friuns2 friuns2 force-pushed the codex/error-feedback-diagnostics branch from 5aa5ad0 to cdcc143 Compare May 12, 2026 21:43
@friuns2
Copy link
Copy Markdown
Owner Author

friuns2 commented May 12, 2026

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 12, 2026

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@friuns2
Copy link
Copy Markdown
Owner Author

friuns2 commented May 12, 2026

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 12, 2026

Persistent review updated to latest commit b6593ff

Comment on lines +53 to +97
function readVisiblePageText(): string {
if (typeof document === 'undefined') return 'unknown'
const text = document.body?.innerText
.replace(/\r\n/g, '\n')
.replace(/[ \t]+/g, ' ')
.replace(/\n{3,}/g, '\n\n')
.trim() ?? ''
if (!text) return 'No visible page text captured.'
return text
}

function normalizeStorageValue(value: string): string {
return value
.replace(/\r\n/g, '\n')
.replace(/[ \t]+/g, ' ')
.trim()
}

function readStorageSnapshot(storage: Storage | undefined, label: string): string {
if (!storage) return `${label}: unavailable`
try {
const rows: string[] = []
for (let index = 0; index < storage.length; index += 1) {
const key = storage.key(index)
if (!key) continue
rows.push(`${key}=${normalizeStorageValue(storage.getItem(key) ?? '')}`)
}
return `${label}:\n${rows.join('\n') || 'empty'}`
} catch (error) {
return `${label}: unavailable (${normalizeSubjectMessage(normalizeMessage(error))})`
}
}

function readBrowserStateSnapshot(): string {
if (typeof window === 'undefined') return 'unknown'
return [
`Path: ${window.location.pathname || '/'}`,
`Hash: ${window.location.hash || '(none)'}`,
`Search: ${window.location.search || '(none)'}`,
`Online: ${typeof navigator === 'undefined' ? 'unknown' : String(navigator.onLine)}`,
`Language: ${typeof navigator === 'undefined' ? 'unknown' : navigator.language}`,
`Platform: ${typeof navigator === 'undefined' ? 'unknown' : navigator.platform}`,
readStorageSnapshot(window.localStorage, 'localStorage'),
readStorageSnapshot(window.sessionStorage, 'sessionStorage'),
].join('\n')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Secrets in feedback mailto 🐞 Bug ⛨ Security

buildFeedbackMailto() captures full localStorage/sessionStorage contents and visible page text and
encodes them into the mailto body, which can expose auth tokens/PII in the email draft and directly
in rendered link href attributes. The test suite explicitly verifies a token-like key
("codex-token") is included, confirming the leakage path.
Agent Prompt
### Issue description
`buildFeedbackMailto()` currently includes **all** `localStorage`/`sessionStorage` key-values and `document.body.innerText` in the mailto body. This can leak secrets (tokens) and PII into the DOM (`href`) and into user email drafts.

### Issue Context
- Storage snapshots are iterated without filtering/redaction.
- Visible page text capture can include user content.
- Multiple components bind `:href="feedbackMailto"`, so the full encoded payload exists in the DOM even before a click.
- Tests currently assert token inclusion, so they must be updated after redaction.

### Fix Focus Areas
- src/composables/useFeedbackDiagnostics.ts[53-168]
- src/composables/useFeedbackDiagnostics.test.ts[1-90]
- src/components/content/SkillsHub.vue[28-38]
- src/components/content/ThreadConversation.vue[700-704]

### Suggested changes
1. **Redact storage values**:
   - Prefer allowlisting safe keys/prefixes (e.g., `codex-web-local.`), and/or
   - Forbidlist common secret patterns in keys (`token`, `auth`, `secret`, `key`, `session`) and replace values with `[REDACTED]`.
   - Consider emitting only key names + value length instead of full values.
2. **Reduce DOM exposure**:
   - Avoid binding full diagnostics mailto to `href` reactively; use a minimal `href="mailto:..."` and set the full href only in the click handler right before navigation.
3. Update tests to assert redaction behavior (e.g., `codex-token=[REDACTED]` or that the key is omitted).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@friuns2 friuns2 merged commit 1c9dacd into main May 12, 2026
Sign up for free to 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