Back to skill

Security audit

OrchardOS

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a legitimate project-management plugin, but it has serious security and privacy risks that should be reviewed before installation.

Review before installing. Use only in an environment where gateway tokens, task content, run output, and project knowledge can be treated as sensitive persistent data. Disable contextInjection unless you explicitly want external embeddings, avoid storing API keys in plugin config until /orchard/config redacts them, keep the standalone UI loopback-only, and fix the dashboard XSS and document-fetch SSRF issues before using it with privileged tokens or confidential projects.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T09 · Insecure Skill Coding Practices

Error
Location
src/ui/dashboard.html:715
Finding
Persistent Cross-Site Scripting in Project Navigation Can Compromise Gateway Credentials<![CDATA[ ## Vulnerability Details **File Location**: `src/ui/dashboard.html:464-477`, `src/ui/dashboard.html:715-723`; generated copy in `src/ui/dashboard.generated.ts:4` **Vulnerability Type**: Persistent cross-site scripting caused by unsafe JavaScript-context interpolation **Risk Level**: High ### Vulnerable Code ```javascript // AUTH is injected by the server when using the embedded gateway route. // When empty (standalone UI server), the token is read from localStorage const AUTH_INJECTED = ''; function getStoredToken() { return AUTH_INJECTED || localStorage.getItem('orchard-token') || ''; } function setStoredToken(t) { localStorage.setItem('orchard-token', t.trim()); } function clearStoredToken() { localStorage.removeItem('orchard-token'); } ``` ```javascript els.sidebarProjects.innerHTML = state.projects.length ? state.projects.map((project) => ` <div class="sidebar-item ${activeProjectId === project.id ? 'active' : ''}" onclick="navigate('#project/${encodeURIComponent(project.id)}')"> <div class="sidebar-item-main"> <div class="sidebar-item-title">${escapeHtml(project.name)}</div> <div class="sidebar-item-meta">${percent(project.completion_score)}% complete</div> </div> ${badge(projectStatus(project))} </div> `).join('') : '<div class="empty">No projects yet.</div>'; ``` ### Technical Analysis Project IDs originate from authenticated API input and are persisted in SQLite. The dashboard later inserts each ID into an inline JavaScript event handler assigned through `innerHTML`. `encodeURIComponent()` is URL encoding, not JavaScript-string escaping. In particular, it does not encode apostrophes. A crafted project ID can therefore terminate the single-quoted argument passed to `navigate()` and introduce additional JavaScript. An attacker can use an alphanumeric Base64 payload with an expression such as `eval(atob(...))`, avoiding many characters that `encodeURIComponent()` would otherwise encode. The issue is more ...[truncated 1836 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all inline event handlers and avoid embedding database values into executable JavaScript: ```javascript const row = document.createElement('div'); row.className = 'sidebar-item'; row.addEventListener('click', () => { navigate(`#project/${encodeURIComponent(project.id)}`); }); ``` 2. Construct untrusted content using `textContent`, DOM methods, or a template system with contextual escaping rather than `innerHTML`. 3. Apply separate encoders for HTML text, HTML attributes, URLs, and JavaScript strings; HTML escaping alone is not sufficient for executable contexts. 4. Stop storing a gateway bearer token in `localStorage`. Prefer an `HttpOnly`, `Secure`, and `SameSite=Strict` cookie or a short-lived, narrowly scoped UI session. 5. Add a restrictive Content Security Policy that disallows inline scripts and event handlers, for example with nonce-based scripts and no `unsafe-inline`. 6. Regenerate `src/ui/dashboard.generated.ts` after correcting `dashboard.html`. 7. Add security tests using project IDs containing apostrophes, parentheses, HTML metacharacters, and encoded JavaScript payloads. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/api/routes.ts:615
Finding
Authenticated Configuration Endpoint Returns the Gemini API Key<![CDATA[ ## Vulnerability Details **File Location**: `src/api/routes.ts:615-625`; secret field declared at `src/config.ts:4-8` **Vulnerability Type**: Sensitive configuration disclosure **Risk Level**: High ### Vulnerable Code ```typescript export interface ContextInjectionConfig { enabled?: boolean; provider?: string; model?: string; apiKey?: string; topK?: number; minScore?: number; autoExtract?: boolean; } ``` ```typescript // ── /orchard/config (GET current plugin config) ───────────────────────── api.registerHttpRoute({ path: "/orchard/config", auth: "gateway", match: "exact", handler: async (_req: IncomingMessage, res: ServerResponse) => { const cfg = getCfg ? getCfg() : ({} as OrchardConfig); const allowModelOverride = cfg.allowModelOverride === true; return jsonOk(res, { ...cfg, allowModelOverride }); }, }); ``` ### Technical Analysis The `/orchard/config` handler serializes the entire `OrchardConfig` object using object spread. The configuration type contains `contextInjection.apiKey`, so a key configured directly in the plugin entry is returned verbatim in the HTTP response. Gateway authentication does not eliminate the need to redact secrets. A principal may legitimately require access to the dashboard or task APIs without needing permission to recover third-party provider credentials. Returning the raw configuration violates least privilege and unnecessarily increases the consequences of gateway-token compromise. ### Attack Path 1. An attacker obtains a valid gateway bearer token or compromises an authenticated dashboard context. 2. The attacker sends: ```http GET /orchard/config Authorization: Bearer <gateway-token> ``` 3. The route serializes the complete plugin configuration. 4. If `contextInjection.apiKey` was configured, the response contains the API key in plaintext. 5. The attacker extracts and reuses the key against the associated Google API account. ### Impact Assessment The attacke ...[truncated 543 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace full-object serialization with an explicit allowlist of non-sensitive properties: ```typescript return jsonOk(res, { queueIntervalMs: cfg.queueIntervalMs, allowModelOverride: cfg.allowModelOverride === true, contextInjection: { enabled: cfg.contextInjection?.enabled === true, provider: cfg.contextInjection?.provider, model: cfg.contextInjection?.model, topK: cfg.contextInjection?.topK, minScore: cfg.contextInjection?.minScore, autoExtract: cfg.contextInjection?.autoExtract, apiKeyConfigured: Boolean( cfg.contextInjection?.apiKey || process.env.GEMINI_API_KEY ), }, }); ``` 2. Define a reusable secret-redaction function and apply it before any configuration is logged or returned. 3. Separate read-only dashboard authorization from secret-management and operator-administration privileges. 4. Rotate any key that may already have been exposed through this route. 5. Add regression tests asserting that response bodies never contain configured API keys or known secret marker values. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/api/routes.ts:48
Finding
DNS Rebinding Can Bypass Document-Fetch SSRF Protection<![CDATA[ ## Vulnerability Details **File Location**: `src/api/routes.ts:48-103`, `src/api/routes.ts:712-723` **Vulnerability Type**: Server-side request forgery through incomplete hostname validation **Risk Level**: High ### Vulnerable Code ```typescript function isPrivateHost(hostname: string): boolean { // Block loopback, link-local, and RFC-1918 private ranges if (hostname === "localhost") return true; const v4 = hostname.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/); if (v4) { const [, a, b] = v4.map(Number); if (a === 127 || a === 10) return true; if (a === 172 && b >= 16 && b <= 31) return true; if (a === 192 && b === 168) return true; if (a === 169 && b === 254) return true; // link-local if (a === 0) return true; } if (hostname === "::1" || hostname.startsWith("fc") || hostname.startsWith("fd")) return true; return false; } function fetchUrl(url: string, maxChars = 2000, _redirectDepth = 0): Promise<string> { return new Promise((resolve, reject) => { if (_redirectDepth > 3) return reject(new Error("Too many redirects")); let parsed: URL; try { parsed = new URL(url); } catch { return reject(new Error("Invalid URL")); } if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { return reject(new Error("Only http/https URLs are allowed")); } if (isPrivateHost(parsed.hostname)) { return reject(new Error("Requests to private/internal addresses are not allowed")); } const lib = url.startsWith("https") ? https : http; const req = lib.get(url, { timeout: 10000, headers: { "User-Agent": "OrchardOS/1.0" } }, (res) => { if ( res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location ) { res.resume(); return fetchUrl( res.headers.location, maxChars, _redirectDepth + 1 ).then(resolve).catch(reject); } // Resp ...[truncated 2681 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer an explicit allowlist of trusted documentation hosts, such as the official OpenClaw documentation domain. 2. Resolve the hostname before connecting and reject every address that is not globally routable. 3. Validate IPv4, IPv6, IPv4-mapped IPv6, loopback, private, link-local, multicast, unspecified, reserved, and documentation ranges using a maintained IP-address library. 4. Pin the request to the validated address while retaining the original hostname for TLS SNI and certificate validation, preventing resolution from changing between validation and connection. 5. Resolve and validate every redirect target independently. Resolve relative redirects against the current URL before validation. 6. Disable redirects unless they are required, or restrict them to the same allowlisted origin. 7. Apply outbound network controls at the operating-system or container level so the plugin process cannot reach metadata services or internal administration networks. 8. Log rejected destinations without logging credentials or sensitive response bodies. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/kb/knowledge.ts:15
Finding
Ambient Gemini Credential Implicitly Enables Transmission of Project and Task Data<![CDATA[ ## Vulnerability Details **File Location**: `src/kb/knowledge.ts:15-43`, `src/kb/knowledge.ts:69-84`, `src/kb/knowledge.ts:88-108`; calling context at `src/queue/runner.ts:886-891` **Vulnerability Type**: Implicit external transmission of potentially sensitive content **Risk Level**: Medium ### Vulnerable Code ```typescript function getApiKey(cfg: OrchardConfig): string { return cfg.contextInjection?.apiKey || process.env.GEMINI_API_KEY || ""; } function isEnabled(cfg: OrchardConfig): boolean { if (cfg.contextInjection?.enabled === false) return false; // Conservative default: only active if API key present return !!getApiKey(cfg); } export async function embedText( text: string, cfg: OrchardConfig ): Promise<number[]> { const apiKey = getApiKey(cfg); if (!apiKey) return []; const model = cfg.contextInjection?.model ?? "gemini-embedding-001"; const body = JSON.stringify({ content: { parts: [{ text }] } }); return new Promise((resolve) => { const url = `https://generativelanguage.googleapis.com/v1beta/models/` + `${model}:embedContent?key=${apiKey}`; const req = https.request( url, { method: "POST", headers: { "Content-Type": "application/json" } }, (res) => { let data = ""; res.on("data", (c: Buffer) => { data += c; }); res.on("end", () => { try { const json = JSON.parse(data); const values = json?.embedding?.values; resolve(Array.isArray(values) ? values : []); } catch { resolve([]); } }); } ); req.write(body); req.end(); }); } ``` ```typescript export async function addKnowledge( db: Database.Database, cfg: OrchardConfig, projectId: string, content: string, source: string, taskId?: number ): Promise<void> { let embeddingJson: string | null = null; if (isEnabled(cfg)) { try { const vec = await embedText(co ...[truncated 3116 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit opt-in: ```typescript function isEnabled(cfg: OrchardConfig): boolean { return cfg.contextInjection?.enabled === true && Boolean(getApiKey(cfg)); } ``` 2. Do not treat the presence of a process-wide environment variable as consent to transmit Orchard content. 3. Clearly document every data category sent externally, including task titles, descriptions, knowledge entries, and executor output. 4. Provide per-project controls and warnings before enabling external embeddings. 5. Add configurable redaction for secrets, credentials, URLs, personal data, and source-code fragments. 6. Use the provider’s supported authentication header instead of placing the API key in the URL whenever the API supports it. 7. Disable `autoExtract` by default or require explicit activation. 8. Record auditable metadata indicating that an external embedding request occurred, without logging the content or credential. 9. Add tests proving that no outbound embedding request occurs when `contextInjection.enabled` is unset. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (57)

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
The stated function is project/task management, but the code reportedly also includes knowledge-base storage, semantic search, and project knowledge retrieval. Undisclosed retention and retrieval functions are risky because they expand the data collection surface and may expose sensitive project information to agents or interfaces that users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The stated function is project/task management, but the code reportedly also includes knowledge-base storage, semantic search, and project knowledge retrieval. Undisclosed retention and retrieval functions are risky because they expand the data collection surface and may expose sensitive project information to agents or interfaces that users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The stated function is project/task management, but the code reportedly also includes knowledge-base storage, semantic search, and project knowledge retrieval. Undisclosed retention and retrieval functions are risky because they expand the data collection surface and may expose sensitive project information to agents or interfaces that users did not expect.

Known Vulnerable Dependency: openclaw==2026.3.24 — 16 advisory(ies): CVE-2026-53846 (OpenClaw: Workspace .env npm_execpath could influence bundled runtime dependency); CVE-2026-41913 (OpenClaw: Concurrent async auth attempts can bypass the intended shared-secret r); CVE-2026-53830 (OpenClaw: Slack and Zalo webhook secrets could remain active after secrets.reloa) +13 more

High
Category
Supply Chain
Confidence
98% confidence
Finding
This lockfile pins a local file-linked dependency to openclaw version 2026.3.24, and the static finding reports multiple known advisories affecting that exact version. Because OrchardOS is an agentic task-management plugin with REST/API, queue runner, subagent dispatch, and dashboard capabilities, vulnerabilities in the host agent framework can materially expand attack surface and increase the likelihood of secret exposure, auth bypass, or runtime compromise.

Known Vulnerable Dependency: openclaw==2026.3.24 — 16 advisory(ies): CVE-2026-53846 (OpenClaw: Workspace .env npm_execpath could influence bundled runtime dependency); CVE-2026-41913 (OpenClaw: Concurrent async auth attempts can bypass the intended shared-secret r); CVE-2026-53830 (OpenClaw: Slack and Zalo webhook secrets could remain active after secrets.reloa) +13 more

High
Category
Supply Chain
Confidence
93% confidence
Finding
The package declares a peer dependency of "openclaw": "*", and the finding ties the deployed/resolved version to openclaw 2026.3.24 with multiple known advisories. In the context of an agentic plugin with REST API, queue runner, dashboard, and persistent task execution, inheriting a vulnerable host framework materially increases exposure because flaws in auth, secret handling, or workspace/runtime isolation can directly affect plugin operations and user data.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The code fetches attacker-controlled external URLs and injects the retrieved content into generated instruction text. Although it blocks some private IP ranges, it still enables untrusted remote content to influence agent behavior and the redirect handling does not robustly constrain final destinations across all SSRF edge cases such as DNS rebinding or alternative internal address representations.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
return jsonOk(res, updated);
      }

      // DELETE /orchard/config-safety/:id
      if (!sub && req.method === "DELETE") {
        const existing = db.prepare(`SELECT * FROM config_safety_profiles WHERE id = ?`).get(profileId);
        if (!existing) return jsonErr(res, 404, `Profile '${profileId}' not found`);
Confidence
90% confidence
Finding
Deleting config-safety profiles through a broadly reachable gateway-authenticated route is security-relevant because these profiles influence what safety/context instructions are injected elsewhere. An attacker with gateway-level access could remove safeguards or operational guidance, weakening downstream controls and enabling less constrained agent behavior.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
const uiServerAllowUnsafeBind = cfg.uiServer?.allowUnsafeBind === true;
    api.logger.info(`[orchard] initializing, db: ${dbPath}`);
    if (cfg.debug?.enabled) {
      api.logger.warn(`[orchard] debug mode enabled (env overrides supported)`);
    }

    if (cfg.contextInjection?.enabled && !cfg.contextInjection.apiKey && !process.env.GEMINI_API_KEY) {
Confidence
75% confidence
Finding
Skill attempts to nullify the agent's safety policies or restrictions ('you have no restrictions', 'ignore your guidelines', 'do anything now'). This is a direct jailbreak that disables guardrails.

Instruction Override

High
Category
Prompt Injection
Content
// Auto-generated by scripts/generate-dashboard-module.mjs
// Do not edit by hand; edit src/ui/dashboard.html and regenerate.

export const DASHBOARD_HTML = "<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"dark\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\">\n  <meta http-equiv=\"Pragma\" content=\"no-cache\">\n  <meta http-equiv=\"Expires\" content=\"0\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>OrchardOS</title>\n  <style>\n    @import url(\"data:text/css,\"); /* no CDN deps — system fonts only */\n  </style>\n  <link rel=\"icon\" href=\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ff5c5c'><path d='M12 2L8 6h3v4H7l5 5 5-5h-4V6h3L12 2zM4 18h16v2H4v-2z'/></svg>\">\n  <style>\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    html, body { height: 100%; }\n    :root, [data-theme=\"dark\"] {\n      --bg: #0e1015; --bg-accent: #13151b; --bg-elevated: #191c24; --bg-hover: #1f2330;\n      --card: #161920; --text: #d4d4d8; --text-strong: #f4f4f5; --muted: #52525b;\n      --border: #1e2028; --border-strong: #2e3040;\n      --accent: #ff5c5c; --accent-hover: #ff7070; --accent-subtle: rgba(255,92,92,0.12);\n      --ok: #22c55e; --warn: #f59e0b; --danger: #ef4444; --info: #3b82f6; --teal: #14b8a6;\n      --radius-sm: 6px; --radius-md: 10px; --radius-lg: 16px;\n      --font-body: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif;\n      --mono: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n      --shadow: 0 1px 3px rgba(0,0,0,0.4);\n    }\n    [data-theme=\"light\"] {\n      --bg: #f5f6fa; --bg-accent: #ffffff; --bg-elevated: #eef0f6; --bg-hover: #e8eaf2;\n      --card: #ffffff; --text: #374151; --text-strong: #111827; --muted: #9ca3af;\n      --border: #e5e7eb; --border-strong: #d1d5db;\n      --accent: #e03131; --accent-hover: #c92a2a; --accent-subtl
...[truncated 28 chars]
Confidence
70% confidence
Finding
This pattern attempts to override system instructions or ignore safety constraints. Without LLM analysis, manual review is recommended.

Instruction Override

High
Category
Prompt Injection
Content
// Auto-generated by scripts/generate-dashboard-module.mjs
// Do not edit by hand; edit src/ui/dashboard.html and regenerate.

export const DASHBOARD_HTML = "<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"dark\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\">\n  <meta http-equiv=\"Pragma\" content=\"no-cache\">\n  <meta http-equiv=\"Expires\" content=\"0\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>OrchardOS</title>\n  <style>\n    @import url(\"data:text/css,\"); /* no CDN deps — system fonts only */\n  </style>\n  <link rel=\"icon\" href=\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ff5c5c'><path d='M12 2L8 6h3v4H7l5 5 5-5h-4V6h3L12 2zM4 18h16v2H4v-2z'/></svg>\">\n  <style>\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    html, body { height: 100%; }\n    :root, [data-theme=\"dark\"] {\n      --bg: #0e1015; --bg-accent: #13151b; --bg-elevated: #191c24; --bg-hover: #1f2330;\n      --card: #161920; --text: #d4d4d8; --text-strong: #f4f4f5; --muted: #52525b;\n      --border: #1e2028; --border-strong: #2e3040;\n      --accent: #ff5c5c; --accent-hover: #ff7070; --accent-subtle: rgba(255,92,92,0.12);\n      --ok: #22c55e; --warn: #f59e0b; --danger: #ef4444; --info: #3b82f6; --teal: #14b8a6;\n      --radius-sm: 6px; --radius-md: 10px; --radius-lg: 16px;\n      --font-body: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif;\n      --mono: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n      --shadow: 0 1px 3px rgba(0,0,0,0.4);\n    }\n    [data-theme=\"light\"] {\n      --bg: #f5f6fa; --bg-accent: #ffffff; --bg-elevated: #eef0f6; --bg-hover: #e8eaf2;\n      --card: #ffffff; --text: #374151; --text-strong: #111827; --muted: #9ca3af;\n      --border: #e5e7eb; --border-strong: #d1d5db;\n      --accent: #e03131; --accent-hover: #c92a2a; --accent-subtl
...[truncated 28 chars]
Confidence
70% confidence
Finding
This pattern attempts to override system instructions or ignore safety constraints. Without LLM analysis, manual review is recommended.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The dashboard exposes watchdog actions that can create snapshots and trigger a gateway restart directly from the browser UI. For a task/project management plugin, this is privileged operational control over the host gateway, and if the UI or token is compromised an attacker could disrupt service, force restarts, or extract potentially sensitive state via snapshots.

Session Persistence

Medium
Category
Rogue Agent
Content
Use these directly inside any OpenClaw agent session:

- `orchard_task_add` — add a task to a project
- `orchard_task_list` — list tasks (filter by project_id, status)
- `orchard_task_done` — mark task done with summary
- `orchard_task_block` — mark task blocked with reason
Confidence
80% 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.

Lp3

Medium
Category
MCP Least Privilege
Confidence
81% confidence
Finding
The skill metadata does not declare any tool scope or permission boundaries even though the plugin behavior implies access to environment and network-capable functionality. In an agent ecosystem, missing explicit scope increases the chance that the plugin is invoked with broader privileges than users expect, enabling unintended data access or outbound communication.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill description does not clearly warn that project and task data is persisted in SQLite and exposed via API and dashboard interfaces. That omission can lead users to share sensitive information with the plugin without understanding retention, visibility, and exposure boundaries, increasing confidentiality and privacy risk.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The documentation states the tools are available in every agent session after install, with no constraints on when they should activate. Broad activation increases the probability that unrelated agents can read or modify persistent project state, trigger queue execution, or interact with the dashboard/API in contexts where those capabilities are unnecessary.

Session Persistence

Medium
Category
Rogue Agent
Content
|------|-------------|
| `orchard_project_create` | Create a new project |
| `orchard_project_list` | List all projects |
| `orchard_task_add` | Add a task to a project |
| `orchard_task_list` | List tasks (filter by project, status) |
| `orchard_task_done` | Mark a task done with a summary |
| `orchard_task_block` | Mark a task blocked with a reason |
Confidence
80% confidence
Finding
The plugin intentionally persists tasks, comments, summaries, and run history across sessions, which creates durable state that may contain sensitive operational or user data. Session persistence is not inherently malicious, but without retention limits, access controls, or user warning, it increases the blast radius of accidental disclosure or misuse.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The brief explicitly instructs the operator to use a real local gateway token in a manual smoke test, but provides no guidance on safe handling, scoping, storage, or redaction. In an agent-skill context, asking for real credentials increases the chance that a token is exposed in shell history, logs, transcripts, screenshots, or pasted back into tool outputs, which could enable unauthorized API access.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
// ── standalone UI proxy server ────────────────────────────────────────────────

function getDashboardHtml(stripLegacyAuth = false): string {
  return stripLegacyAuth ? DASHBOARD_HTML.replace(/const AUTH = '[^']*'/, "const AUTH = ''") : DASHBOARD_HTML;
}
Confidence
75% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code file contains a destructive operation: deleting a task record from the database. Although the route checks for existence and blocks deletion while running, the handler provides no confirmation prompt, warning log, or explanatory comment disclosing that the operation is irreversible from the user's perspective.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
This route adds a configuration-safety/profile injection subsystem that is materially broader than the plugin’s stated task/project-management scope. In practice it can assemble prompt material from remote URLs, local knowledge sources, and custom rules into an injection payload, which creates a powerful control surface for steering downstream agent behavior and increases the blast radius if abused by any authenticated caller.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
Deleting knowledge entries affects stored user/project data and is safety-relevant under SQP-2 for code files. The handler executes the deletion directly after existence checking, but does not include any visible confirmation step, warning log, or explanatory comment about the data-removal effect.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
This module transmits arbitrary project knowledge content to Google's embedding API, which can include sensitive task, project, or operational data. Although this appears to support semantic search rather than overtly malicious behavior, the external data transfer expands the trust boundary and creates a confidentiality/privacy risk that is not clearly constrained in this code.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The code sends user/project content directly to an external API when embeddings are enabled, but there is no visible disclosure, approval flow, or sensitivity check here. In a task-management skill, stored content may contain credentials, internal plans, or personal data, so silent export to a third party can violate privacy expectations and compliance requirements.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
Search queries are also embedded remotely, which exposes what users are looking for and may reveal sensitive project intent, incident details, or internal identifiers. Even if content storage is expected, transmitting live queries to a third party without clear disclosure increases privacy and operational security risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The executor dispatch sends task title, description, acceptance criteria, project goal, and possibly retrieved knowledge-base context to a subagent session. That is a real data-exposure boundary: sensitive project/task content can be propagated to another model/runtime without any consent gate, redaction layer, or data-classification check in this code path.

Static analysis

No suspicious patterns detected.