T09 · Insecure Skill Coding Practices
- Location
scripts/stripe_actions.py:208- Finding
Unrestricted Authenticated Stripe API Path in Metadata Update
- Content
View full analysis
Vulnerability Details
File Location:
scripts/stripe_actions.py:18-19,scripts/stripe_actions.py:116, andscripts/stripe_actions.py:208-212
Vulnerability Type: Unrestricted caller-controlled API resource path
Risk Level: MediumVulnerable Code
python url = BASE + pathpython meta.add_argument("path", help="API path like /customers/cus_123 or /products/prod_123")python if args.cmd == "update_metadata": require_confirm(args) path = args.path if args.path.startswith("/") else f"/{args.path}" params = {} for k, v in parse_metadata(args.metadata).items(): params[f"metadata[{k}]"] = v print_json(request("POST", path, params)) returnTechnical Analysis
The
update_metadatacommand accepts a raw API path from the caller and concatenates it directly with the fixed Stripe API base URL. The implementation does not enforce the documented customer or product resource scope, validate the resource type or object identifier, normalize the path, or reject query strings, fragments, encoded path separators, traversal components, or duplicate separators.The request remains restricted to Stripe's configured HTTPS origin, so this issue does not directly expose the secret key to an arbitrary third-party server. However, the resulting request is authenticated with
STRIPE_SECRET_KEY, allowing the caller to target unintended Stripe API resources that accept metadata updates and are accessible to that key. Requiring--confirmprovides a deliberate-action check but does not constrain what resource may be targeted.Attack Path
- An attacker, compromised agent, or mistaken operator supplies an unintended Stripe resource path to
update_metadata. - The invocation includes
--confirm, satisfying the only write-operation safeguard. - The script prepends the fixed Stripe API base URL without checking the resource type or identifier.
...[truncated 795 chars]
- An attacker, compromised agent, or mistaken operator supplies an unintended Stripe resource path to
- Remediation
View remediation
Remediation Suggestions
- Replace the raw
pathargument with explicit resource-type and object-ID arguments. - Allowlist only resource types intentionally supported by the skill, such as
customersandproducts. - Validate identifiers against the expected Stripe object prefixes and a conservative character set, such as
cus_orprod_followed by alphanumeric characters. - Construct the endpoint internally only after validation rather than concatenating caller-controlled paths.
- Reject query strings, fragments,
..components, encoded separators, backslashes, control characters, and duplicate slashes. - Use a restricted Stripe key whose permissions include only the resource types and operations required by this skill.
- Display the normalized resource type and object ID before submission, especially when operating against a live Stripe account.
- Add tests confirming that unsupported resource paths and malformed identifiers are rejected before the secret key is loaded or any network request is made.
A safer interface would resemble:
python meta.add_argument( "object_type", choices=["customers", "products"], ) meta.add_argument("object_id") prefixes = { "customers": "cus_", "products": "prod_", } expected_prefix = prefixes[args.object_type] if not args.object_id.startswith(expected_prefix): raise SystemExit("Invalid Stripe object ID for selected object type") if not args.object_id.replace("_", "").isalnum(): raise SystemExit("Invalid characters in Stripe object ID") path = f"/{args.object_type}/{args.object_id}"- Replace the raw
