[Bug]: Desktop release-note normalizer replaces Object.prototype entity names such as &constructor; with native function text
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 78/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- electron, typescript
- Área
- desktop
Línea de trabajo
Lee apps/desktop/src/updates/releaseNotes.ts y su archivo de pruebas correspondiente apps/desktop/src/updates/releaseNotes.test.ts; el issue señala decodeHtmlEntity y la consulta de propiedades heredadas. Ejecuta las pruebas existentes de notas de la versión y luego añade cobertura para nombres heredados en todos los formatos de nota admitidos. El trabajo estará terminado cuando esos nombres permanezcan literales, las entidades admitidas sigan decodificándose y las entidades desconocidas permanezcan sin cambios.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/desktop
Steps to reproduce
Use a checkout of the baseline commit under Version or commit with its locked dependencies installed. From the checkout root, run the script below with env -u NODE_ENV sh repro.sh. It sets a private HOME, TMPDIR and T3CODE_HOME, then passes synthetic release notes through releaseNotesOfOfferedVersion and normalizeDesktopUpdateReleaseNotes from apps/desktop/src/updates/releaseNotes.ts, the two functions handleUpdateAvailable in apps/desktop/src/updates/DesktopUpdates.ts (lines 926-959) applies to an update-available event. It covers the four accepted note shapes (Markdown string, HTML string, string with a Full changelog summary, tagged array) with & and &unknownentity; controls. It does not start the desktop app. The script exits 0 when the controls are correct and every inherited name shows the substitution, so its assertions pin the current defect.
Reproduction script: repro.sh
#!/bin/sh
set -eu
checkout=$PWD
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT HUP INT TERM
mkdir -p "$tmp/userhome" "$tmp/tmp" "$tmp/t3"
export HOME="$tmp/userhome" TMPDIR="$tmp/tmp" T3CODE_HOME="$tmp/t3"
cd "$checkout/apps/desktop"
env -u NODE_ENV node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { normalizeDesktopUpdateReleaseNotes, releaseNotesOfOfferedVersion } from './src/updates/releaseNotes.ts';
const render = (note) => normalizeDesktopUpdateReleaseNotes(
releaseNotesOfOfferedVersion({ version: '1.0.0', releaseName: 'T3 Code v1.0.0', releaseNotes: note }),
'1.0.0', 'latest',
).releaseNotes[0]?.items[0];
const shapes = {
markdown: (text) => `- mentions ${text} here`,
html: (text) => `<ul><li>mentions ${text} here</li></ul>`,
summary: (text) => `- mentions ${text} here\n<details><summary>Full changelog</summary>ignored</details>`,
tagged: (text) => [{ version: '1.0.0', note: `- mentions ${text} here` }],
};
const inherited = ['constructor', 'toString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString'];
for (const [shape, wrap] of Object.entries(shapes)) {
// Controls: a supported entity decodes, an unsupported one stays literal.
assert.equal(render(wrap('&')), 'mentions & here');
assert.equal(render(wrap('&unknownentity;')), 'mentions &unknownentity; here');
for (const name of inherited) {
const actual = render(wrap(`&${name};`));
console.log(JSON.stringify({ shape, entity: name, actual }));
// Pins the current defect: the inherited Object.prototype function's source text replaces the entity.
assert.equal(actual, `mentions ${String(Object.prototype[name])} here`);
}
}
// An HTML-escaped entity stays literal after one decode.
assert.equal(render('<ul><li>mentions &constructor; here</li></ul>'), 'mentions &constructor; here');
console.log('controls passed; inherited-name substitution reproduced in all four input shapes');
JS
Expected behavior
HTML_ENTITY_REPLACEMENTS in apps/desktop/src/updates/releaseNotes.ts (lines 23-30) lists six supported names (amp, apos, gt, lt, nbsp, quot), and decodeHtmlEntity (lines 39-48) returns &${entity}; unchanged for any other non-numeric name. The &unknownentity; control follows that contract. &constructor; and the other inherited names are not supported entities, so each should stay literal: mentions &constructor; here.
Actual behavior
Summary. The desktop update release-note normalizer decodes six named HTML entities and is meant to leave any other named entity literal. An entity whose name matches an inherited Object.prototype property (&constructor;, &toString;, &valueOf;, &hasOwnProperty;, &isPrototypeOf;, &propertyIsEnumerable;, &toLocaleString;) is instead replaced by that function's source text, for example function Object() { [native code] }. This was reproduced with synthetic release-note input through the exported production functions; no published T3 Code release note was found that triggers it, so this is a latent cosmetic formatting defect rather than an observed incident.
Every inherited name in every input shape normalizes to the inherited function's source text, for example mentions function Object() { [native code] } here for &constructor; and mentions function toString() { [native code] } here for &toString;. The & and &unknownentity; controls behave correctly, and the HTML-escaped &constructor; correctly yields literal &constructor;. String.replace coerces the inherited function to text; the substitution does not call the function and does not show code execution. The sidebar release-notes popover (apps/web/src/components/sidebar/SidebarUpdateReleaseNotes.tsx, lines 120-136) renders the stored items as text, so the corrupted string would be displayed as text.
Reachability is conditional. The production updater reads T3 Code's own maintainer-controlled GitHub release feed, whose entries electron-updater passes as HTML. A read-only sample of eight stable release bodies (v0.1.2 through v0.1.9) and six recent nightlies, read as Markdown through the GitHub releases API, contained no alphabetic &name; entity at all. That sample is not the complete release history and does not inspect the rendered Atom HTML. If GitHub escapes & when it renders a release note to the feed, a note that mentions &constructor; reaches the decoder as &constructor; and displays correctly; how the feed renders such text was not checked. No packaged desktop app or live update feed was exercised.
Code locator, as a breadcrumb rather than a prescribed fix: decodeHtmlEntity reads HTML_ENTITY_REPLACEMENTS[entity] on an ordinary object literal (line 40), so the lookup also finds properties inherited from Object.prototype, and line 41 returns the truthy function.
Evidence
- Expected source:
HTML_ENTITY_REPLACEMENTSand the literal fallback indecodeHtmlEntity,apps/desktop/src/updates/releaseNotes.tslines 23-48, plus the&unknownentity;control in the reproduction. - Failure source: the reproduction output under Logs, produced by the exported production functions
releaseNotesOfOfferedVersionandnormalizeDesktopUpdateReleaseNotes. - Evidence provenance: observed
- Local verification: reproduced
- Reproduction completeness: complete
The script above exited 0 from a clean checkout at the baseline. An independent verification run of an equivalent probe also reproduced it on the earlier upstream main commit named under Version or commit. The existing apps/desktop/src/updates/releaseNotes.test.ts passes (14 tests) and has no case for an inherited entity name.
Related
None of these covers this substitution: #32827 and #32832 concern stable/nightly release-note attribution, and #24358 concerns Full Changelog footer filtering.
Restoration check
With the script's input, each of &constructor;, &toString;, &valueOf;, &hasOwnProperty;, &isPrototypeOf;, &propertyIsEnumerable; and &toLocaleString; normalizes to mentions &<name>; here in all four shapes, matching the &unknownentity; control. (The script's inherited-name assertion encodes the current defect and will fail once it is fixed.) The & control still yields mentions & here, the six supported names still decode, and &constructor; still yields literal &constructor;.
Impact
Cosmetic issue
Version or commit
Commit d9805b2ca667be7caec8c9035950564ab96704c3 (desktop package version 0.1.9); also reproduced at upstream main 4bf5d53d93eedc45ca67d4042a33ca974a8ef567. releaseNotes.ts is byte-identical at the later main commit 4fd40e1a16e3800418b8f6a32932b68179098c8d, where the probe was not rerun.
Environment
macOS on arm64, Node v26.12.1. Exported production-function probe only; no packaged desktop app, browser or update installation was exercised.
Logs or stack traces
Excerpt of the script's output; each of the four shapes prints the same seven lines:
{"shape":"html","entity":"constructor","actual":"mentions function Object() { [native code] } here"}
{"shape":"html","entity":"toString","actual":"mentions function toString() { [native code] } here"}
{"shape":"html","entity":"valueOf","actual":"mentions function valueOf() { [native code] } here"}
{"shape":"html","entity":"hasOwnProperty","actual":"mentions function hasOwnProperty() { [native code] } here"}
{"shape":"html","entity":"isPrototypeOf","actual":"mentions function isPrototypeOf() { [native code] } here"}
{"shape":"html","entity":"propertyIsEnumerable","actual":"mentions function propertyIsEnumerable() { [native code] } here"}
{"shape":"html","entity":"toLocaleString","actual":"mentions function toLocaleString() { [native code] } here"}
controls passed; inherited-name substitution reproduced in all four input shapes
Screenshots, recordings, or supporting files
No response
Workaround
For maintainer-authored release notes, the tested HTML-escaped form &constructor; preserves literal &constructor;. No published release was found that shows the substitution, so no end-user workaround is needed on current evidence.
- Lenguaje dominante
- TypeScript
- Estrellas
- 24.8k
- Forks
- 6.4k
- Merge medio
- 4 h 16 min
- PR fusionados (30 d)
- 352
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de pingdotgg/t3code
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 67/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
[Bug]: t3_thread_read drops the typed failure (usage_limit class and resetAt) on error itemsAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
Los mantenedores suelen responder en 1 día
Todos los issues de pingdotgg/t3code
Issues similares
-
Add "append" and "prepend" slots for vue-vuetify extended renderersPosiblemente ocupada @simeyc la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
eclipsesource/jsonforms#2668 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
cobot/zapier#520 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
README の通知条件を実際の判定処理に合わせるPosiblemente ocupada @unhappychoice la tomó hoy. Abiertofactory:in-progress priority:medium
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100
unhappychoice/oss-issue-opener#395 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
bug Website
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
OWASP/OWASP-Hunting#88 ·
Los mantenedores suelen responder en 1 día