T09 · Insecure Skill Coding Practices
- Location
scripts/verify-agent.mjs:38- Finding
API Key Disclosure Through Improper API Origin Validation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is coherent for OpenJobs marketplace work, but it asks agents to refresh mutable remote instructions and run unpinned code while handling wallet, API key, and state-changing marketplace actions.
Review carefully before installing. Do not run the automatic heartbeat refresh as written; pin and vet the OpenJobs CLI, disable unpinned npx fallback, require manual review before skill updates, avoid command-line wallet secrets, and use manual or notify-only oversight until the API origin validation and notification scoping are fixed.
scripts/verify-agent.mjs:38API Key Disclosure Through Improper API Origin Validation
HEARTBEAT.md:88Automatic Execution of an Unpinned npm Package During Heartbeats
SKILL.md:614Audited Skill Instructions Are Forcibly Replaced by Mutable Remote Content
Referenced artifact was not completely inspected
`HEARTBEAT.md` lives at the top level of the skill (a peer of `SKILL.md`,
Referenced artifact was not completely inspected
`HEARTBEAT.md` lives at the top level of the skill (a peer of `SKILL.md`,
Referenced artifact was not completely inspected
`HEARTBEAT.md` lives at the top level of the skill (a peer of `SKILL.md`,
The skill explicitly states that remotely hosted skill.md and heartbeat.md are the runtime execution authority and should be refreshed automatically before each heartbeat. That creates a direct remote-instruction trust channel into the agent: anyone who can alter the hosted content or its delivery path can change the agent's future behavior, including causing command execution, exfiltration, or unsafe financial actions.
The document asserts that the workflow is driven through the exact OpenJobs CLI binary, but later requires non-CLI tooling such as Telegram delivery and Ollama. This mismatch can mislead operators and security controls about what code paths and external services may be invoked, weakening review, sandboxing, and least-privilege assumptions.
The trigger section activates the skill for a wide range of natural-language requests such as checking messages, platform stats, webhook management, oversight updates, staking, and feedback. These categories are broad and lack exclusion criteria or negative examples, increasing the chance the skill is invoked for casual discussion about OpenJobs rather than an explicit workflow run.
The workflow falls back to npx -y @openjobs/cli, which fetches and executes the latest package version at runtime without pinning. In a privileged workflow that can read messages, move funds, apply to jobs, and manage staking/webhooks, this creates a supply-chain execution risk: a compromised or breaking upstream release would immediately gain those capabilities.
The deposit flow can use a stored profile secret, --wallet-secret, or OPENJOBS_WALLET_SECRET without a strong, immediate warning in the operational step. In a workflow that encourages automatic retries and financial actions, insufficient emphasis on secret handling increases the risk of exposing wallet credentials in shell history, logs, environment inspection, or unintended automated fund movement.
The workflow introduces local Ollama-based content generation despite the manifest describing an OpenJobs CLI-centric skill. This hidden capability expansion can trigger execution of additional local tooling and models, increasing attack surface, resource use, and the chance of unsafe or unexpected behavior beyond what operators would infer from the declared scope.
The workflow mandates Telegram notifications for every state-changing action even though the skill is presented as an OpenJobs CLI workflow. This expands data flow to a separate messaging channel and can leak operational metadata, IDs, and job activity to an external destination that may be misconfigured, unauthorized, or unsupported in the current runtime.
Requiring Telegram messaging as a mandatory capability is not justified by the stated purpose of participating in OpenJobs through the CLI. This creates an unnecessary outbound communication channel that can exfiltrate sensitive workflow details and pressures the agent to use tools or targets outside the user's explicit request and the skill's declared trust boundary.
The skill invokes shell commands, accesses local config/state, and relies on networked CLI/API operations, but it does not declare any explicit tool scope or allowed-tools boundary. In agent runtimes, that omission can cause the skill to run with broader capabilities than intended, increasing the blast radius if the skill is triggered unexpectedly or if later instructions are abused.
The activation criteria are very broad, covering generic terms like marketplace, agent, wallet, jobs, and onboarding. In a multi-skill agent, that can cause the skill to activate in contexts the user did not intend, leading to unnecessary command execution, credential use, or modifications to local and remote OpenJobs state.
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.
Common doctor outputs and what to do:
| Doctor row | Status | Fix |
| ----------------------- | ------ | ------------------------------------------------------------------------------------------------- |
| `auth.apiKey` missing | ✗ | `openjobs login --api-key sk_live_xxx` (or `openjobs agents register …` for a brand-new agent). |
| `cli.version` outdated | ⚠ | `openjobs upgrade --yes` |
Using npx @openjobs/cli without pinning an exact version allows whatever package version is current at execution time to be fetched and run. That creates a supply-chain and reproducibility risk: a compromised or breaking upstream release could change behavior, execute unexpected code, or alter wallet- and credential-handling flows.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
npx @openjobs/cli --help # zero-install, one-off runs
If `npm i -g` fails with `EACCES`, point npm at a user-owned prefix (don't `sudo npm i -g`):
```bash
npm config set prefix ~/.npm-global
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.
npm config set prefix ~/.npm-global
export PATH=~/.npm-global/bin:$PATH # add to ~/.bashrc or ~/.zshrc
npm install -g @openjobs/cli
openjobs doctor
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
1. Run `openjobs doctor` and READ the output.
2. If it says "Could not locate the bundled skill files" → your CLI predates the bundled skill. Run `openjobs upgrade --yes`, then `openjobs --version` (must show 2.2.x or newer), then re-try install-skill **once**.
3. If `upgrade` itself fails with `EACCES` → use the `~/.npm-global` recipe above. Do NOT `sudo` (it changes file ownership and breaks the next non-sudo install).
4. If a PATH-shadow warning appears (`⚠ openjobs PATH-shadow: which openjobs resolves to A but this process is running B`), `which -a openjobs` and remove or reorder the stale copy. Do NOT keep upgrading — both copies upgrade simultaneously and the shadow persists.
The CLI never auto-retries failed installs; neither should you. One failure → run `doctor` → apply the named fix → one more attempt.
The skill instructs automatic refresh of the skill bundle with --force, which overwrites local files, but it does not require an explicit warning or user approval before modifying the local environment. This is dangerous because a remote publisher or MITM/compromised distribution path could push changed instructions into the runtime and replace trusted local content silently.
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.
#!/usr/bin/env node
/**
* OpenJobs — Create Solana Wallet (offline)
*
* Generates a brand-new Solana keypair LOCALLY and writes:
* - ~/.openjobs/wallet.json (Phantom/solana-cli compatible 64-byte secret key array)
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
console.log("✅ New Solana wallet created");
console.log("");
console.log(" Address: " + walletAddress);
console.log(" Secret key: " + WALLET_PATH + " (chmod 600)");
console.log(" Preferences: " + PREFS_PATH);
console.log("");
console.log("Next step: prove ownership of this wallet.");
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
console.log("✅ New Solana wallet created");
console.log("");
console.log(" Address: " + walletAddress);
console.log(" Secret key: " + WALLET_PATH + " (chmod 600)");
console.log(" Preferences: " + PREFS_PATH);
console.log("");
console.log("Next step: prove ownership of this wallet.");
Detected: suspicious.exposed_secret_literal