[Bug]: Desktop release-note normalizer replaces Object.prototype entity names such as &constructor; with native function text
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('&')), '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.
- 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
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.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của pingdotgg/t3code
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 67/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[Bug]: t3_thread_read drops the typed failure (usage_limit class and resetAt) on error itemsĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của pingdotgg/t3code
Issue tương tự
-
Add "append" and "prepend" slots for vue-vuetify extended renderersCó thể đã có người làm @simeyc đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
eclipsesource/jsonforms#2668 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
cobot/zapier#520 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 1 ngày
-
README の通知条件を実際の判定処理に合わせるCó thể đã có người làm @unhappychoice đã nhận hôm nay. Đang mởfactory:in-progress priority:medium
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
unhappychoice/oss-issue-opener#395 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug Website
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
OWASP/OWASP-Hunting#88 ·
Maintainer thường phản hồi trong vòng 1 ngày