Back to skill

Security audit

AI Upwork Proposal Writing & Job Tracking System

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent local Upwork proposal assistant, but its included shell filter has unsafe input handling that could let a crafted job field run local commands if the script is used.

Install only if you are comfortable with a local assistant storing proposal drafts and job history in files. Do not run pre-apply-check.sh on untrusted or copied job fields until its numeric validation, job-type allowlist, duplicate matching, and path layout are fixed; use manual review before any local log or vault updates.

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

T09 · Insecure Skill Coding Practices

Error
Location
pre-apply-check.sh:97
Finding

Arbitrary Command Execution Through Bash Arithmetic Evaluation

Content
View full analysis
MAX_DAYS_POSTED )); then disqualify "Job is $DAYS_POSTED days old, exceeds your maximum of $MAX_DAYS_POSTED days" fi ``` ```bash if (( CLIENT_SPEND == 0 )); then warn "Client has \$0 total spend on Upwork — no track record" fi if (( DAYS_POSTED >= 5 )); then warn "Job is $DAYS_POSTED days old — competition may have grown" fi ``` ### Technical Analysis The values supplied through `--budget`, `--client-spend`, and `--days-posted` are stored without validating that they contain only decimal digits. They are subsequently evaluated in Bash arithmetic contexts. Bash arithmetic evaluation does not treat variable contents as inert strings. Values can be recursively interpr ...[truncated 1745 chars]
Remediation
View remediation
&2 exit 2 } is_nonnegative_integer "$CLIENT_SPEND" || { printf '%s\n' "Error: --client-spend must be a non-negative integer." >&2 exit 2 } is_nonnegative_integer "$DAYS_POSTED" || { printf '%s\n' "Error: --days-posted must be a non-negative integer." >&2 exit 2 } ``` Additionally: - Verify that each value-taking option has a following argument before reading `$2`. - Reject signs, whitespace, arithmetic operators, brackets, variable names, and shell metacharacters. - Consider parsing validated decimal values with a language or utility that does not recursively evaluate shell expressions. - Add regression tests using malformed arithmetic expressions and command-substitution payloads. - Enable defensive shell options where compatible, while recognizing that `set -e` or `set -u` does not replace input validation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
pre-apply-check.sh:99
Finding

Budget Validation Bypass Through Unsupported Job Types

Content
View full analysis
Remediation
View remediation
&2 exit 2 ;; esac ``` Also: - Require `--job-type` rather than silently defaulting it when reliable listing data is expected. - Normalize case only if alternative capitalization is intentionally supported. - Add tests proving that unknown, empty, and mixed-case job types fail closed. - Print a qualified result only after confirming that exactly one applicable budget check ran successfully. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
pre-apply-check.sh:93
Finding

Blacklist Policy Bypass Because Job Descriptions Are Not Inspected

Content
View full analysis
Remediation
View remediation
&2 exit 2 } JOB_DESCRIPTION="$2" shift 2 ;; ``` ```bash SEARCH_TEXT="${JOB_TITLE,,}"$'\n'"${JOB_DESCRIPTION,,}" for keyword in "${BLACKLIST_KEYWORDS[@]}"; do if [[ "$SEARCH_TEXT" == *"$keyword"* ]]; then disqualify "Blacklisted keyword found: '$keyword'" fi done ``` For Bash 3.2 compatibility, use `tr` with `printf` rather than `${value,,}`. Avoid `echo` for untrusted content. Clearly define whether matching is substring-based, word-based, or phrase-based, and add tests for terms appearing only in the description. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
pre-apply-check.sh:233
Finding

Regex Injection in Duplicate Job-ID Detection

Content
View full analysis
/dev/null; then disqualify "Duplicate: Job ID $JOB_ID is already in .upwork/APPLICATION_LOG.md" fi else warn "Log file not found at $LOG_FILE — duplicate check skipped." warn "Make sure you run this script from the upwork-auto-apply/ root folder." fi ``` ### Technical Analysis `JOB_ID` is inserted directly into a Basic Regular Expression passed to `grep`. It is not validated or escaped, and fixed-string matching is not enabled. Regex metacharacters in a supplied ID can change the meaning of the search. A broad expression can produce false duplicate matches, while malformed syntax can make `grep` return an error. Because the script suppresses diagnostics and treats every nonzero result as “not found,” a regex error bypasses duplicate detection. The search is also not anchored to the exact job-ID field, increasing the chance of partial matches. ### Attack Path 1. An attacker or malformed data source supplies a job ID containing regex syntax. 2. The script interpolates that value into `JOB_ID:.*$JOB_ID`. 3. `grep` interprets the value as part of the regular expression rather than literal data. 4. A malformed expression returns an error that is hidden by `2>/dev/null`, or a crafted expression matches an unrelated record. 5. The script either misses an existing application or falsely disqualifies a new one. ### Impact Assessment No additional operating-system privilege is obtained. The attacker can affect application-tracking integrity by bypassing duplicate protections or causing denial of service against legitimate listings through false duplicate matches. ]]>
Remediation
View remediation
&2 exit 2 } ``` Use fixed-string matching: ```bash if grep -Fq -- "JOB_ID: $JOB_ID" "$LOG_FILE"; then disqualify "Duplicate: Job ID $JOB_ID is already present in the application log" fi ``` For variable spacing, parse the field rather than constructing a regex from untrusted input: ```bash if awk -v id="$JOB_ID" ' /^JOB_ID:[[:space:]]*/ { value = $0 sub(/^JOB_ID:[[:space:]]*/, "", value) if (value == id) found = 1 } END { exit !found } ' "$LOG_FILE"; then disqualify "Duplicate application" fi ``` Do not suppress unexpected `grep` or parser errors. Distinguish “no match” from an execution or syntax failure and fail closed when the log cannot be checked reliably. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
pre-apply-check.sh:70
Finding

Distributed Directory Layout Disables Duplicate Detection and Breaks Skill State Paths

Content
View full analysis
/dev/null; then disqualify "Duplicate: Job ID $JOB_ID is already in .upwork/APPLICATION_LOG.md" fi else warn "Log file not found at $LOG_FILE — duplicate check skipped." warn "Make sure you run this script from the upwork-auto-apply/ root folder." fi ``` The Skill instructions also reference paths that are absent from the audited package: ```text upwork-auto-apply/assets/FREELANCER_PROFILE.md upwork-auto-apply/assets/IDEAL_JOB_CRITERIA.md upwork-auto-apply/assets/PROPOSAL_VAULT.md upwork-auto-apply/.upwork/APPLICATION_LOG.md ``` The audited package instead places all seven files at its root: ```text APPLICATION_LOG.md FREELANCER_PROFILE.md IDEAL_JOB_CRITERIA.md PROPOSAL_VAULT.md README.md SKILL.md pre-apply-check.sh ``` ### Technical Analysis The implementation assumes the documented nested `assets/`, `scripts/`, and `.upwork/` directory layout, but the distributed artifact is flattened. Consequently, `.upwork/APPLICATION_LOG.md` does not exist when the script is run from the packaged project root. The missing-log condition is handled by issuing a warning and continuing. This is a fail-open behavior: duplicate detection is disabled, but the final result may still be `QUALIFIED`. In addition, the relative path depends on the caller's current working directory rather than the location of the script. Even if the expected structure is restored, launching the script from another directory can still disable the check. ### Attack Path 1. The user runs the package i ...[truncated 769 chars]
Remediation
View remediation
&2 exit 2 fi ``` Add an installation or startup self-check that verifies all required files, canonicalizes their paths, and confirms that read/write targets remain inside the project root. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
pre-apply-check.sh:155
Finding

Terminal Output Spoofing Through Untrusted Escape Sequences

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (1)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger phrase "Help me draft proposals for these jobs" is broad natural language that could be said in ordinary conversation, making accidental activation plausible. In a skill that processes pasted job listings and writes to local tracking files, unintended invocation could cause the assistant to analyze user-provided content and update proposal/application artifacts when the user did not mean to start the workflow.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.