T09 · Insecure Skill Coding Practices
- Location
scripts/run.py:75- Finding
Runtime Cost Cap Is Not Enforced During Billable Subprocess Execution
- Content
View full analysis
Vulnerability Details
File Location:
scripts/run.py:75-93, 138-154;scripts/_shared/cost_guard.py:46-72
Vulnerability Type: Cost-control enforcement failure
Risk Level: MediumVulnerable Code
python guard = CostGuard.load(project_dir, cap=args.cap) try: guard.preflight(est["total"]) except BudgetExceeded as e: print(f"❌ {e}") return 2python for step, skill, script, sub_args in steps: if cp.is_done(step): print(f"⏭️ {step}: 已完成,跳过") continue cp.mark_running(step) try: run_substep(skill, script, *sub_args) cp.mark_done(step) except subprocess.CalledProcessError as e: cp.mark_failed(step, str(e)) print(f"❌ {step} 失败: {e}") return 3 except BudgetExceeded as e: print(f"🛑 熔断: {e}") return 4 report = guard.report()The runtime enforcement mechanism exists but is never invoked:
python def charge(self, step: str, item: str, cost: float) -> None: self.spent += cost self.records.append(CostRecord(step=step, item=item, cost=cost)) ratio = self.spent / self.cap if self.cap else 0 if ratio >= 1.0: self._persist() raise BudgetExceeded( f"累计成本 ¥{self.spent:.2f} 达到熔断上限 ¥{self.cap:.2f}({step}/{item})。\n" f"已完成步骤可从 checkpoint 续跑。" )Technical Analysis
The orchestrator performs a preflight estimate but never calls
CostGuard.charge()after billable image, video, speech, lip-sync, or music operations. As a result,spentremains unchanged in the orchestrator, runtime warnings cannot activate, and the hard cap cannot interrupt subsequent paid operations.The
except BudgetExceededblock aroundsubprocess.run()does not fix this issue. A Python exception raised inside a child process does not propagate into the parent process as the same exception; it is represent ...[truncated 1105 chars]- Remediation
View remediation
Remediation Suggestions
- Require every child Skill to return machine-readable actual usage and cost for each billable item.
- Call
guard.charge(step, item, cost)immediately after each paid request, not merely after the entire child process completes. - Check the remaining budget before submitting each new paid request.
- If child processes enforce costs directly, use a shared, atomic, locked cost ledger to prevent races and stale reads.
- Define a structured exit status for budget exhaustion and translate it into
BudgetExceededin the parent process. - Include retries and partially completed requests in recorded usage.
- Reconcile local records against provider billing or usage responses before presenting the final report.
- Add integration tests proving that execution stops before the next billable request once the configured cap is reached.
