T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/talent-scout.mjs:21506
- Finding
- Untrusted GitHub Content Is Forwarded to AI Agents Without Prompt-Injection Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/talent-scout.mjs:21506-21513`, `scripts/talent-scout.mjs:21552-21558`, and `scripts/talent-scout.mjs:25081-25110` **Vulnerability Type**: Indirect prompt injection through untrusted GitHub metadata **Risk Level**: Medium ### Vulnerable Code The evaluator receives candidate profiles and signals containing externally controlled GitHub content: ```js const result = await callAgent("evaluator", { task: "batch_deep_evaluation", candidates: batch.map((c3) => ({ username: c3.username, profile: c3.profile, signals: c3.signals, evaluation: c3.evaluation, features: c3.features })) }); ``` The identity agent receives the same type of untrusted profile data: ```js const result = await callAgent("identity", { task: "batch_identity_inference", candidates: batch.map((c3) => ({ username: c3.username, profile: c3.profile, signals: c3.identity?.signals ?? [] })) }); ``` Commit messages are one example of attacker-controlled content incorporated into candidate signals: ```js async function collectCommitSignals(config, cache) { const candidates = /* @__PURE__ */ new Map(); for (const qcfg of config.commit_queries) { const q = qcfg.query; const items = await ghApi( `/search/commits?q=${encodeSearchQuery(q)}&sort=committer-date`, { maxPages: config.api_budget.search_pages_per_query, sleepMs: config.api_budget.search_sleep_ms, accept: "application/vnd.github.cloak-preview+json", cache, cacheTtl: config.cache.ttl.search_results } ); for (const item of items) { const login = item.author?.login.toLowerCase(); if (!login) continue; const signals2 = candidates.get(login) ?? []; signals2.push({ type: labelToSignalType(qcfg.label), detail: `${item.repository.full_name}: ${item.commit.message.slice(0, 60)}`, weight: qcfg.weight, source: "commit-sea ...[truncated 2790 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Establish an explicit trust boundary in every agent request** - State that all candidate fields originate from untrusted public sources. - Instruct the agent never to follow commands, policies, role changes, or tool requests found inside candidate data. - Require the agent to analyze those fields only as quoted evidence. 2. **Separate instructions from data** - Place trusted task instructions in the agent's system or policy layer. - Serialize candidate content inside a clearly delimited data structure. - Avoid concatenating external text into instruction-bearing strings. 3. **Minimize forwarded data** - Send only fields necessary for the specific inference. - Prefer normalized booleans, counts, and allowlisted attributes over complete profile and signal objects. - Truncate and sanitize free-form biographies, repository descriptions, and commit messages. 4. **Validate agent responses** - Enforce a strict response schema. - Constrain confidence values to an expected numeric range. - Validate usernames against the submitted batch. - Reject unknown fields, malformed actions, and unsupported city values. - Do not permit model output to select tools, commands, files, or destinations. 5. **Add defensive evaluation** - Detect common prompt-injection phrases in external text and flag affected candidates for manual review. - Record which source fields contributed to each AI conclusion. - Require human confirmation before high-impact shortlist or outreach decisions. 6. **Add adversarial tests** - Test biographies, repository descriptions, and commit messages containing role-change requests, instruction overrides, fake system messages, and requests to alter scores. - Verify that such content is quoted as evidence and never followed as an instruction. ]]>
