T09 · Insecure Skill Coding Practices
Error
- Location
- src/commands/sell.ts:57
- Finding
- Path Traversal Allows File Creation Outside Seller Directories<![CDATA[ ## Vulnerability Details **File Location**: `src/commands/sell.ts:57-58`, `src/commands/sell.ts:242-281`, `src/commands/sell.ts:594-595`, and `src/commands/sell.ts:638-664` **Vulnerability Type**: Unvalidated path input leading to arbitrary file creation or overwrite **Risk Level**: High ### Vulnerable Code ```ts function resolveOfferingDir(offeringName: string): string { return path.resolve(OFFERINGS_ROOT, offeringName); } ``` ```ts export async function init(offeringName: string): Promise<void> { if (!offeringName) { output.fatal("Usage: acp sell init <offering_name>"); } const dir = resolveOfferingDir(offeringName); if (fs.existsSync(dir)) { output.fatal(`Offering directory already exists: ${dir}`); } fs.mkdirSync(dir, { recursive: true }); const offeringJson: Record<string, unknown> = { name: offeringName, description: "", jobFee: null, jobFeeType: null, requiredFunds: null, requirement: {}, }; fs.writeFileSync( path.join(dir, "offering.json"), JSON.stringify(offeringJson, null, 2) + "\n" ); const handlersTemplate = `import type { ExecuteJobResult, ValidationResult } from "../../runtime/offeringTypes.js"; // Required: implement your service logic here export async function executeJob(request: any): Promise<ExecuteJobResult> { // TODO: Implement your service return { deliverable: "TODO: Return your result" }; } // Optional: validate incoming requests export function validateRequirements(request: any): ValidationResult { // Return { valid: true } to accept, or { valid: false, reason: "explanation" } to reject return { valid: true }; } // Optional: provide custom payment request message export function requestPayment(request: any): string { // Return a custom message/reason for the payment request return "Request accepted"; } `; fs.writeFileSync(path.join(dir, "handlers.ts"), handlersTemplate); } ``` The same issue exists in resource creation: ```ts function ...[truncated 4316 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Restrict offering and resource names to a safe identifier format: ```ts function validateArtifactName(name: string): void { if (!/^[A-Za-z0-9_-]+$/.test(name)) { throw new Error( "Name may contain only letters, numbers, underscores, and hyphens." ); } } ``` 2. Verify containment after path resolution: ```ts function resolveWithinRoot(root: string, name: string): string { validateArtifactName(name); const candidate = path.resolve(root, name); const relative = path.relative(root, candidate); if ( relative === "" || relative.startsWith("..") || path.isAbsolute(relative) ) { throw new Error("Resolved path is outside the permitted directory."); } return candidate; } ``` 3. Apply the same validation to: - `resolveOfferingDir` - `resolveResourceDir` - `loadOffering` - Any command that accepts an offering or resource name 4. Before dynamically importing an offering handler, require the name to match a locally registered allowlist and verify the real path remains beneath the offerings root. Where symbolic links are possible, compare canonical paths obtained through `fs.realpathSync`. 5. Avoid using remote job data directly as a filesystem selector. Resolve the remote offering name through a map of known local offering identifiers instead. 6. Add tests covering: - `../` traversal - Absolute paths - Backslash traversal on Windows - Symbolic-link escapes - Empty names and separator-only names ]]>
