From 75c3b72255f853d79f5cb7a752224aec542049f9 Mon Sep 17 00:00:00 2001 From: kkdev92 <112151103+kkdev92@users.noreply.github.com> Date: Sat, 3 Oct 2026 23:54:11 +0900 Subject: [PATCH] docs: say what the path and input checks do SECURITY.md said that the save directory cannot be absolute or contain `..`, and that file name patterns cannot contain shell metacharacters. Both are reported as configuration warnings, not refused. What keeps the saved image inside the workspace is the check against the workspace's real path: it refuses a save directory that leads outside, and lets one whose `..` stays inside resolve to the folder it names. The sections now say so, the code sample follows the check as it is written, and the file name sanitizing is described as it is: control characters and the characters a Windows file name cannot hold are removed, and on Windows a reserved name is prefixed. The comment in sanitizeConfiguration said fileName.pattern keeps only safe characters, a filter the code does not apply; it now says what happens. Co-Authored-By: Claude Opus 5.5 --- SECURITY.md | 25 +++++++++++++------------ src/config/validators.ts | 5 +++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 1967de5..a9dd0fd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -40,16 +40,17 @@ const cmd = `powershell.exe -EncodedCommand ${encoded}`; ### Path Traversal Prevention -- All paths are validated to be within the workspace -- `realpath()` is used to resolve symbolic links -- Parent directory references (`..`) are blocked +- The saved image, and every folder created for it, is checked to be inside the workspace before it is written +- `realpath()` resolves symbolic links in the part of the path that already exists, so a link cannot lead the image outside the workspace +- A save directory that leads outside the workspace, through `..` or otherwise, is refused when the image is saved. One whose `..` stays inside the workspace resolves to the folder it names ```typescript -// Path validation -const realTarget = await fs.realpath(targetPath); -const relative = path.relative(workspaceRoot, realTarget); -if (relative.startsWith('..')) { - throw new PathValidationError('Path is outside workspace'); +// Path validation (src/security/path-validator.ts, simplified) +const realRoot = await fs.realpath(workspaceRoot); +const realTarget = await resolveExistingPrefix(targetPath, workspaceRoot); +const relative = path.relative(realRoot, realTarget); +if (relative.startsWith('..') || path.isAbsolute(relative)) { + throw new PathValidationError('Path is outside the workspace'); } ``` @@ -62,10 +63,10 @@ if (relative.startsWith('..')) { ### Input Validation -All user-configurable values are validated: -- Save directory cannot be absolute or contain `..` -- File name patterns cannot contain shell metacharacters -- Numeric values are clamped to valid ranges +Settings are checked each time they are read: +- Numeric values are clamped to valid ranges, and a value of the wrong type falls back to the default +- A save directory that is absolute or contains `..`, and a file name pattern with shell metacharacters, are reported as configuration warnings in the ClipShot log. They are not what keeps writes inside the workspace; the check above is +- Each generated file name has control characters, and the characters a Windows file name cannot hold (`< > : " / \ | ? *`), removed. On Windows, a reserved name such as `CON` is prefixed ### Workspace Trust diff --git a/src/config/validators.ts b/src/config/validators.ts index f3dc33d..046dc21 100644 --- a/src/config/validators.ts +++ b/src/config/validators.ts @@ -425,9 +425,10 @@ export function sanitizeConfiguration(config: DeepPartial): Dee .replace(/^\/+|\/+$/g, ''); } - // Sanitize fileName.pattern - remove dangerous characters + // Trim fileName.pattern. Characters a file name cannot hold are removed from + // each generated name by sanitizeFileName; shell metacharacters in the pattern + // are only reported, by validateFileNamePattern. if (sanitized.fileName?.pattern !== undefined && sanitized.fileName.pattern !== '') { - // Keep only safe characters (alphanumeric, underscore, hyphen, dot, spaces, and ${} tokens) sanitized.fileName = { ...sanitized.fileName, pattern: sanitized.fileName.pattern.trim(),