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

Đang mở Phù hợp với người mới
#35,352 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
78/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
electron, typescript
Lĩnh vực
desktop

Hướng nghiên cứu

Đọc apps/desktop/src/updates/releaseNotes.ts và tệp kiểm thử đi kèm apps/desktop/src/updates/releaseNotes.test.ts; issue chỉ ra decodeHtmlEntity và việc tra cứu thuộc tính kế thừa. Chạy các kiểm thử ghi chú phát hành hiện có, sau đó bổ sung kiểm thử cho các tên kế thừa trên mọi dạng ghi chú được hỗ trợ. Công việc hoàn tất khi các tên đó vẫn giữ nguyên dạng ký tự, các thực thể được hỗ trợ vẫn được giải mã và các thực thể không xác định vẫn không thay đổi.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.

Ngôn ngữ chính
TypeScript
Star
24.8k
Fork
6.4k
Merge trung bình
4 giờ 16 phút
Pull request đã merge (30 ngày)
352

Chuẩn bị môi trường

Mở trong Codespaces

Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của pingdotgg/t3code

Tất cả issue của pingdotgg/t3code

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.