Skills commands to wrap npm's skills#10528
Conversation
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
There was a problem hiding this comment.
Code Review
This pull request introduces a new 'skills' command set to the CLI, including functionality to add, update, and delete skills. The implementation includes a manifest system for tracking local file hashes and transformations, as well as a registry for synchronizing with remote updates. Feedback focuses on improving type safety by removing 'any' casts, enhancing the robustness of CLI argument parsing for multiple flags, addressing potential regex injection vulnerabilities during template replacement, and optimizing I/O operations during bulk updates.
| set.transformName = transformName; | ||
| } | ||
|
|
||
| await installSkill(path, path, { ...options, set } as any); |
There was a problem hiding this comment.
There are two issues here:
- Casting to
anyviolates the repository style guide regarding type safety. - Using the raw
pathas theskillNameis problematic whenpathis a local file path (e.g.,./myskill.ts). This leads to the manifest using the path as a key andinstallSkillgenerating filenames like.agent/skills/./myskill.ts.ts. Consider deriving a clean name from the path or requiring an alias for local files.
References
- Never use any or unknown as an escape hatch. Define proper interfaces/types or use type guards. (link)
| for (const [key, value] of Object.entries(options.set)) { | ||
| transformations[key] = value; | ||
| // Simple template replacement | ||
| content = content.replace(new RegExp(`{{${key}}}`, "g"), String(value)); |
There was a problem hiding this comment.
Using new RegExp with an unescaped variable (key) can lead to regex injection or unexpected behavior if the key contains special characters (e.g., ., *, or {). Since this is a simple template replacement, using split().join() is safer and more performant.
| content = content.replace(new RegExp(`{{${key}}}`, "g"), String(value)); | |
| content = content.split(`{{${key}}}`).join(String(value)); |
| if (typeof options.set === "string") { | ||
| const [key, value] = options.set.split("="); | ||
| if (key && value) { | ||
| set[key] = value; | ||
| } | ||
| } |
There was a problem hiding this comment.
The current logic only handles a single --set flag if it is passed as a string. In many CLI configurations, providing multiple --set flags results in an array. This implementation would skip processing entirely if options.set is an array, preventing users from applying multiple transformations.
| if (typeof options.set === "string") { | |
| const [key, value] = options.set.split("="); | |
| if (key && value) { | |
| set[key] = value; | |
| } | |
| } | |
| const set: Record<string, string> = {}; | |
| const setArgs = Array.isArray(options.set) ? options.set : (typeof options.set === "string" ? [options.set] : []); | |
| for (const s of setArgs) { | |
| const [key, value] = s.split("="); | |
| if (key && value) { | |
| set[key] = value; | |
| } | |
| } |
| await installSkill(skillName, entry.source, { | ||
| ...options, | ||
| alias: entry.transformations?.alias, | ||
| set: entry.transformations | ||
| } as any); |
There was a problem hiding this comment.
| localFileHash: string; | ||
| transformations?: { | ||
| alias?: string; | ||
| [key: string]: any; |
There was a problem hiding this comment.
Avoid using any as an escape hatch for transformation values. Define a more specific type (e.g., string | number | boolean) to adhere to the style guide and ensure type safety.
References
- Never use any or unknown as an escape hatch. Define proper interfaces/types or use type guards. (link)
No description provided.