[Bug]: Desktop release-note normalizer replaces Object.prototype entity names such as &constructor; with native function text
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 78/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- electron, typescript
- 領域
- desktop
調査の方向性
apps/desktop/src/updates/releaseNotes.ts と対応するテストファイル apps/desktop/src/updates/releaseNotes.test.ts を読みます。issue では decodeHtmlEntity と継承プロパティの検索が指摘されています。既存のリリースノートテストを実行してから、サポート対象のすべてのノート形式について、継承された名前のテストを追加します。それらの名前がそのまま保持され、サポート対象のエンティティは引き続きデコードされ、未知のエンティティは変更されずに残ることを完了条件とします。
索引モデルが issue の本文から書いたものです。
説明
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.
- 主要言語
- TypeScript
- スター
- 24.8k
- フォーク
- 6.4k
- 平均マージ
- 4時間 16分
- マージ済み PR(30日)
- 352
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
pingdotgg/t3code のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
pingdotgg/t3code の issue をすべて見る
似ている issue
-
Add "append" and "prepend" slots for vue-vuetify extended renderers対応中かも @simeyc が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
eclipsesource/jsonforms#2668 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
README の通知条件を実際の判定処理に合わせる対応中かも @unhappychoice が今日担当しました。 オープンfactory:in-progress priority:medium
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
unhappychoice/oss-issue-opener#395 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
bug Website
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
OWASP/OWASP-Hunting#88 ·
メンテナーはふだん 1 日以内に返信