T09 · Insecure Skill Coding Practices
- Location
scripts/cloud_cli.py:12- Finding
Unrestricted Local File Content Can Be Uploaded to the Cloud API
- Content
View full analysis
Vulnerability Details
File Location:
scripts/cloud_cli.py:12-20; data is subsequently transmitted atscripts/run.py:39-48
Vulnerability Type: Unrestricted file read followed by network transmission
Risk Level: MediumVulnerable Code
python def read_text_arg(value: str) -> str: """CLI parameter: treat an existing path as a file; otherwise use it as text.""" raw = (value or "").strip() if not raw: return "" path = Path(raw) if path.is_file(): return path.read_text(encoding="utf-8") return rawThe resulting file contents are placed directly into the cloud request:
python payload: dict = { "platform": args.platform, "lang": args.lang, "our_product": args.our_product.strip(), "competitor": read_text_arg(args.competitor), } if args.our_listing: payload["our_listing"] = read_text_arg(args.our_listing) try: data = run_skill(SKILL_API_ID, payload, timeout=180.0)Technical Analysis
The documented functionality permits competitor and first-party listing text to be loaded from
.txtfiles. However,read_text_arg()accepts any readable regular file recognized byPath.is_file(). It does not enforce an approved workspace, file extension, content type, maximum size, symlink policy, or sensitive-path denylist.Because
run.pyplaces the returned text into the cloud API payload, any UTF-8-readable file accessible to the invoking process can be uploaded. Potential targets include.envfiles, source configuration, API credentials, cloud configuration, agent configuration, and private keys stored in text-compatible formats.This behavior exceeds the minimum file privileges required for listing analysis because the Skill only needs access to explicitly selected listing documents, not unrestricted process-readable files.
Attack Path
- An attacker supplies content that influences an Agent or ...[truncated 1026 chars]
- Remediation
View remediation
Remediation Suggestions
- Separate literal text and file input into distinct arguments, such as
--competitor-textand--competitor-file, so a text value cannot be silently reinterpreted as a path. - Restrict file access to an explicitly configured workspace:
python workspace = Path(os.environ["SKILL_WORKSPACE"]).resolve() candidate = Path(value).resolve(strict=True) candidate.relative_to(workspace) - Reject symlinks or validate the final resolved target before reading it.
- Permit only expected extensions such as
.txtand validate that the content is plain text. - Enforce a conservative maximum file size before reading or uploading.
- Deny known sensitive names and locations, including
.env,.ssh, cloud credential directories, and agent configuration files. - Require explicit user confirmation that identifies the resolved path and remote destination before uploading local file contents.
- Document that selected listing content is transmitted to the cloud service.
- Separate literal text and file input into distinct arguments, such as
