Back to skill

Security audit

here.now

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent here.now publishing and cloud-storage helper, but it persists account credentials and contains script weaknesses that could expose tokens or write files outside an export folder.

Review before installing. Use only with files you intend to upload to here.now, prefer a pinned/local install, and avoid exporting Drive contents from untrusted shares until path containment is fixed. Treat ~/.herenow/credentials and .herenow/state.json as sensitive files; approve credential persistence explicitly and remove or revoke keys/tokens when no longer needed.

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

Warning
Location
SKILL.md:27
Finding
Unpinned Remote Global Installation Creates a Supply-Chain Execution Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:27` and `SKILL.md:83` **Vulnerability Type**: Unpinned remote dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash npx skills add heredotnow/skill --skill here-now -g ``` The same command is presented both as the recommended installation method and as the fallback when bundled scripts are unavailable. ### Technical Analysis The installation command retrieves and installs a mutable remote Skill revision without pinning the `skills` CLI or the requested Skill to an immutable version, commit, or verified artifact digest. The `-g` option also installs the retrieved content globally. Consequently, the code that executes during or after installation may differ from the artifact reviewed in this audit. A compromise of the package publisher, upstream repository, package registry, or dependency resolution process could introduce arbitrary code into a command users are explicitly instructed to run. This is a supply-chain weakness rather than evidence that the currently reviewed scripts contain a hidden payload. Nevertheless, the recommended installation process crosses the trust boundary from audited local content to mutable remote content. ### Attack Path 1. An attacker compromises the upstream package, repository, publisher account, or dependency-distribution channel. 2. The attacker publishes a modified version under the same mutable package or Skill identifier. 3. A user or Agent follows the documented `npx skills add ... -g` instruction. 4. The package manager retrieves and runs the attacker-controlled revision. 5. The malicious revision executes with the invoking user's permissions and is installed globally. ### Impact Assessment Successful exploitation can result in arbitrary code execution under the invoking account. Depending on that account's permissions, the payload could access local files, Agent credentials, environment variables, project data, and network resou ...[truncated 121 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the `skills` CLI to a reviewed, exact version rather than allowing `npx` to resolve a mutable latest release. 2. Pin the Skill to an immutable release, commit hash, or content digest. 3. Verify downloaded artifacts with a cryptographic checksum or signature before installation. 4. Prefer project-local installation over global installation unless global scope is explicitly required. 5. Use package-manager lockfiles where applicable and document the expected artifact digest. 6. Perform installation with the least-privileged account available and avoid lifecycle-script execution where the tooling supports that restriction. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/publish.sh:458
Finding
Anonymous Site Claim Credentials Are Written Without Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/publish.sh:458-487` **Vulnerability Type**: Insecure storage of sensitive claim credentials **Risk Level**: Medium ### Vulnerable Code ```bash # Save state. Merge into the existing entry (never replace it wholesale) so # a previously saved claimToken survives authenticated republishes. mkdir -p "$STATE_DIR" if [[ -f "$STATE_FILE" ]]; then STATE=$(cat "$STATE_FILE") else STATE='{"publishes":{}}' fi entry=$(echo "$STATE" | "$JQ_BIN" --arg s "$OUT_SLUG" '.publishes[$s] // {}') entry=$(echo "$entry" | "$JQ_BIN" --arg v "$SITE_URL" '.siteUrl = $v') LIVE_VERSION_ID=$(echo "$FIN_RESPONSE" | "$JQ_BIN" -r '.currentVersionId // empty') [[ -n "$LIVE_VERSION_ID" ]] && entry=$(echo "$entry" | "$JQ_BIN" --arg v "$LIVE_VERSION_ID" '.versionId = $v') entry=$(echo "$entry" | "$JQ_BIN" --arg v "$TARGET_ABS" '.path = $v') RESPONSE_CLAIM_TOKEN=$(echo "$RESPONSE" | "$JQ_BIN" -r '.claimToken // empty') RESPONSE_CLAIM_URL=$(echo "$RESPONSE" | "$JQ_BIN" -r '.claimUrl // empty') RESPONSE_EXPIRES=$(echo "$RESPONSE" | "$JQ_BIN" -r '.expiresAt // empty') [[ -n "$RESPONSE_CLAIM_TOKEN" ]] && entry=$(echo "$entry" | "$JQ_BIN" --arg v "$RESPONSE_CLAIM_TOKEN" '.claimToken = $v') [[ -n "$RESPONSE_CLAIM_URL" ]] && entry=$(echo "$entry" | "$JQ_BIN" --arg v "$RESPONSE_CLAIM_URL" '.claimUrl = $v') [[ -n "$RESPONSE_EXPIRES" ]] && entry=$(echo "$entry" | "$JQ_BIN" --arg v "$RESPONSE_EXPIRES" '.expiresAt = $v') STATE=$(echo "$STATE" | "$JQ_BIN" --arg slug "$OUT_SLUG" --argjson e "$entry" '.publishes[$slug] = $e') echo "$STATE" | "$JQ_BIN" '.' > "$STATE_FILE" ``` ### Technical Analysis The state file stores `claimToken` and `claimUrl`, which are bearer-like credentials capable of authorizing actions involving an anonymous site. The script creates `.herenow` and writes `.herenow/state.json` without setting explicit permissions on either object. The resulting mode is controlled by the caller's current `umask`. Under a permissiv ...[truncated 1396 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Set `umask 077` before creating or updating state containing secrets. 2. Create the state directory with mode `700`, for example: ```bash install -d -m 700 "$STATE_DIR" ``` 3. Create a temporary state file inside the same directory with mode `600`, validate the generated JSON, and atomically rename it over the final file. 4. Explicitly enforce `chmod 600 "$STATE_FILE"` when handling an existing state file. 5. Refuse to write through symbolic links and verify that the state directory and file are owned by the current user. 6. Document `.herenow/state.json` as a sensitive credential-bearing file, not merely a cache, and ensure it is excluded from source control and publication. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/drive.sh:323
Finding
Drive Export Allows Remote Paths to Escape the Destination Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/drive.sh:323-332` **Vulnerability Type**: Path traversal and arbitrary local file write **Risk Level**: High ### Vulnerable Code ```bash while IFS= read -r p; do [[ -n "$p" ]] || continue rel="$p" [[ -n "$prefix" ]] && rel="${p#$prefix/}" out="$to/$rel" if [[ "$dry" -eq 1 ]]; then echo "download $p -> $out" else mkdir -p "$(dirname "$out")" curl -fsS "$BASE_URL/api/v1/drives/$id/files/$(urlenc_path "$p")" "${auth_header[@]}" -o "$out" fi total=$((total + 1)) done < <(echo "$files" | "$JQ_BIN" -r '.files[].path') ``` ### Technical Analysis File paths received from the Drive API are directly concatenated with the user-selected destination: ```bash out="$to/$rel" ``` The script does not reject: - Absolute paths - `..` path components - `.` components - Empty or malformed path components - Paths that resolve through symbolic links outside the export root For example, a remote path such as `../../.ssh/authorized_keys` produces an output path resembling: ```text <destination>/../../.ssh/authorized_keys ``` The subsequent `mkdir -p` and `curl -o` operations follow the filesystem's normal path resolution and can therefore write outside the intended export directory. Prefix stripping does not provide containment and may yield unexpected relative paths when returned entries do not actually begin with the expected prefix. Because the path originates in remote Drive metadata, this is a trust-boundary violation. It is especially relevant when exporting from a Drive or scoped share whose contents may be controlled by another party. ### Attack Path 1. An attacker creates or controls a Drive entry with a path containing traversal components, such as `../../target-file`. 2. The victim receives access to that Drive or Drive share and runs the export command. 3. The API returns the attacker-controlled path in `.files[].path`. 4. The script constructs `out="$to/$rel"` without v ...[truncated 889 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject absolute remote paths and any path containing `.` or `..` components. 2. Require every returned path to match the requested prefix before stripping that prefix. 3. Canonicalize the export root once and verify that each canonical destination remains beneath it. 4. Do not rely only on string-prefix comparison; include a path-separator boundary in containment checks. 5. Detect and reject symbolic links in every destination path component, or use directory-relative safe file APIs that do not follow symlinks. 6. Download to a securely created temporary file under the validated destination directory and atomically rename it into place. 7. Reject duplicate normalized paths so multiple remote names cannot overwrite the same local file. 8. Add tests covering absolute paths, traversal paths, misleading prefixes, repeated separators, and symlink escapes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publish.sh:376
Finding
API Bearer Credential Is Forwarded to an Unvalidated Finalize URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/publish.sh:376-377` and `scripts/publish.sh:441-448` **Vulnerability Type**: Credential disclosure through an unvalidated response URL **Risk Level**: High ### Vulnerable Code ```bash VERSION_ID=$(echo "$RESPONSE" | "$JQ_BIN" -r '.upload.versionId') FINALIZE_URL=$(echo "$RESPONSE" | "$JQ_BIN" -r '.upload.finalizeUrl') ``` The API-provided value is later used while forwarding the account authorization header: ```bash # Step 3: Finalize echo "finalizing..." >&2 FIN_RESPONSE=$(curl -sS -X POST "$FINALIZE_URL" \ "${AUTH_ARGS[@]+"${AUTH_ARGS[@]}"}" \ "${CLIENT_ARGS[@]+"${CLIENT_ARGS[@]}"}" \ "${ACCOUNT_ARGS[@]+"${ACCOUNT_ARGS[@]}"}" \ -H "content-type: application/json" \ -d "{\"versionId\":\"$VERSION_ID\"}") ``` Earlier, `AUTH_ARGS` is populated as follows: ```bash AUTH_ARGS=() if [[ -n "$API_KEY" ]]; then AUTH_ARGS=(-H "authorization: Bearer $API_KEY") fi ``` ### Technical Analysis The script protects user-supplied `--base-url` values by refusing to send an API key to a non-default base URL unless an explicit override is supplied. That protection is not extended to `FINALIZE_URL`, which is parsed from the create/update API response and used directly. When authenticated publishing is active, the request to `FINALIZE_URL` includes the full account bearer key regardless of the URL's scheme, host, port, or path. If the response is compromised or malformed, the API key can be sent to an attacker-controlled endpoint. TLS alone does not prevent this issue: an attacker-controlled HTTPS host receives the authorization header legitimately once the script chooses that URL. The URL must therefore be treated as untrusted data and validated before privileged headers are attached. ### Attack Path 1. The script performs an authenticated create or update request. 2. A compromised API service, reverse proxy, test endpoint, or explicitly allowed alternate base returns a malicious `upload.finalize ...[truncated 831 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse `FINALIZE_URL` and require: - HTTPS - The exact expected hostname - The expected port - An allowed finalize path prefix - No embedded user information 2. Prefer constructing the finalize endpoint locally from validated identifiers rather than accepting an arbitrary absolute URL. 3. If cross-host finalization is required, use a narrowly scoped, one-time finalize token instead of the account API key. 4. Attach the account authorization header only after origin validation succeeds. 5. Apply equivalent validation to all API-provided upload and callback URLs. Upload URLs should receive only the headers explicitly returned for that upload and never the account bearer credential. 6. Add negative tests in which the API returns off-origin, non-HTTPS, user-info-bearing, and unexpected-port finalize URLs. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code clearly targets here.now Drive APIs (`/api/v1/drives`, `/files`, `/tokens`) rather than site publishing APIs. Its primary behavior is file storage administration: creating drives, listing drives/files, uploading files, importing folders, exporting/downloading files, removing files, and managing access tokens. While file upload could be a supporting piece of a publishing system, the declared description centers on publishing websites/files to live URLs and managing sites/workspaces. Those core behaviors are absent from the code shown. Instead, the code exposes materially different capabilities—remote file storage and token-based sharing—without any logic for creating or updating public websites, assigning slugs, custom domains, or workspace-hosted sites. Therefore the description does not accurately represent this code chunk.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill invokes shell commands and network operations but does not declare an explicit tool scope or allowed-tools policy. That omission weakens least-privilege controls and makes it easier for an agent runtime to grant broader capabilities than necessary, increasing the blast radius if the skill is misused or compromised.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrases are very broad, covering common requests like 'share this', 'put this online', and 'build a chatbot'. Overbroad activation criteria can cause the skill to engage in contexts where the user did not intend external publication or storage, leading to accidental data disclosure.

Session Persistence

Medium
Category
Rogue Agent
Content
Publish HTML, documents, images, PDFs, videos, and static files to live
  URLs at {slug}.here.now or custom domains. Use when asked to "publish
  this", "host this", "deploy this", "share this on the web", "make a
  website", "put this online", "create a webpage", "generate a URL",
  "build a chatbot", "password protect this site", "make this site
  private", or "share this site with only certain people". here.now also
  includes workspaces — shared team accounts where Sites belong to the
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
Using `npx skills add heredotnow/skill --skill here-now -g` without a pinned version allows installation of whatever package version is current at execution time. This creates a supply-chain risk where a malicious or compromised upstream release could silently change skill behavior or introduce code execution.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
A second unpinned `npx skills add` instruction repeats the same supply-chain exposure, enabling unreviewed remote code or content changes at install time. In a skill ecosystem, this is especially risky because agents may follow setup instructions automatically.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Authenticated updates require a saved API key.

**Stale-base protection.** The live Site may have changed since your local files were published — the owner can edit it from other tools (another agent, the here.now Studio, a teammate). The script records the live `versionId` in `.herenow/state.json` after each publish and sends it as `baseVersionId` on the next update of the same slug from the same directory; if the live Site moved past it, the update is rejected with `code: "version_conflict"` naming the live version and what created it. When that happens, relay the message to the user and offer to (a) read the live files with `GET /api/v1/publish/{slug}/files` (lists them with a `url` each) and `GET /api/v1/publish/{slug}/files/{path}` (the bytes; owner API key, works for password-protected and restricted Sites without the visitor password), reconcile them into the local files, and republish, or (b) re-run with `--overwrite` to replace the live version anyway. Before editing local files for an authenticated Site you haven't touched recently, check for drift first: `GET /api/v1/publish/{slug}` returns `currentVersionId` plus `currentVersionSource` and `currentVersionCreatedAt` (what changed it and when, e.g. `studio`) — if the id differs from your state file's `versionId`, read the live files before editing. The published version is the shared truth; never fetch the public URL to read an owned Site (it is gated for protected Sites) and never ask the user for a visitor password to read their own Site. Anonymous Sites can't call these endpoints; they rely on the saved state and server enforcement. Omitting `baseVersionId` (or using `--overwrite`) is an unchecked full replacement — today's default for raw API callers.

Every publish records an immutable version. If the user asks to see earlier versions of a Site, undo a publish, or roll back: list history with `GET /api/v1/publish/{slug}/versions` and restore instantly with `POST /api/v1/publish/{slug}/versions/{versio
...[truncated 25 chars]
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.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The instructions direct the agent to store an API key on disk immediately, but they do not require an explicit user-facing warning or consent for persistent local credential storage. Persisting credentials can outlive the session and expose account access to later processes, users, or compromise of the host.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
To store a key, write it to the credentials file:

```bash
mkdir -p ~/.herenow && echo "{API_KEY}" > ~/.herenow/credentials && chmod 600 ~/.herenow/credentials
```

**IMPORTANT**: After receiving an API key, save it immediately — run the command above yourself. Do not ask the user to run it manually. Avoid passing the key via CLI flags (e.g. `--api-key`) in interactive sessions; the credentials file is the preferred storage method.
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
To store a key, write it to the credentials file:

```bash
mkdir -p ~/.herenow && echo "{API_KEY}" > ~/.herenow/credentials && chmod 600 ~/.herenow/credentials
```

**IMPORTANT**: After receiving an API key, save it immediately — run the command above yourself. Do not ask the user to run it manually. Avoid passing the key via CLI flags (e.g. `--api-key`) in interactive sessions; the credentials file is the preferred storage method.
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
mkdir -p ~/.herenow && echo "{API_KEY}" > ~/.herenow/credentials && chmod 600 ~/.herenow/credentials
```

**IMPORTANT**: After receiving an API key, save it immediately — run the command above yourself. Do not ask the user to run it manually. Avoid passing the key via CLI flags (e.g. `--api-key`) in interactive sessions; the credentials file is the preferred storage method.

Never commit credentials or local state files (`~/.herenow/credentials`, `.herenow/state.json`) to source control.
Confidence
90% confidence
Finding
The instruction to save the returned API key yourself and not ask the user to do it encourages autonomous persistence of credentials. That removes an opportunity for informed consent and can cause agents to modify the local environment in security-sensitive ways without clear approval.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The email and one-time code flow collects authentication data and transmits it to the external service without mandating a privacy notice or explicit consent. This can surprise users and normalize handing sensitive login material to the agent, which raises account takeover and privacy concerns if mishandled.

External Transmission

Medium
Category
Data Exfiltration
Content
2. Request a one-time sign-in code:

```bash
curl -sS https://here.now/api/auth/agent/request-code \
  -H "content-type: application/json" \
  -d '{"email": "user@example.com"}'
```
Confidence
88% confidence
Finding
This workflow transmits the user's email address to an external service, which is a real data egress event. In context that may be expected for authentication, but without explicit consent and scoping it still presents privacy and data-handling risk.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
-d '{"email":"user@example.com","code":"ABCD-2345"}'
```

5. Save the returned `apiKey` yourself (do not ask the user to do this):

```bash
mkdir -p ~/.herenow && echo "{API_KEY}" > ~/.herenow/credentials && chmod 600 ~/.herenow/credentials
Confidence
90% confidence
Finding
This repeats the same autonomous credential-storage pattern after code verification, instructing the agent to persist the API key without involving the user. In the context of authentication material, silent persistence materially increases risk.

Behavior Manipulation

Medium
Category
Prompt Injection
Content
- When `publish_result.account_url` is non-empty (workspace publishes), share it as the primary team URL alongside `siteUrl`.
- When `publish_result.auth_mode=authenticated`: tell the user the site is **permanent** and saved to their account. No claim URL is needed.
- When `publish_result.auth_mode=anonymous`: tell the user the site **expires in 24 hours**. Share the claim URL (if `publish_result.claim_url` is non-empty and starts with `https://`) so they can keep it permanently. Copy it byte-for-byte as a clickable link — never shorten, redact, summarize, or replace any part of it with `...`; a modified claim link will not work. Warn that claim tokens are only returned once and cannot be recovered.
- Never tell the user to inspect `.herenow/state.json` for claim URLs or auth status.

For Drives:
Confidence
75% confidence
Finding
Subtle instructions detected that may alter agent decision-making or introduce hidden biases.

External Transmission

Medium
Category
Data Exfiltration
Content
tmp=$(mktemp)
  local code
  if [[ -n "$body" ]]; then
    code=$(curl -sS -o "$tmp" -w "%{http_code}" -X "$method" "$url" "${auth_header[@]}" -H "content-type: application/json" -d "$body")
  else
    code=$(curl -sS -o "$tmp" -w "%{http_code}" -X "$method" "$url" "${auth_header[@]}")
  fi
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
upload_id=$(echo "$upload" | "$JQ_BIN" -r '.uploadId')
  # `|| http_code=000`: a connection-level failure would otherwise exit the
  # script under set -e with only curl's message, before the hint below.
  http_code=$(curl -sS -o /dev/null -w "%{http_code}" -X PUT "$upload_url" -H "Content-Type: $ct" --data-binary "@$local_file") || http_code="000"
  [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]] || die "upload failed for $path (HTTP $http_code). The upload URL PUTs directly to *.r2.cloudflarestorage.com, not to here.now; if this environment restricts outbound network access, allow that host as well as here.now."
  api_json POST "$BASE_URL/api/v1/drives/$id/files/finalize" "$("$JQ_BIN" -n --arg u "$upload_id" '{uploadId:$u}')" | "$JQ_BIN" .
}
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  echo "publishing from Drive..." >&2
  RESPONSE=$(curl -sS -X POST "$BASE_URL/api/v1/publish/from-drive" \
    -H "authorization: Bearer $API_KEY" \
    -H "x-herenow-client: $CLIENT_HEADER_VALUE" \
    -H "content-type: application/json" \
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# Step 1: Create/update publish
echo "creating publish ($file_count files)..." >&2
RESPONSE=$(curl -sS -X "$METHOD" "$URL" \
  "${AUTH_ARGS[@]+"${AUTH_ARGS[@]}"}" \
  "${CLIENT_ARGS[@]+"${CLIENT_ARGS[@]}"}" \
  "${ACCOUNT_ARGS[@]+"${ACCOUNT_ARGS[@]}"}" \
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
[[ -n "$upload_ct" ]] && ct_args=(-H "Content-Type: $upload_ct")

  # `|| http_code=000`: a connection-level failure (proxy refusing CONNECT,
  # DNS, TLS) exits curl non-zero, which under set -e would kill the script
  # here with only curl's own message. Fall through so the count and the
  # hint below are reached.
  http_code=$(curl -sS -o /dev/null -w "%{http_code}" -X PUT "$upload_url" \
Confidence
90% confidence
Finding
The script blindly PUTs file contents to upload_url values returned by the server, with no validation of the destination host or scheme. If the API endpoint is compromised, misconfigured, or replaced via the non-default base URL override, it can supply attacker-controlled upload URLs and cause arbitrary local files selected for publishing to be exfiltrated to an unintended external host.

External Transmission

Medium
Category
Data Exfiltration
Content
# Step 3: Finalize
echo "finalizing..." >&2
FIN_RESPONSE=$(curl -sS -X POST "$FINALIZE_URL" \
  "${AUTH_ARGS[@]+"${AUTH_ARGS[@]}"}" \
  "${CLIENT_ARGS[@]+"${CLIENT_ARGS[@]}"}" \
  "${ACCOUNT_ARGS[@]+"${ACCOUNT_ARGS[@]}"}" \
Confidence
86% confidence
Finding
The script POSTs to FINALIZE_URL taken directly from the server response, again without validating that the URL belongs to the expected here.now service. If a malicious or untrusted API base is used, this can send authorization headers and workspace/account-selection headers to an attacker-controlled endpoint, enabling credential leakage or unauthorized request replay.

Static analysis

No suspicious patterns detected.