Validate skill listing prices strictly - #543
Conversation
Greptile SummaryReplaces the permissive
Confidence Score: 5/5Safe to merge; the changes tighten input validation without removing any existing behaviour, and all rejection paths are covered by new tests. All changed paths are additive validation constraints. The logic in parsePriceSats is correct — the regex excludes every invalid format documented in the PR description, and Number.isSafeInteger catches overflow. The packaging-target validators correctly remove duplicate function definitions that would have been TypeScript compile errors. No runtime regressions were found. packages/targets/pkg-snap/src/index.ts has a minor redundancy: validateSnapName is invoked twice during build() (once directly, once through renderSnapcraftYaml), but this does not affect correctness. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["--price raw"] --> B["parsePriceSats(raw)"]
B --> C["value = raw.trim()"]
C --> D{"/^\\d+$/ test"}
D -- fail --> E["throw: not a non-negative integer"]
D -- pass --> F["price = Number(value)"]
F --> G{"Number.isSafeInteger(price)?"}
G -- false --> H["throw: exceeds safe integer range"]
G -- true --> I["return price"]
I --> J["write manifest + marketplace command"]
E --> K["console.error + process.exit(1)"]
H --> K
Reviews (2): Last reviewed commit: "Address snap validation review" | Re-trigger Greptile |
| const value = raw.trim(); | ||
| if (!/^\d+$/.test(value)) { |
There was a problem hiding this comment.
trim() silently accepts whitespace-padded input (e.g., --price " 5 " normalises to 5 without any warning). More subtly, it also means leading-zero strings like "007" pass the regex unchanged and resolve to 7. If the intent is strict validation of what the user typed, both cases should be rejected rather than coerced. Removing trim() and testing raw directly would make the function's rejection contract completely unambiguous.
| const value = raw.trim(); | |
| if (!/^\d+$/.test(value)) { | |
| if (!/^\d+$/.test(raw)) { |
| throw new Error(`--price must be a non-negative integer in sats. Got: ${JSON.stringify(raw)}`); | ||
| } | ||
|
|
||
| const price = Number(value); |
There was a problem hiding this comment.
If
trim() is removed (so raw is tested directly), the Number(value) conversion should also use raw to stay consistent — keeping value alive solely to pass into Number() while the regex already ran on raw would be confusing. The suggestion below aligns both checks on the same variable.
| const price = Number(value); | |
| const price = Number(raw); |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Update: I folded in the current package-validator cleanup that was already fixing the full-suite CI break on master, so this PR can be tested independently while the package-target PR is still open.\n\nThe original skills fix is unchanged: listing prices now reject non-integer, negative, partially parsed, and unsafe integer values before writing a manifest.\n\nAdditional CI-unblock scope:\n- remove duplicate pkg-flatpak/pkg-winget validators that caused import/build failures\n- keep the snap validator review fixes, including no filesystem side effects on invalid snap names\n\nVerification:\n- npx --yes pnpm@9.12.0 exec vitest run packages/cli/src/commands/skills.test.ts packages/targets/pkg-flatpak/src/index.test.ts packages/targets/pkg-winget/src/index.test.ts packages/targets/pkg-snap/src/index.test.ts -> 39 passed\n- npx --yes pnpm@9.12.0 --filter @profullstack/sh1pt --filter @profullstack/sh1pt-target-pkg-flatpak --filter @profullstack/sh1pt-target-pkg-winget --filter @profullstack/sh1pt-target-pkg-snap typecheck -> passed |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
3 similar comments
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
Fixes #457.
Summary
Number.parseIntcoercion forskills new --pricewith strict digit-only safe-integer validationpriceandtagsfor both the manifest and marketplace command generationRoot Cause
Number.parseIntaccepts partially numeric strings such as5abc,1e2,0x10, and+5, so the previous guard still persisted invalid listing prices.Verification