mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-10-14 03:51:41 +00:00
Fix 9814 file system paths "pragma: \ define" tiddler (#9815)
* New test for all "forbidden" directory characters * Fix for 9814 Pragma: \define creates dir problem * improve handling of tiddlers in root/community directory * Add ChangeNote * Apply suggestion from @saqimtiaz * Apply suggestion from @saqimtiaz --------- Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
This commit is contained in:
co-authored by
Saq Imtiaz
parent
97e27d0dad
commit
abf8ffdbca
@@ -217,6 +217,11 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
|
||||
fileInfo.isEditableFile = true;
|
||||
fileInfo.originalpath = options.fileInfo.originalpath;
|
||||
}
|
||||
// Propagate the pinFilepath flag (so generateTiddlerFilepath can short-circuit
|
||||
// FileSystemPaths and write back to the original location)
|
||||
if(options.fileInfo && !!options.fileInfo.pinFilepath) {
|
||||
fileInfo.pinFilepath = true;
|
||||
}
|
||||
// Check if the tiddler has any unsafe fields that can't be expressed in a .tid or .meta file: containing control characters, or leading/trailing whitespace
|
||||
var hasUnsafeFields = false;
|
||||
$tw.utils.each(tiddler.getFieldStrings(),function(value,fieldName) {
|
||||
@@ -319,9 +324,16 @@ exports.generateTiddlerFilepath = function(title,options) {
|
||||
extension = options.extension || "",
|
||||
originalpath = (options.fileInfo && options.fileInfo.originalpath) ? options.fileInfo.originalpath : "",
|
||||
overwrite = options.fileInfo && options.fileInfo.overwrite || false,
|
||||
pinFilepath = !!(options.fileInfo && options.fileInfo.pinFilepath),
|
||||
filepath;
|
||||
// If the tiddler's filepath is pinned via tiddlywiki.files, the originalpath
|
||||
// wins and the path filters are skipped entirely.
|
||||
if(pinFilepath && originalpath) {
|
||||
var pinnedExt = path.extname(originalpath);
|
||||
filepath = originalpath.substring(0,originalpath.length - pinnedExt.length);
|
||||
}
|
||||
// Check if any of the pathFilters applies
|
||||
if(options.pathFilters && options.wiki) {
|
||||
if(!filepath && options.pathFilters && options.wiki) {
|
||||
$tw.utils.each(options.pathFilters,function(filter) {
|
||||
if(!filepath) {
|
||||
var source = options.wiki.makeTiddlerIterator([title]),
|
||||
@@ -352,8 +364,10 @@ exports.generateTiddlerFilepath = function(title,options) {
|
||||
}
|
||||
// Replace any Unicode control codes
|
||||
filepath = filepath.replace(/[\x00-\x1f\x80-\x9f]/g,"_");
|
||||
// Replace any characters that can't be used in cross-platform filenames
|
||||
filepath = $tw.utils.transliterate(filepath.replace(/<|>|~|\:|\"|\||\?|\*|\^/g,"_"));
|
||||
// Replace any characters that can't be used in cross-platform filenames.
|
||||
if(!pinFilepath) {
|
||||
filepath = $tw.utils.transliterate(filepath.replace(/<|>|~|\:|\"|\||\?|\*|\^|\\/g,"_"));
|
||||
}
|
||||
// Replace any dots or spaces at the end of the extension with the same number of underscores
|
||||
extension = extension.replace(/[\. ]+$/, function (u) { return u.replace(/[\. ]/g, "_");});
|
||||
// Truncate the extension if it is too long
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*\
|
||||
title: test-filesystem.js
|
||||
type: application/javascript
|
||||
tags: [[$:/tags/test-spec]]
|
||||
|
||||
Tests for $:/core-server filesystem utilities.
|
||||
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
if($tw.node) {
|
||||
|
||||
var path = require("path");
|
||||
|
||||
describe("generateTiddlerFilepath", function() {
|
||||
|
||||
var directory = path.resolve("/tmp/tw5-test-filesystem");
|
||||
|
||||
// Characters stripped by the cross-platform-filename regex at
|
||||
// core-server/filesystem.js:356. Each entry is [char, description].
|
||||
// The forbidden set includes all characters disallowed by Windows
|
||||
// (< > : " | ? *), backslash (directory separator on Windows),
|
||||
// tilde (legacy 8.3 short-name marker), and caret (disallowed in
|
||||
// some shell/FS contexts).
|
||||
var forbiddenChars = [
|
||||
["<","less-than"],
|
||||
[">","greater-than"],
|
||||
["~","tilde"],
|
||||
[":","colon"],
|
||||
["\"","double-quote"],
|
||||
["|","pipe"],
|
||||
["?","question-mark"],
|
||||
["*","asterisk"],
|
||||
["^","caret"],
|
||||
["\\","backslash"]
|
||||
];
|
||||
|
||||
// Use originalpath so we exercise the line-356 regex directly.
|
||||
// The title-branch at line ~342 pre-strips "/" and "\" before the
|
||||
// main regex runs, which would mask the backslash case.
|
||||
function filepathFromOriginalpath(title,extension) {
|
||||
return $tw.utils.generateTiddlerFilepath(title,{
|
||||
extension: extension,
|
||||
directory: directory,
|
||||
fileInfo: {
|
||||
overwrite: true,
|
||||
originalpath: title + extension
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
forbiddenChars.forEach(function(entry) {
|
||||
var char = entry[0], name = entry[1];
|
||||
it("should replace " + name + " (" + char + ") with underscore", function() {
|
||||
var result = filepathFromOriginalpath("a" + char + "b",".tid");
|
||||
expect(path.dirname(result)).toBe(directory);
|
||||
expect(path.basename(result)).toBe("a_b.tid");
|
||||
});
|
||||
});
|
||||
|
||||
it("should replace every forbidden char in a dense string", function() {
|
||||
// Prefix with "x" so the sanitized result isn't all underscores,
|
||||
// which would trigger the charcode-fallback branch at line ~371.
|
||||
var title = "x" + forbiddenChars.map(function(e) { return e[0]; }).join("");
|
||||
var expected = "x" + forbiddenChars.map(function() { return "_"; }).join("");
|
||||
var result = filepathFromOriginalpath(title,".tid");
|
||||
expect(path.dirname(result)).toBe(directory);
|
||||
expect(path.basename(result)).toBe(expected + ".tid");
|
||||
});
|
||||
|
||||
it("should sanitize real-world 'Pragma: \\define' title without creating a subdirectory", function() {
|
||||
// Issue for editions/tw5.com titles like "Pragma: \define".
|
||||
// Before the fix, backslash survived sanitization and path.resolve
|
||||
// treated it as a separator on Windows, producing a "Pragma_ "
|
||||
// subdir containing "define.tid".
|
||||
var result = filepathFromOriginalpath("Pragma: \\define",".tid");
|
||||
expect(path.dirname(result)).toBe(directory);
|
||||
expect(path.basename(result)).toBe("Pragma_ _define.tid");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
change-category: nodejs
|
||||
change-type: bugfix
|
||||
created: 20260711175727000
|
||||
description: Tiddler titles containing a backslash no longer break generated file paths
|
||||
github-contributors: pmario
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9815
|
||||
release: 5.5.0
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.5.0/#9815
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
* Titles containing a backslash, such as `pragma: \define`, get the backslash replaced like other forbidden filename characters instead of it acting as a Windows path separator (fixes [[Issue #9814|https://github.com/TiddlyWiki/TiddlyWiki5/issues/9814]])
|
||||
* Tiddler files declared in `tiddlywiki.files` are pinned to their original location and skip the `$:/config/FileSystemPaths` filters and filename sanitising
|
||||
Reference in New Issue
Block a user