Back to skill

Security audit

Indeed Brightdata

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its Indeed/Bright Data purpose, but it needs review because one polling script accepts crafted timing options that can execute local shell commands.

Review before installing. Use a narrowly scoped Bright Data key, avoid the optional unpinned npx installer, and do not let untrusted prompts or inputs control script options until --timeout and --interval are strictly validated. Be aware that job searches, company URLs, and fetched results are sent to Bright Data and cached locally under ~/.config/indeed-brightdata.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/indeed_poll_and_fetch.sh:52
Finding
Command Injection Through Unvalidated Arithmetic Input<![CDATA[ ## Vulnerability Details **File Location**: `scripts/indeed_poll_and_fetch.sh:52-58, 104` **Vulnerability Type**: Bash arithmetic-expression injection **Risk Level**: High ### Vulnerable Code ```bash while [[ $# -gt 0 ]]; do case "$1" in --help) show_help ;; --timeout|--interval|--description|--dataset-type) [[ -n "${2:-}" ]] || { echo "Error: $1 requires a value" >&2; exit 1; } case "$1" in --timeout) TIMEOUT="$2" ;; --interval) INTERVAL="$2" ;; --description) DESCRIPTION="$2" ;; --dataset-type) DATASET_TYPE="$2" ;; esac shift 2 ;; ``` The unvalidated value is subsequently used in an arithmetic comparison: ```bash while [[ "$elapsed" -lt "$TIMEOUT" ]]; do ``` ### Technical Analysis The `--timeout` argument is assigned directly to `TIMEOUT` without verifying that it is a decimal integer. It is subsequently used as an operand in a Bash arithmetic comparison. Bash arithmetic contexts parse operand values as arithmetic expressions rather than inert strings. Arithmetic expressions can recursively resolve variable names and array subscripts, and specially constructed expressions can cause additional shell expansions. Consequently, treating untrusted input as an arithmetic operand may permit command execution rather than merely causing a numeric parsing error. The same validation omission affects `--interval`. Although that value is first passed as a quoted argument to `sleep`, it is also later evaluated in: ```bash elapsed=$((elapsed + INTERVAL)) ``` Strict numeric validation is therefore required for both options. ### Attack Path 1. An attacker supplies or induces the agent to supply a crafted nonnumeric value to `--timeout`. 2. `parse_args` stores the value verbatim in `TIMEOUT`. 3. Execution reaches the loop condition at line 104. 4. Bash interprets the supplied value as part of an arithmetic expression. 5. Malicious expansion embedded in that expression can execute a local co ...[truncated 771 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Validate both arguments as bounded decimal integers before assigning them: ```bash validate_positive_integer() { local name="$1" local value="$2" local maximum="$3" if [[ ! "$value" =~ ^[0-9]+$ ]] || (( 10#$value < 1 || 10#$value > maximum )); then echo "Error: ${name} must be an integer between 1 and ${maximum}" >&2 exit 1 fi } case "$1" in --timeout) validate_positive_integer "--timeout" "$2" 3600 TIMEOUT="$2" ;; --interval) validate_positive_integer "--interval" "$2" 300 INTERVAL="$2" ;; esac ``` Additional hardening should include: - Use `10#` when converting validated decimal input to prevent unintended octal interpretation. - Enforce sensible upper bounds to prevent excessive polling or denial of service. - Reject zero and negative values. - Add regression tests containing whitespace, signs, arithmetic operators, variable names, array syntax, and command-substitution syntax. - Avoid placing any untrusted string into Bash arithmetic contexts unless it has first passed a strict decimal-only allowlist. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_lib.sh:36
Finding
Bright Data API Key Exposed Through Process Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/_lib.sh:36-49` **Vulnerability Type**: Sensitive credential exposure in process command line **Risk Level**: Medium ### Vulnerable Code ```bash make_api_request() { local method="$1" local endpoint="$2" local payload="${3:-}" local api_key="${BRIGHTDATA_API_KEY:?Set BRIGHTDATA_API_KEY}" local curl_args=(-s -w "\n%{http_code}" -H "Authorization: Bearer ${api_key}") if [[ "$method" == "POST" ]]; then curl_args+=(-X POST -H "Content-Type: application/json" -d "$payload") fi local response if ! response=$(curl "${curl_args[@]}" "$endpoint"); then ``` ### Technical Analysis The bearer credential is interpolated directly into a `curl` argument: ```text Authorization: Bearer <BRIGHTDATA_API_KEY> ``` Although the request uses HTTPS and the header is appropriate for authenticating to Bright Data, placing the secret in the argument vector can make it visible through process-inspection facilities. The exact exposure depends on operating-system process visibility, container boundaries, monitoring software, and whether other local users can inspect the process. The network transmission itself is necessary for the Skill's declared functionality and is sent to the expected provider. The issue is the local delivery mechanism used to provide the header to `curl`, not the use of bearer authentication. ### Attack Path 1. The Skill invokes a search or polling operation. 2. `make_api_request` reads `BRIGHTDATA_API_KEY` from the environment. 3. The function constructs a `curl` argument containing the complete bearer token. 4. While `curl` is running, another local process, monitoring agent, or user with sufficient process-inspection access reads its command-line arguments. 5. The observer extracts and reuses the Bright Data API key. ### Impact Assessment An exposed key may permit unauthorized Bright Data API operations within the permissions and billing limits assigned to that credenti ...[truncated 431 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Avoid including the authorization value directly in `curl`'s argument vector. Supply sensitive configuration through a protected file descriptor or a mode-`0600` temporary configuration file. One possible approach is: ```bash local config_file config_file=$(mktemp "${TMPDIR:-/tmp}/.brightdata_curl_XXXXXX") chmod 600 "$config_file" printf 'header = "Authorization: Bearer %s"\n' "$api_key" > "$config_file" if ! response=$(curl --config "$config_file" "${other_curl_args[@]}" "$endpoint"); then rm -f "$config_file" # Handle failure fi rm -f "$config_file" ``` Further hardening should include: - Install a cleanup trap immediately after creating the protected file. - Ensure the temporary directory is not shared insecurely and that the file is owned by the current user. - Never print the API key in diagnostics, debug traces, or HTTP error messages. - Keep `set -x` disabled around credential-handling code. - Use a narrowly scoped Bright Data key with minimum required dataset permissions and spending limits. - Rotate the key if process-monitoring logs may already have captured command lines. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:92
Finding
Unpinned Third-Party Package Execution in Installation Instructions<![CDATA[ ## Vulnerability Details **File Location**: `README.md:92-96` **Vulnerability Type**: Unpinned supply-chain dependency execution **Risk Level**: Medium ### Vulnerable Code ```markdown ### Universal CLI (Community) If you use the [add-skill](https://add-skill.org) CLI: ```bash npx add-skill foreztgump/indeed-brightdata ``` ``` ### Technical Analysis The documented optional installation command invokes `npx` without pinning an audited package version or integrity digest. Depending on the local npm configuration and cache state, `npx` can download the current registry version of `add-skill` and execute it immediately. The reviewed repository therefore cannot establish what code this installation path will execute in the future. A compromised maintainer account, package takeover, malicious future release, or registry compromise could replace the effective installer after this Skill has been audited. The issue is confined to the optional community installation method. The repository's local `install.sh` does not itself download or execute remote code. ### Attack Path 1. An attacker compromises the npm package, its publisher account, or its release pipeline. 2. The attacker publishes a malicious version under the package name used by the documentation. 3. A user follows the README and executes the unversioned `npx add-skill ...` command. 4. `npx` retrieves the current package release from the registry. 5. The malicious package executes with the user's local privileges before or while installing the Skill. ### Impact Assessment A compromised npm package executed through `npx` could run arbitrary code with the privileges of the invoking user. This may allow access to source code, environment variables, API credentials, SSH material, agent configuration, and other user-readable files. It could also modify files, install persistence available to the user, or make arbitrary network requests. No malicious package behavior was found in the audited rep ...[truncated 222 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Pin the installer to a specifically reviewed version: ```bash npx --yes add-skill@AUDITED_VERSION foreztgump/indeed-brightdata ``` Additional supply-chain controls should include: - Record the expected package version and integrity information in the documentation. - Review the exact published npm artifact, including lifecycle scripts and transitive dependencies. - Prefer the repository's local `install.sh` for the primary installation path. - Clearly label the community CLI as third-party and outside the repository's trust boundary. - Consider removing the `npx` method if reproducible verification cannot be provided. - For high-assurance environments, download the package artifact separately, verify its digest, inspect it, and only then execute it in a restricted environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (40)

Credential Access

High
Category
Privilege Escalation
Content
docs/

# Environment
.env
.env.*

# OS
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared purpose describes runtime functionality for searching and scraping Indeed via Bright Data. The actual code chunk does not implement any of that behavior; it only installs the skill for supported platforms and checks environment/setup prerequisites. While checking for BRIGHTDATA_API_KEY is consistent with the description, the primary behavior of this code is installation, not Indeed data retrieval. Therefore the supplied code chunk does not accurately represent the declared skill behavior and is a clear description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description presents a full Indeed scraping/search skill backed by Bright Data's Web Scraper API. However, this code chunk is explicitly a pure formatting script: it consumes already-produced JSON input and reformats it. It performs no remote access, no Indeed lookup, no API requests, no scraping, and no polling. Its primary purpose is materially different from the declared purpose. While it is related to Indeed data, it represents only a post-processing helper, not the described acquisition/search capability.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This code chunk is not implementing the advertised Indeed search/scraping behavior. Its primary function is administrative/discovery-oriented: calling Bright Data's dataset listing endpoint, filtering returned dataset definitions for 'Indeed', and optionally persisting dataset IDs to ~/.config/indeed-brightdata/datasets.json. While this may support a larger Indeed scraping workflow, on its own it does not perform the declared user-facing capabilities such as keyword/location search, URL-based extraction, company discovery, or batch data collection. Therefore the code's actual behavior is materially narrower and partially different from the declared description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description says the skill searches and scrapes Indeed data via Bright Data's API. The supplied code chunk does none of that. It only packages the project into a ZIP file for distribution, copying files and creating an archive. This is a materially different primary purpose from the declared runtime functionality, so this chunk does not accurately represent the described behavior.

Ae1

High
Category
analysis-evasion
Content
| `indeed_smart_search.sh` | **Primary job search** — keyword expansion, parallel queries, dedup, caching | ASYNC |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_smart_search.sh` | **Primary job search** — keyword expansion, parallel queries, dedup, caching | ASYNC |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_smart_search.sh` | **Primary job search** — keyword expansion, parallel queries, dedup, caching | ASYNC |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_smart_search.sh` | **Primary job search** — keyword expansion, parallel queries, dedup, caching | ASYNC |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_smart_search.sh` | **Primary job search** — keyword expansion, parallel queries, dedup, caching | ASYNC |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_smart_search.sh` | **Primary job search** — keyword expansion, parallel queries, dedup, caching | ASYNC |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_smart_search.sh` | **Primary job search** — keyword expansion, parallel queries, dedup, caching | ASYNC |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `indeed_format_results.sh` | Format JSON results into summary, full, or CSV | Local |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Chaining Abuse

High
Category
Tool Misuse
Content
jq -r --argjson cutoff "$history_cutoff" \
      '.[] | select((.timestamp | fromdateiso8601) <= $cutoff) | .result_file // empty' \
      "$LIB_HISTORY_FILE" 2>/dev/null | while IFS= read -r file; do
      [[ -n "$file" && -f "$file" ]] && rm -f "$file"
    done

    # Remove old entries from history
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
├── Wants to search by keyword/location?
│   └── indeed_smart_search.sh [ASYNC — 3-8 min]
│       Agent says: "Searching now, this takes a few minutes."
│       If results < 5: auto-expands date range, do NOT ask user
│       Always pipe output through: indeed_format_results.sh --top 5
├── Wants company info?
│   ├── Has Indeed company URL → indeed_company_by_url.sh [SYNC — seconds]
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

External Transmission

Medium
Category
Data Exfiltration
Content
All scripts source `scripts/_lib.sh` for shared HTTP and persistence functions. The library:

- Makes requests to a **single endpoint**: `https://api.brightdata.com/datasets/v3`
- Uses **one credential**: `BRIGHTDATA_API_KEY` (sent via `Authorization: Bearer` header)
- Writes **only** to `~/.config/indeed-brightdata/` (see Data Storage above)
- Does not read other environment variables, contact other hosts, or modify files outside its config directory
Confidence
90% confidence
Finding
The skill is explicitly designed to transmit data and a bearer credential to a third-party service endpoint at api.brightdata.com. While that is expected for the stated purpose, any user-supplied search terms, URLs, or company/job data sent to the external API may expose sensitive research activity or personal data if inputs are not minimized and users are not informed.

Static analysis

Detected: suspicious.obfuscated_code

Potential obfuscated payload detected.

Warn
Code
suspicious.obfuscated_code
Location
scripts/indeed_format_results.sh:169