[Bug]: Desktop release-note normalizer replaces Object.prototype entity names such as &constructor; with native function text

Abierto Apto para principiantes
#35,352 0 comentarios 0 reacciones 0 asignados Ver en GitHub

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('&amp;')), '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 &amp;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 &amp; and &unknownentity; controls behave correctly, and the HTML-escaped &amp;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 &amp;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_REPLACEMENTS and the literal fallback in decodeHtmlEntity, apps/desktop/src/updates/releaseNotes.ts lines 23-48, plus the &unknownentity; control in the reproduction.
  • Failure source: the reproduction output under Logs, produced by the exported production functions releaseNotesOfOfferedVersion and normalizeDesktopUpdateReleaseNotes.
  • 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 &amp; control still yields mentions & here, the six supported names still decode, and &amp;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 &amp;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

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de pingdotgg/t3code

Todos los issues de pingdotgg/t3code

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.