-
-
Notifications
You must be signed in to change notification settings - Fork 503
fix(security): validate path characters in skill removal request (fixes #53) #55
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
2170674
b2f313e
59ec7eb
97122d4
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 |
|---|---|---|
|
|
@@ -17,6 +17,8 @@ const REMOVABLE_SOURCES = new Set<RemovableSkillSource>([ | |
| "openclaw-workspace", | ||
| ]); | ||
|
|
||
| const SAFE_PATH_RE = /^[a-zA-Z0-9_.~\x2F:\\-]+$/; | ||
|
|
||
| const normalizeRequired = (value: unknown, field: string): string => { | ||
| if (typeof value !== "string") { | ||
| throw new Error(`${field} is required.`); | ||
|
|
@@ -28,6 +30,14 @@ const normalizeRequired = (value: unknown, field: string): string => { | |
| return trimmed; | ||
| }; | ||
|
|
||
| const normalizeRequiredPath = (value: unknown, field: string): string => { | ||
| const trimmed = normalizeRequired(value, field); | ||
| if (!SAFE_PATH_RE.test(trimmed)) { | ||
| throw new Error(`${field} contains invalid characters.`); | ||
| } | ||
| return trimmed; | ||
| }; | ||
|
|
||
| const resolveSkillRemovalSshTarget = (): string | null => { | ||
| const configured = resolveConfiguredSshTarget(process.env); | ||
| if (configured) return configured; | ||
|
|
@@ -51,9 +61,9 @@ const normalizeRemoveRequest = (body: unknown): SkillRemoveRequest => { | |
| return { | ||
| skillKey: normalizeRequired(record.skillKey, "skillKey"), | ||
| source: sourceRaw as RemovableSkillSource, | ||
| baseDir: normalizeRequired(record.baseDir, "baseDir"), | ||
| workspaceDir: normalizeRequired(record.workspaceDir, "workspaceDir"), | ||
| managedSkillsDir: normalizeRequired(record.managedSkillsDir, "managedSkillsDir"), | ||
| baseDir: normalizeRequiredPath(record.baseDir, "baseDir"), | ||
|
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.
|
||
| workspaceDir: normalizeRequiredPath(record.workspaceDir, "workspaceDir"), | ||
| managedSkillsDir: normalizeRequiredPath(record.managedSkillsDir, "managedSkillsDir"), | ||
| }; | ||
| }; | ||
|
|
||
|
|
@@ -79,7 +89,8 @@ export async function POST(request: Request) { | |
| message.includes("Remote workspace skill removal is not supported over SSH") || | ||
| message.includes("Gateway URL is missing") || | ||
| message.includes("Invalid gateway URL") || | ||
| message.includes("require OPENCLAW_GATEWAY_SSH_TARGET") | ||
| message.includes("require OPENCLAW_GATEWAY_SSH_TARGET") || | ||
| message.includes("invalid characters") | ||
| ? 400 | ||
| : 500; | ||
| if (status >= 500) { | ||
|
|
||


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.
This introduces a new validation failure mode ("contains invalid characters"), but the catch block below does not classify that message as client input. As written, malformed paths will now return 500 instead of 400. Please either include this message in the 400 mapping or switch to a structured validation error check.