-
Notifications
You must be signed in to change notification settings - Fork 464
Preserve HTTPS angle-bracket autolinks in safe-output sanitization #48169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0eddda9
b965f41
b80b29a
fb2851a
58edd98
5144310
f71cd08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -441,9 +441,9 @@ describe("sanitize_content.cjs", () => { | |
| }); | ||
|
|
||
| it("should move title into link text for inline link with angle-bracket URL", () => { | ||
| // Note: convertXmlTags runs after neutralizeMarkdownLinkTitles and converts <url> to (url) | ||
| // Angle-bracket HTTPS autolinks are preserved so CommonMark/Slack link syntax survives sanitization. | ||
| const result = sanitizeContent('[click here](<https://github.com/path> "injected payload")'); | ||
| expect(result).toBe("[click here (injected payload)]((https://github.com/path))"); | ||
| expect(result).toBe("[click here (injected payload)](<https://github.com/path>)"); | ||
| }); | ||
|
|
||
| it("should move multiple link titles into link text in the same content", () => { | ||
|
|
@@ -581,6 +581,43 @@ describe("sanitize_content.cjs", () => { | |
| expect(result).not.toContain("onerror"); | ||
| }); | ||
|
|
||
| it("should preserve HTTPS angle-bracket autolinks", () => { | ||
| const input = "Tracking issue: <https://github.com/octo-org/octo-repo/issues/123>"; | ||
| expect(sanitizeContent(input)).toBe(input); | ||
| }); | ||
|
|
||
| it("should preserve Slack mrkdwn links on allowed HTTPS domains", () => { | ||
| const input = "Tracking issue: <https://github.com/octo-org/octo-repo/issues/123|Build failure — Build github/gh-aw#456>"; | ||
| expect(sanitizeContent(input)).toBe(input); | ||
| }); | ||
|
|
||
| it("should not preserve IPv6 angle-bracket links", () => { | ||
| // IPv6 authority is not a simple [\w.-]+ hostname — treat as unknown tag | ||
| const result = sanitizeContent("<https://[2001:db8::1]/path>"); | ||
| expect(result).not.toContain("<https://[2001:db8::1]"); | ||
| }); | ||
|
|
||
| it("should not preserve userinfo angle-bracket links", () => { | ||
| // Userinfo (user@host) must not bypass domain filtering | ||
| const result = sanitizeContent("<https://github.com@evil.example/path>"); | ||
| expect(result).not.toContain("<https://github.com@evil.example"); | ||
| }); | ||
|
|
||
| it("should redact Slack mrkdwn links with disallowed domains", () => { | ||
| // Domain is blocked — URL is redacted but label text is preserved so the | ||
| // author's visible link text is not silently lost. | ||
| const result = sanitizeContent("<https://evil.example.com/path|Build failure>"); | ||
| expect(result).toContain("evil.example.com/redacted"); // domain shows in redacted form | ||
| expect(result).toContain("Build failure"); // label is preserved | ||
| expect(result).not.toContain("<https://evil.example.com"); // not in original angle-bracket form | ||
| }); | ||
|
|
||
| it("should redact plain angle-bracket autolinks with disallowed domains", () => { | ||
| const result = sanitizeContent("<https://evil.example.com/path>"); | ||
| expect(result).toContain("evil.example.com/redacted"); // domain shows in redacted form | ||
| expect(result).not.toContain("<https://evil.example.com"); // not in original angle-bracket form | ||
| }); | ||
|
|
||
| it("should handle CDATA sections", () => { | ||
| const result = sanitizeContent("<![CDATA[<script>alert('xss')</script>]]>"); | ||
| expect(result).toBe("(![CDATA[(script)alert('xss')(/script)]])"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no test covering a Slack mrkdwn link whose domain is not in the allowlist (e.g. @copilot please address this. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[/tdd] No test covers the disallowed-domain path for Slack mrkdwn links — the label-loss defect (line 328 in
sanitize_content_core.cjs) is invisible without it.💡 Add a companion test for the disallowed-domain case
Without this test the current code silently discards
Build failure, and a future refactor has no safety net.@copilot please address this.