T09 · Insecure Skill Coding Practices
Warning
- Location
- linkedin_post.py:73
- Finding
- Unrestricted Browser Destination and Target Selection## Vulnerability Details **File Location**: `linkedin_post.py`, lines 73–115 **Vulnerability Type**: Insufficient validation of browser navigation and target scope **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument("--open-url", default=DEFAULT_FEED_URL, help="URL to open for the share modal") parser.add_argument("--target-id", help="Existing tab target id. Skip opening a new share modal when provided") if args.target_id: target_id = args.target_id else: opened = run_browser(["open", args.open_url], token, args.profile) target_id = parse_target_id(opened) time.sleep(args.wait_seconds) snapshot = run_browser(["snapshot", "--target-id", target_id, "--limit", "300", "--format", "ai"], token, args.profile) textbox_ref = parse_ref( snapshot, [ r'textbox "Text editor for creating content" \[ref=(e\d+)\]', r'textbox "[^\"]*creating content[^\"]*" \[ref=(e\d+)\]', r'textbox "[^\"]*What do you want to talk about\?[^\"]*" \[ref=(e\d+)\]', ], "textbox", ) if args.publish: post_snapshot = run_browser(["snapshot", "--target-id", target_id, "--limit", "220", "--format", "ai"], token, args.profile) post_ref = parse_ref( post_snapshot, [ r'button "Post" \[ref=(e\d+)\]', r'button "게시" \[ref=(e\d+)\]', ], "Post button", ) run_browser(["click", post_ref, "--target-id", target_id], token, args.profile) ``` ### Technical Analysis The skill is intended exclusively for publishing LinkedIn feed posts, but the `--open-url` option accepts an arbitrary URL and `--target-id` accepts an arbitrary existing browser tab. The script does not verify that the selected target is an HTTPS LinkedIn page or that its path corresponds to the intended feed composer. After selecting the target, the script trusts accessible snapshot labels to identify the textbox and final su ...[truncated 1806 chars]
- Remediation
- ## Remediation Suggestions 1. Remove `--open-url` if custom destinations are not required and always use the fixed LinkedIn feed URL. 2. If custom URLs must remain supported, parse them with `urllib.parse.urlparse` and require: - the `https` scheme; - an exact approved hostname such as `www.linkedin.com`; - the intended `/feed/` path; - rejection of embedded credentials, deceptive subdomains, and nonstandard destinations. 3. Before every fill or click operation, query the current target URL and confirm that it remains on an approved LinkedIn origin and path. 4. When `--target-id` is supplied, resolve the target metadata and reject tabs whose current URL is outside the allowlist. 5. Repeat origin validation immediately before clicking the final `Post` button to mitigate redirects or target navigation between snapshots. 6. Preserve the existing explicit `--publish` control and require an additional confirmation mechanism when the caller is interactive. 7. Prefer structured browser selectors scoped to the verified LinkedIn composer rather than relying only on generic accessibility labels.
