Back to skill

Security audit

Selenium Automation Skill

Security checks for vulnerabilities and agentic risk

Overview

This Selenium automation skill is mostly purpose-aligned, but it should be reviewed because it can submit forms/time entries and retain or expose sensitive data without enough user control or disclosure.

Install only in an isolated environment with non-sensitive test accounts or approved internal sites. Avoid passing real passwords or confidential form data on the command line, assume logs may contain entered values, and check for generated screenshots or scraped output files before running in shared or synchronized workspaces. Prefer pinned dependencies and preapproved browser drivers.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T08 · Insecure Dependencies

Error
Location
SKILL.md:27
Finding
Unpinned Dependencies and Runtime WebDriver Executable Downloads<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:27`; `scripts/form_filler.py:43-50`; `scripts/time_logger.py:44`; `scripts/web_scraper.py:46-53` **Vulnerability Type**: Software supply-chain exposure through unpinned packages and runtime executable retrieval **Risk Level**: High ### Vulnerable Code `SKILL.md:27`: ```bash pip install selenium webdriver-manager beautifulsoup4 pandas ``` `scripts/form_filler.py:43-50`: ```python if self.browser == 'chrome': service = Service(ChromeDriverManager().install()) self.driver = webdriver.Chrome(service=service, options=options) elif self.browser == 'firefox': service = Service(GeckoDriverManager().install()) self.driver = webdriver.Firefox(service=service, options=options) elif self.browser == 'edge': service = Service(EdgeChromiumDriverManager().install()) self.driver = webdriver.Edge(service=service, options=options) ``` `scripts/time_logger.py:44`: ```python service = Service(ChromeDriverManager().install()) ``` `scripts/web_scraper.py:46-53`: ```python if self.browser == 'chrome': service = Service(ChromeDriverManager().install()) self.driver = webdriver.Chrome(service=service, options=options) elif self.browser == 'firefox': service = Service(GeckoDriverManager().install()) self.driver = webdriver.Firefox(service=service, options=options) elif self.browser == 'edge': service = Service(EdgeChromiumDriverManager().install()) self.driver = webdriver.Edge(service=service, options=options) ``` ### Technical Analysis The installation instructions do not constrain dependency versions or verify package hashes. Consequently, later executions of the installation command may retrieve package versions that differ from those reviewed during the audit. The scripts also invoke `webdriver-manager` at runtime to locate or download native WebDriver executables. These executables run locally with the permissions of the user launching the script. The reviewed co ...[truncated 1661 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to a reviewed version in a lockfile or requirements file. 2. Require cryptographic hashes, such as through `pip install --require-hashes`, for reproducible installations. 3. Use an approved internal package mirror or explicitly trusted package index. 4. Include transitive dependencies in dependency review, vulnerability scanning, and lockfile generation. 5. Do not download native WebDriver executables during ordinary script execution. 6. Provision reviewed drivers through the deployment or packaging process and reference a fixed local path. 7. Verify driver signatures or SHA-256 checksums before execution when downloading drivers cannot be avoided. 8. Run browser automation as a non-privileged account inside an isolated environment with restricted filesystem and network access. 9. Periodically update pinned versions through a controlled review and testing process. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/form_filler.py:139
Finding
Sensitive Form Values Are Exposed Through Logs and Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/form_filler.py:139`; `scripts/form_filler.py:247-248` **Vulnerability Type**: Plaintext disclosure of credentials and personal data **Risk Level**: High ### Vulnerable Code `scripts/form_filler.py:139`: ```python self.logger.info(f"Filled field '{field_name}' with '{value}'") ``` `scripts/form_filler.py:247-248`: ```python parser.add_argument('--username', help='Username field value') parser.add_argument('--password', help='Password field value') ``` The affected interface also accepts other potentially sensitive fields: ```python parser.add_argument('--email', help='Email field value') parser.add_argument('--name', help='Name field value') parser.add_argument('--message', help='Message field value') parser.add_argument('--phone', help='Phone field value') parser.add_argument('--company', help='Company field value') ``` ### Technical Analysis The script accepts passwords and other sensitive values directly as command-line arguments. Depending on the operating system and execution environment, command-line arguments can be retained in shell history, process telemetry, job logs, or orchestration metadata. Other local users may also be able to inspect process arguments while the program is running. The `_fill_field` method then records every supplied value at the `INFO` log level without distinguishing passwords from ordinary data. As a result, passwords, contact details, messages, and other form content may be written to terminal output or centralized logging systems in plaintext. The exposure does not require compromising Selenium or the destination website. It occurs locally as part of normal use of the script. ### Attack Path 1. A user invokes the script with a sensitive value, for example through `--password`. 2. The plaintext secret is stored or exposed through shell history, process inspection, CI job metadata, or command logging. 3. When the field is populated, the script writes the ...[truncated 881 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never log form values. Log only the field name and whether the operation succeeded. 2. Apply explicit redaction for fields whose names indicate passwords, secrets, tokens, credentials, personal data, or payment data. 3. Remove the `--password` command-line option for interactive use and obtain passwords with `getpass.getpass()`. 4. For automation, accept secrets through a protected standard-input channel, operating-system credential store, or dedicated secret manager. 5. Avoid placing secrets in environment variables where process or diagnostic tooling may expose them unless the deployment platform specifically protects those variables. 6. Document that ordinary command-line options should not be used for confidential values. 7. Configure logging to avoid collecting page content or exception details that may reproduce entered values. 8. Rotate any credentials that may already have been exposed through historical invocations or retained logs. 9. Apply restrictive access controls and retention limits to automation logs. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/form_filler.py:67
Finding
Browser Sandbox Is Disabled While Processing Arbitrary URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/form_filler.py:67,79`; `scripts/time_logger.py:40`; `scripts/web_scraper.py:70,82` **Vulnerability Type**: Unsafe browser configuration reducing exploit containment **Risk Level**: High ### Vulnerable Code `scripts/form_filler.py:63-69`: ```python if self.browser == 'chrome': options = Options() if self.headless: options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') ``` The Edge configuration in the same file also includes: ```python options.add_argument('--no-sandbox') ``` `scripts/time_logger.py:36-41`: ```python options = Options() if self.headless: options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') ``` `scripts/web_scraper.py:66-71`: ```python if self.browser == 'chrome': options = Options() if self.headless: options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument('--no-sandbox') ``` The Edge configuration in `web_scraper.py` also includes: ```python options.add_argument('--no-sandbox') ``` ### Technical Analysis Chrome and Edge use operating-system sandboxing to isolate renderer and related browser processes from the host. Passing `--no-sandbox` disables an important defense-in-depth boundary. All three scripts navigate to a URL provided by the user. That URL may point directly to an untrusted website or redirect to attacker-controlled content. Browser vulnerabilities can sometimes permit code execution inside a renderer process. With normal sandboxing, an attacker generally requires an additional sandbox escape to gain meaningful host access. Disabling the sandbox reduces this containment and can make browser exploitation substantially more damaging. The flag may sometimes be used ...[truncated 1457 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--no-sandbox` from the default Chrome and Edge configurations in every script. 2. Run browser automation as a dedicated non-root account. 3. If an exceptional environment requires disabling the browser sandbox, make it an explicit, prominently documented opt-in option rather than a default. 4. Place any sandbox-disabled execution inside a hardened container or virtual machine with no privileged mode, no host PID namespace, no unnecessary device access, and minimal filesystem mounts. 5. Restrict outbound network access to approved destinations when the automation workflow permits it. 6. Keep browsers and drivers patched and ensure their versions are compatible and reviewed. 7. Validate URL schemes and consider an allowlist of approved hosts for workflows that do not require unrestricted browsing. 8. Prevent access to cloud instance metadata, loopback services, and internal management endpoints where arbitrary URLs are accepted. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/time_logger.py:61
Finding
Time Logger Unconditionally Stores Screenshots Containing Potentially Sensitive Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/time_logger.py:61`; `scripts/time_logger.py:242`; `scripts/time_logger.py:330-331` **Vulnerability Type**: Unexpected plaintext retention of page and form information **Risk Level**: Medium ### Vulnerable Code `scripts/time_logger.py:60-61`: ```python # Take initial screenshot self.take_screenshot('initial_page.png') ``` `scripts/time_logger.py:241-242`: ```python # Take screenshot after filling form self.take_screenshot('filled_form.png') ``` The command-line option implies that screenshots are optional: ```python parser.add_argument('--screenshot', action='store_true', help='Take screenshots during the process') ``` However, the two screenshots above are created by `log_time()` regardless of whether `--screenshot` was supplied. The storage operation uses the caller's current working directory and the supplied fixed filename: ```python def take_screenshot(self, filename='screenshot.png'): """Take a screenshot of the current page.""" try: self.driver.save_screenshot(filename) self.logger.info(f"Screenshot saved as {filename}") return True except Exception as e: self.logger.error(f"Error taking screenshot: {str(e)}") return False ``` ### Technical Analysis The script automatically captures the initial task page and the completed time-entry form. These images can contain task names, internal project information, user identifiers, entered work descriptions, dates, and other data displayed by the service. The behavior conflicts with the `--screenshot` option's description because the option controls only the additional final screenshot. The initial and filled-form screenshots are unconditional. Fixed filenames are written into the current working directory without explicit restrictive permissions, a protected destination, unique naming, retention controls, or cleanup. The issue is local data retention rather than remote ...[truncated 1154 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Guard every screenshot operation with the explicit `--screenshot` setting. 2. Pass the screenshot preference into `TimeLogger` or `log_time()` and default it to disabled. 3. Allow users to select a destination directory rather than writing silently to the current working directory. 4. Create screenshots with unique filenames to avoid accidental overwrites and cross-run confusion. 5. Use restrictive file permissions and a private directory accessible only to the invoking user. 6. Warn users that screenshots may contain confidential task and form information. 7. Provide an automatic deletion or configurable retention policy. 8. Avoid collecting screenshots in CI artifacts unless the user explicitly requests them. 9. Consider redacting sensitive elements before capture when screenshots are needed for diagnostics. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (13)

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill advertises web scraping, screenshots, cookie handling, and file output, all of which can collect, store, or expose sensitive information, yet it omits any privacy, consent, or local data handling warnings. In practice, this increases the risk of unauthorized collection of personal data, persistence of session material, or accidental storage of sensitive content in local files.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill advertises web scraping, screenshots, cookie handling, and file output, all of which can collect, store, or expose sensitive information, yet it omits any privacy, consent, or local data handling warnings. In practice, this increases the risk of unauthorized collection of personal data, persistence of session material, or accidental storage of sensitive content in local files.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The fill_form docstring says submit_button can be a "CSS selector or ID", but _submit_form only uses By.CSS_SELECTOR when the string starts with '.' and otherwise uses By.ID. This actively contradicts the documented interface because valid CSS selectors like '#submit', 'button[type="submit"]', or 'form button' will not be handled as documented unless they happen to start with a dot.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script submits the form by default unless the user passes --no-submit, which can trigger irreversible actions on remote sites. While the CLI description says it automatically fills web forms, there is no explicit warning at the submission point or in argument help that the default behavior includes submitting data to the target site.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The script logs raw field values after sending them to the page, including likely secrets such as passwords, emails, phone numbers, and other PII. In an automation skill context, logs are often centrally collected or retained, so this can leak credentials and sensitive user data beyond the intended destination form.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script saves screenshots of the loaded task page and the filled form to local files by default, which may capture sensitive project details, internal URLs, comments, dates, or personally identifiable information. Because this happens automatically and before/after form filling, it creates a quiet data retention and exposure risk on the operator's machine or shared workspace.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script automatically submits a time entry once it locates likely form fields, without any final confirmation, preview, or domain allowlist check. In a browser automation context using heuristic selectors, this raises the risk of writing incorrect data to the wrong system or triggering unintended state-changing actions on pages that partially match the expected UI.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The CLI exposes a --screenshot flag that implies screenshots are optional, but the script always captures screenshots earlier in the workflow via log_time() and _fill_time_form(). This can mislead users into believing no local data capture occurs unless they opt in, resulting in unintended storage of potentially sensitive task content on disk.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script automatically downloads browser drivers at runtime via webdriver_manager, causing undisclosed outbound network activity and execution of externally obtained binaries. In security-sensitive or restricted environments, this creates supply-chain and policy-compliance risk because remote artifacts are fetched implicitly rather than pinned, verified, or administrator-approved.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
# Build form data dictionary
    form_data = {}
    for field in ['username', 'password', 'email', 'name', 'message', 'phone', 'company']:
        value = getattr(args, field)
        if value:
            form_data[field] = value
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
The selector logic is tied to specific English and Chinese strings, which bakes in language assumptions rather than allowing users to specify or opt into their preferred locale. This can conflict with language/locale policy expectations when skills should not force or presume a language choice implicitly.

Missing User Warnings

Low
Confidence
81% confidence
Finding
The script loads arbitrary user-supplied URLs in a real browser session, which causes network requests and may transmit system/browser metadata to external sites, but the user-facing description only says 'Automatically scrape web pages' without any warning about this behavior. There is logging after the page is loaded, but no prior disclosure of the network/privacy implications.

Intent-Code Divergence

Low
Confidence
97% confidence
Finding
The docstring for `_make_absolute_url` says it will convert a relative URL to an absolute one, but the implementation only returns already-absolute URLs unchanged and otherwise returns the original relative URL. This is an active contradiction between the documented intent and actual behavior, not just missing detail.

Static analysis

No suspicious patterns detected.