T09 · Insecure Skill Coding Practices
- Location
scripts/gitlab_cli.py:141- Finding
GitLab write operations bypass the documented user-confirmation gate
- Content
View full analysis
Vulnerability Details
File Location:
scripts/gitlab_cli.py:141-186, 220-244; related confirmation requirement inSKILL.md:74-75
Vulnerability Type: Missing authorization confirmation for state-changing operations
Risk Level: MediumVulnerable Code
python def cmd_create_issue(cfg, args): body = {"title": args.title, "description": args.description} if args.labels: body["labels"] = args.labels i = request(cfg, "POST", f"/projects/{pid(args.project)}/issues", data=json.dumps(body)) out({"iid": i["iid"], "title": i["title"], "url": i["web_url"]}) def cmd_create_mr(cfg, args): body = {"source_branch": args.source, "target_branch": args.target, "title": args.title, "description": args.description} m = request(cfg, "POST", f"/projects/{pid(args.project)}/merge_requests", data=json.dumps(body)) out({"iid": m["iid"], "title": m["title"], "url": m["web_url"]}) def cmd_merge_mr(cfg, args): m = request(cfg, "PUT", f"/projects/{pid(args.project)}/merge_requests/{args.iid}/merge") out({"iid": m["iid"], "state": m["state"], "merged_by": (m.get("merged_by") or {}).get("username")}) def cmd_trigger_pipeline(cfg, args): body = {"ref": args.ref} p = request(cfg, "POST", f"/projects/{pid(args.project)}/pipeline", data=json.dumps(body)) out({"id": p["id"], "status": p["status"], "ref": p["ref"], "url": p["web_url"]})The argument parser exposes these handlers directly:
python s = sub.add_parser("create-issue", help="create an issue") s.add_argument("project") s.add_argument("--title", required=True) s.add_argument("--description", default="") s.add_argument("--labels", default="") s.set_defaults(func=cmd_create_issue) s = sub.add_parser("create-mr", help="create a merge request") s.add_argument("project") s.add_argument("--source", required=True) s.add_argument("--target", required=True) s.a ...[truncated 2675 chars]- Remediation
View remediation
Remediation Suggestions
- Add a mandatory confirmation mechanism to every state-changing subcommand.
- Require the confirmation to identify the resolved operation and target, rather than accepting a generic Boolean where practical.
- Default write commands to dry-run mode and print the project, branch or ref, merge-request IID, and intended API operation.
- Reject non-interactive writes unless an explicit approval parameter is supplied after user review.
- For merge and pipeline operations, resolve and display relevant GitLab metadata before approval, including the project path, source and target branches, pipeline ref, and protected status where available.
- Keep server-side GitLab approval, protected-branch, and pipeline policies enabled as defense in depth.
A minimal CLI control could require a flag such as:
text --confirm-project group/project --confirm-operation merge-mrThe handler should validate those values against the parsed and resolved target before issuing any authenticated write request.
