You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* fix(template): neutralize feed-controlled note injection
Feed-controlled fields are written into generated notes by the template
engines without escaping, letting a malicious RSS feed inject Markdown,
external/tracking images, phishing links, Obsidian wikilinks, and
(speculatively) executable Dataview code blocks into a reader's vault.
- Escape raw {{title}}/{{author}} via sanitizeInlineText: collapse control
characters to spaces (no line/block breakout from `# {{title}}`) and
neutralize the link/image/inline-code/raw-HTML metacharacters, while
keeping ordinary punctuation readable.
- Percent-encode all URL tags (artwork/stream/url/feedurl/feed*/episode*)
via a broadened sanitizeUrlForTemplate so a crafted value cannot break
out of ``/`[](...)`, inject a line, or terminate a quoted YAML
scalar. {{url}}/{{episodeurl}} still pass a trusted local-file vault
wikilink through verbatim (isLocalFile gate).
- Strip `[` and `]` in replaceIllegalFileNameCharactersInString so a feed
<title> cannot inject a wikilink through {{podcast}}/{{safetitle}}/
{{podcastlink}} (getFeedNoteWikilink).
- Render injected Dataview code fences (```dataviewjs/```dataview, ~~~,
any blockquote/callout/list container) inert via feedHtmlToMarkdown.
Legitimate show-note rich text (links, images, normal code blocks) is
preserved by design.
Applied consistently across NoteTemplateEngine, FeedNoteTemplateEngine,
and TranscriptTemplateEngine. Adds unit tests proving each malicious input
is neutralized and legitimate input is preserved.
Fixes deepsec findings other-markdown-injection and other-wikilink-injection.
* fix(template): gate local-file url passthrough on plugin-set filePath
Address PR #228 review (Codex P1): isLocalFile() alone keys off
podcastName === "local file", which the feed parser sets from the channel
<title>. A feed titled exactly "local file" would make NoteTemplateEngine /
TranscriptTemplateEngine treat its episode as a trusted local file and skip
sanitizeUrlForTemplate for {{url}}/{{episodeurl}}, bypassing the injection
fix on the bare {{url}} line.
Gate the verbatim passthrough on episode.filePath in addition to
isLocalFile: filePath is set only by getContextMenuHandler for genuine local
files and never by feedParser, so a feed cannot forge it. Factored into a
resolveEpisodeUrl helper shared by both engines. Adds a regression test for
the forged-"local file" feed title and updates local-file fixtures to carry
the realistic filePath.
// Stays a single line: the title can no longer inject extra blocks/headings.
730
+
expect(rendered.split("\n")).toHaveLength(1);
731
+
// The image marker is escaped so it cannot load an external (tracking) image.
732
+
expect(rendered).not.toMatch(/!\[pixel\]\(/);
733
+
expect(rendered).toContain("\\[pixel\\]");
734
+
});
735
+
736
+
it("renders an injected ```dataviewjs code block inert while keeping normal formatting",()=>{
737
+
constmalicious: Episode={
738
+
...demoEpisode,
739
+
// htmlToMarkdown is a passthrough in the test mock, so this is what a feed
740
+
// <content:encoded> would yield after conversion.
741
+
content:
742
+
"```dataviewjs\napp.vault.getFiles().forEach(f => f.unlink())\n```\n\nSome [docs](https://example.com) and a normal block:\n\n```js\nconsole.log(1)\n```",
0 commit comments