Back to skill

Security audit

Apify HN Scraper

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says: it uses an Apify token to scrape Hacker News, with some credential-handling and input-safety cautions users should understand.

Install only if you are comfortable sending Hacker News search parameters to Apify and using an Apify token from the agent environment. Prefer a narrowly scoped token, avoid logging full curl commands, and update the commands to use an Authorization header plus jq-built JSON before using this with sensitive terms or shared systems.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:30
Finding
APIFY_TOKEN Exposed Through URL Query Parameters<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 30-42 **Vulnerability Type**: Credential exposure through command-line URL parameters **Risk Level**: Medium ### Vulnerable Code ```bash RESULT=$(curl -s -X POST "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/run-sync-get-dataset-items?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"searchTerms": ["TERM"], "maxResults": 30}') echo "$RESULT" | jq '.' ``` ```bash RUN_ID=$(curl -s -X POST "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/runs?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"searchTerms": ["TERM"], "maxResults": 100}' | jq -r '.data.id') curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID?token=$APIFY_TOKEN" | jq -r '.data.status' curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID/dataset/items?token=$APIFY_TOKEN" | jq '.' ``` ### Technical Analysis The Skill places the `APIFY_TOKEN` secret directly in URL query parameters. The token is sent to the declared Apify API over HTTPS, so the inspected content does not demonstrate intentional transmission to an unrelated host. Network access to Apify is also necessary for the Skill's stated scraping functionality. Nevertheless, URL-based credentials are vulnerable to incidental disclosure. Complete URLs may be recorded in process arguments, shell tracing output, command histories, diagnostic reports, HTTP proxy logs, server access logs, monitoring systems, or copied error messages. HTTPS protects the URL while it is in transit but does not prevent exposure at either endpoint or through local process observation. The same credential is repeatedly included in Actor creation, status, and dataset requests, increasing its potential exposure surface. ### Attack Path 1. A user invokes the Skill with a valid `APIFY_TOKEN` in the environment. 2. The Agent executes one of the documented `curl` commands and expands the token inside the command-line URL. 3. A local process observer, shell tra ...[truncated 828 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use Apify's supported authorization header instead of a URL query parameter: ```bash curl --fail-with-body --silent --show-error \ -X POST \ -H "Authorization: Bearer $APIFY_TOKEN" \ -H "Content-Type: application/json" \ --data-binary "$payload" \ "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/run-sync-get-dataset-items" ``` 2. Apply the same header-based authentication to Actor creation, status, and dataset requests. 3. Never print authenticated URLs, authorization headers, or environment-variable values in normal or error output. 4. Disable shell tracing while secrets are present and redact authorization data from diagnostics and logs. 5. Use a narrowly scoped Apify token where supported, rotate it periodically, and revoke it immediately if URL logs may have exposed it. 6. Add `--fail-with-body`, `--silent`, and `--show-error` so failures are handled without encouraging operators to print complete authenticated requests. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:20
Finding
Unsafe User Input Substitution in Shell and JSON Request Template<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 20-32 **Vulnerability Type**: Unsafe shell and JSON construction from user-controlled input **Risk Level**: Medium ### Vulnerable Code ```markdown ### Step 1: Confirm parameters with user Ask what they want to scrape. Supported input fields: - `searchTerms` (array of strings) - keywords to search - `maxResults` (integer) - max stories to return - `sortBy` (string) - "points", "date", or "relevance" - `includeComments` (boolean) - include comment threads ### Step 2: Run the Actor ``` ```bash RESULT=$(curl -s -X POST "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/run-sync-get-dataset-items?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"searchTerms": ["TERM"], "maxResults": 30}') ``` ### Technical Analysis The workflow tells the Agent to obtain search parameters from the user and then provides a shell command containing the placeholder `TERM`. It does not define a safe encoding procedure for converting the user-controlled term into JSON. A naive implementation may replace `TERM` directly in the shell command. Quotes, backslashes, JSON delimiters, command substitutions, or shell metacharacters in the search term can then produce malformed JSON. If substitution is performed before shell parsing or by constructing and evaluating a command string, crafted input may also escape the intended quoting context and cause command injection. The literal example does not itself interpolate a shell variable, so local command execution depends on how the Agent or downstream implementation replaces the placeholder. The confirmed weakness is that the Skill instructs processing of untrusted input without requiring structured JSON generation, validation, or safe argument handling. ### Attack Path 1. An attacker supplies a crafted Hacker News search term containing quotes, JSON delimiters, or shell syntax. 2. The Agent follows the Skill and substitutes that value for `TERM` in th ...[truncated 1111 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never replace user input directly inside a shell command or hand-written JSON string. 2. Construct the request body with `jq` so the search term is encoded as JSON data: ```bash payload=$( jq -n \ --arg term "$TERM" \ --argjson maxResults "$MAX_RESULTS" \ '{ searchTerms: [$term], maxResults: $maxResults }' ) curl --fail-with-body --silent --show-error \ -X POST \ -H "Authorization: Bearer $APIFY_TOKEN" \ -H "Content-Type: application/json" \ --data-binary "$payload" \ "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/run-sync-get-dataset-items" ``` 3. Validate `maxResults` as an integer and enforce a reasonable upper bound before passing it to `jq --argjson`. 4. Allow only the documented values for `sortBy`: `points`, `date`, or `relevance`. 5. Parse `includeComments` strictly as a JSON boolean rather than accepting arbitrary text. 6. Avoid `eval`, `sh -c` with dynamically constructed strings, and unquoted variable expansion. 7. Treat Actor responses as untrusted data and keep them in data-processing pipelines rather than evaluating them as commands. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (7)

Vague Triggers

Medium
Confidence
94% confidence
Finding
The description says to use the skill when the user asks to "search HN, find Hacker News posts, monitor tech discussions, or extract HN data." Phrases like "monitor tech discussions" are broad and lack clear scope or exclusion conditions, which could cause unintended invocation for general tech-monitoring requests not specifically about Hacker News.

External Transmission

Medium
Category
Data Exfiltration
Content
### Step 2: Run the Actor
```bash
RESULT=$(curl -s -X POST "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms": ["TERM"], "maxResults": 30}')
echo "$RESULT" | jq '.'
Confidence
90% confidence
Finding
The presence of a direct HTTPS call to api.apify.com confirms that the skill relies on an external service, which is a legitimate but real security-relevant data egress path. In this context, the danger is not the domain itself but the combination of third-party transmission and URL-based token handling.

External Transmission

Medium
Category
Data Exfiltration
Content
### Step 2: Run the Actor
```bash
RESULT=$(curl -s -X POST "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms": ["TERM"], "maxResults": 30}')
echo "$RESULT" | jq '.'
Confidence
90% confidence
Finding
The presence of a direct HTTPS call to api.apify.com confirms that the skill relies on an external service, which is a legitimate but real security-relevant data egress path. In this context, the danger is not the domain itself but the combination of third-party transmission and URL-based token handling.

External Transmission

Medium
Category
Data Exfiltration
Content
### Step 3: Poll and fetch (if async)
```bash
RUN_ID=$(curl -s -X POST "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms": ["TERM"], "maxResults": 100}' | jq -r '.data.id')
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID?token=$APIFY_TOKEN" | jq -r '.data.status'
Confidence
90% confidence
Finding
This external call initiates an actor run against a third-party API and therefore constitutes genuine outbound transmission of user-requested data. Because the token is again placed in the URL, the skill increases the attack surface for secret leakage beyond the intended external processing.

External Transmission

Medium
Category
Data Exfiltration
Content
### Step 3: Poll and fetch (if async)
```bash
RUN_ID=$(curl -s -X POST "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms": ["TERM"], "maxResults": 100}' | jq -r '.data.id')
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID?token=$APIFY_TOKEN" | jq -r '.data.status'
Confidence
90% confidence
Finding
This external call initiates an actor run against a third-party API and therefore constitutes genuine outbound transmission of user-requested data. Because the token is again placed in the URL, the skill increases the attack surface for secret leakage beyond the intended external processing.

External Transmission

Medium
Category
Data Exfiltration
Content
RUN_ID=$(curl -s -X POST "https://api.apify.com/v2/acts/0UDODOnpTkxY3Oc90/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms": ["TERM"], "maxResults": 100}' | jq -r '.data.id')
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID?token=$APIFY_TOKEN" | jq -r '.data.status'
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID/dataset/items?token=$APIFY_TOKEN" | jq '.'
```
Confidence
88% confidence
Finding
Polling the actor-run status is an external network operation to Apify, but it appears to send minimal new user data beyond the run identifier. The main concern remains that the APIFY_TOKEN is in the URL, which can leak through logs even in low-sensitivity status checks.

External Transmission

Medium
Category
Data Exfiltration
Content
-H "Content-Type: application/json" \
  -d '{"searchTerms": ["TERM"], "maxResults": 100}' | jq -r '.data.id')
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID?token=$APIFY_TOKEN" | jq -r '.data.status'
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID/dataset/items?token=$APIFY_TOKEN" | jq '.'
```

### Step 4: Present results
Confidence
90% confidence
Finding
Fetching dataset items from Apify retrieves externally processed results and again authenticates with a URL token. This reinforces that the skill sends and receives data through a third-party boundary, and the URL-based secret handling makes accidental credential disclosure more likely.

Static analysis

No suspicious patterns detected.