T09 · Insecure Skill Coding Practices
- Location
scripts/daily_batting.py:360- Finding
Authentication Code Persisted in Plaintext During Dry-Run
- Content
View full analysis
Vulnerability Details
File Location:
scripts/daily_batting.py, lines 360–371 and 416–426
Vulnerability Type: Plaintext credential exposure
Risk Level: MediumVulnerable Code
python return { "task_id": f"daily_batting_{today.replace('-', '')}", "task_name": f"每日击球区日报 - {today}", "task_result": summary, "task_content": content, "schedule_task_id": "daily_batting_zone", "auth_code": read_auth_code(), } def read_auth_code(): try: with open(r"C:\Users\And19\.openclaw\openclaw.json", "r", encoding="utf-8") as f: cfg = json.load(f) code = cfg.get("skills", {}).get("entries", {}).get("today-task", {}).get("config", {}).get("authCode") if code: return code except: pass return "MISSING"python card = build_card(quotes) log.info(f"Summary: {card['task_result']}") if args.dry_run: with open("daily_dryrun.json", "w", encoding="utf-8") as f: json.dump(card, f, ensure_ascii=False, indent=2) log.info("[DRY-RUN] saved") return 0Technical Analysis
build_card()unconditionally reads thetoday-taskauthentication code from the user's OpenClaw configuration and inserts it into the generated card. This occurs even in dry-run mode, where authentication is unnecessary because no push is performed.The dry-run branch serializes the complete card—including
auth_code—to the predictable plaintext filedaily_dryrun.json. The script does not set restrictive permissions, redact the credential, or delete the file afterward. This creates an avoidable persistent copy of a secret outside its original configuration store.Attack Path
-
A user or automated process executes:
bash python scripts/daily_batting.py --dry-run -
build_card()callsread_auth_code(). -
The script reads the
today-taskauthentication code fromC:\Users\And19\.openclaw\openclaw.json. -
The authentication code is inserted into the card as `auth_ ...[truncated 960 chars]
-
- Remediation
View remediation
Remediation Suggestions
-
Remove
auth_codefrombuild_card()so ordinary card construction never retrieves or embeds a credential. -
Add the authentication code only immediately before an actual push:
python card = build_card(quotes) if args.dry_run: with open("daily_dryrun.json", "w", encoding="utf-8") as f: json.dump(card, f, ensure_ascii=False, indent=2) return 0 push_card = {**card, "auth_code": read_auth_code()} ok = push_to_neg1(push_card) -
Prefer passing the credential to the push component through a protected environment variable, standard input, or another mechanism that does not serialize it to disk.
-
If a sensitive temporary file is unavoidable, create it with owner-only permissions, keep its lifetime minimal, and guarantee deletion in a
finallyblock. -
Add a test asserting that dry-run output never contains
auth_code,authCode, or the configured credential value. -
Add
daily_dryrun.jsonto ignore rules as defense in depth, while recognizing that ignore rules do not prevent local disclosure. -
Delete existing dry-run files and rotate the authentication code if any generated file may have been shared, committed, backed up, or otherwise exposed.
-
