mirror of
https://github.com/tmlnv/obsidian-telegram-bridge.git
synced 2026-07-22 06:53:13 +00:00
fix: sanitize path separators per-variable in template expansion
Sanitize each template variable value individually in path mode so slashes inside values (e.g. URLs in content) become _ instead of injecting unintended subdirectories, while preserving the template's own literal / separators as real path structure.
This commit is contained in:
parent
b70131db90
commit
1217b9fe71
2 changed files with 50 additions and 20 deletions
|
|
@ -4,11 +4,10 @@ interface ExpandOptions {
|
|||
isPath?: boolean;
|
||||
}
|
||||
|
||||
function sanitizePath(value: string): string {
|
||||
return value
|
||||
.replace(/[\\:*?"<>|\n\r]/g, "_")
|
||||
.replace(/\/+/g, "/")
|
||||
.replace(/(^\/|\/$)/g, "");
|
||||
const PATH_UNSAFE_CHARS = /[\\/:*?"<>|\n\r]/g;
|
||||
|
||||
function sanitizeForPath(value: string): string {
|
||||
return value.replace(PATH_UNSAFE_CHARS, "_");
|
||||
}
|
||||
|
||||
function normalizeTemplatePath(value: string): string {
|
||||
|
|
@ -53,8 +52,12 @@ function getChatLabel(message: MessageRow): string {
|
|||
return message.telegram_chat_title ?? String(message.telegram_chat_id);
|
||||
}
|
||||
|
||||
function getTopicLabel(message: MessageRow): string {
|
||||
return message.topic_name ? `${message.topic_name}/` : "";
|
||||
function getTopicLabel(message: MessageRow, isPath: boolean): string {
|
||||
if (!message.topic_name) {
|
||||
return "";
|
||||
}
|
||||
const name = isPath ? sanitizeForPath(message.topic_name) : message.topic_name;
|
||||
return `${name}/`;
|
||||
}
|
||||
|
||||
function getUserLabel(message: MessageRow): string {
|
||||
|
|
@ -95,27 +98,30 @@ export function expandTemplate(
|
|||
message: MessageRow,
|
||||
options: ExpandOptions = {},
|
||||
): string {
|
||||
const isPath = options.isPath ?? false;
|
||||
const safe = (value: string): string => (isPath ? sanitizeForPath(value) : value);
|
||||
|
||||
const content = getContent(message);
|
||||
let result = template;
|
||||
|
||||
result = result.replace(/\{\{messageDate:([^}]+)\}\}/g, (_match, format) =>
|
||||
formatDate(message.telegram_date, format),
|
||||
safe(formatDate(message.telegram_date, format)),
|
||||
);
|
||||
result = result.replace(/\{\{content(?::(\d+))?\}\}/g, (_match, length) =>
|
||||
sliceContent(content, length),
|
||||
safe(sliceContent(content, length)),
|
||||
);
|
||||
result = result.replace(/\{\{chatId\}\}/g, String(message.telegram_chat_id));
|
||||
result = result.replace(/\{\{chat\}\}/g, getChatLabel(message));
|
||||
result = result.replace(/\{\{topicId\}\}/g, message.topic_id ? String(message.topic_id) : "");
|
||||
result = result.replace(/\{\{topic\}\}/g, getTopicLabel(message));
|
||||
result = result.replace(/\{\{user\}\}/g, getUserLabel(message));
|
||||
result = result.replace(/\{\{messageId\}\}/g, String(message.telegram_message_id));
|
||||
result = result.replace(/\{\{messageType\}\}/g, message.message_type);
|
||||
result = result.replace(/\{\{file:name\}\}/g, getFileBaseName(message));
|
||||
result = result.replace(/\{\{file:extension\}\}/g, getFileExtension(message));
|
||||
result = result.replace(/\{\{chatId\}\}/g, safe(String(message.telegram_chat_id)));
|
||||
result = result.replace(/\{\{chat\}\}/g, safe(getChatLabel(message)));
|
||||
result = result.replace(/\{\{topicId\}\}/g, message.topic_id ? safe(String(message.topic_id)) : "");
|
||||
result = result.replace(/\{\{topic\}\}/g, getTopicLabel(message, isPath));
|
||||
result = result.replace(/\{\{user\}\}/g, safe(getUserLabel(message)));
|
||||
result = result.replace(/\{\{messageId\}\}/g, safe(String(message.telegram_message_id)));
|
||||
result = result.replace(/\{\{messageType\}\}/g, safe(message.message_type));
|
||||
result = result.replace(/\{\{file:name\}\}/g, safe(getFileBaseName(message)));
|
||||
result = result.replace(/\{\{file:extension\}\}/g, safe(getFileExtension(message)));
|
||||
|
||||
if (options.isPath) {
|
||||
return normalizeTemplatePath(sanitizePath(result));
|
||||
if (isPath) {
|
||||
return normalizeTemplatePath(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -61,4 +61,28 @@ describe("expandTemplate", () => {
|
|||
it("supports content slicing", () => {
|
||||
expect(expandTemplate("{{content:5}}", createMessage())).toBe("hello");
|
||||
});
|
||||
|
||||
it("strips path separators from variable values in path mode", () => {
|
||||
expect(
|
||||
expandTemplate(
|
||||
"Telegram/{{chat}}/{{content:30}}.md",
|
||||
createMessage({
|
||||
telegram_chat_title: "Saved",
|
||||
topic_id: null,
|
||||
topic_name: null,
|
||||
text_content: "https://radio.garden/something",
|
||||
}),
|
||||
{ isPath: true },
|
||||
),
|
||||
).toBe("Telegram/Saved/https___radio.garden_something.md");
|
||||
});
|
||||
|
||||
it("preserves path separators in non-path mode", () => {
|
||||
expect(
|
||||
expandTemplate(
|
||||
"see {{content}}",
|
||||
createMessage({ text_content: "https://radio.garden/x" }),
|
||||
),
|
||||
).toBe("see https://radio.garden/x");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue