本文へ移動
cccskills
無料GitHub で公開

fleet-event-spawn-trust

Secure the path from an inbound event (webhook, email, SMS, GitHub comment, file, cron) to an agent spawn in the Port Daddy fleet. Use when wiring trigger sources to agents, reviewing the io-wiring registry, adding a trust/permission gate, hardening output sinks (SSRF/path traversal), scoping tools by provenance, or proving the spawn path is safe against prompt-injection → tool-abuse. Encodes ADR-0093 and the red-team threat model. NOT for generic Coast Guard sandboxing (ADR-0050), the macaroon push-grant gate alone (ADR-0053), or non-fleet code.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md10.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Fleet Event→Spawn Trust

The governing truth (from OWASP LLM Top-10 2025, CaMeL, and two red-team passes):

Prompt-level defenses are probabilistic and broken by adaptive attackers. Only ARCHITECTURAL defenses are sound: least-privilege tool gating by provenance, allowlists, capability credentials, dual-LLM, and human gates.

When an inbound webhook/email/SMS/GitHub-comment becomes an agent's task text, the attacker's words are the agent's instructions — and the agent may hold Bash(curl*)/Bash(git*). This skill is how you stop that.

The one distinction that drives everything

Transport auth ≠ content trust. An HMAC-verified webhook proves the relay is genuine; it says nothing about the author of the forwarded payload. Classify by content source, never by the HTTP wrapper. Violating this is the highest- severity attack ("webhook relay laundering").

Where it slots

#539 wires trigger → runAgentOnce() directly. The trust gate inserts in the io-dispatch.startTrigger onFire callback, before requestAgentRun:

trigger fires ─► classifyTrust ─► validateAllowedToolsForTier ─► (approval?) ─► spawn
                 (provenance)      (least privilege, fail-closed)   (gated)      (macaroon-scoped)

Pattern 1 — classify provenance by content source (lib/fleet/trust.ts)

export function classifyTrust(input, policy = {}) {
  const kind = (input.source ?? '').trim().toLowerCase();
  if (kind === 'pd') return 'OPERATOR';
  if (INTERNAL_TRIGGER_KINDS.has(kind)) return 'INTERNAL';     // git/schedule/file/pd
  if (EXTERNAL_TRIGGER_KINDS.has(kind)) {                       // webhook/email/sms/calendar/github
    const author = (input.metadata?.sender ?? '').trim().toLowerCase();
    const allow  = (policy.allowlistedAuthors ?? []).map(a => a.trim().toLowerCase());
    // BOTH required: allowlisted AND verified. Transport HMAC must NOT set consent_verified.
    if (author && allow.includes(author) && input.metadata?.consent_verified === true)
      return 'AUTHENTICATED_EXTERNAL';
    return 'ANONYMOUS_EXTERNAL';
  }
  return 'ANONYMOUS_EXTERNAL';   // unknown kind → fail closed (lowest tier)
}

Pattern 2 — least-privilege, fail-closed tool gating

Allowlist per tier (never denylist — OWASP LLM05). Absent allowedTools means "unrestricted" to the engine, which is the worst case for an untrusted trigger, so it is refused:

export function validateAllowedToolsForTier(tier, spec) {
  if (tier === 'OPERATOR') return { ok: true, ... };
  const declared = parseAllowedTools(spec);              // "Read,Bash(gh*)" -> {read, bash}
  if (declared.size === 0)                                // absent == unrestricted == DENY
    return { ok: false, reason: 'tier requires an explicit allowedTools set', offendingTools: [] };
  const offending = [...declared].filter(t => !toolAllowedForTier(tier, t)).sort();
  return offending.length
    ? { ok: false, reason: `tools [${offending}] exceed the safe set for ${tier}`, offendingTools: offending }
    : { ok: true, ... };
}

Tool-name normalization defeats unicode/case/glob smuggling — Bash(gh*) still grants bash:

export function normalizeToolName(raw) {
  const nfc = (raw ?? '').normalize('NFC').trim().toLowerCase();
  const paren = nfc.indexOf('(');
  return (paren >= 0 ? nfc.slice(0, paren) : nfc).trim();   // "bash(gh*)" -> "bash"
}

Approval is a whitelist of trusted tiers (a typo/unknown tier fails to gated):

const TRUSTED_TIERS = new Set(['OPERATOR', 'INTERNAL']);
export const requiresApproval = (tier) => !TRUSTED_TIERS.has(tier);

Pattern 3 — SSRF guard on outbound sinks (lib/fleet/url-guard.ts)

Any sink that fetch()es an attacker-influenceable URL must call this first. It classifies IPv4 literals in all legal forms (dotted/decimal/octal/hex), blocks loopback/private/link-local/metadata/CGNAT, IPv6 loopback/ULA/mapped, non-http(s) schemes, and embedded creds:

assertSafeOutboundUrl(payload.recipient, allowlist ? { allowlist } : {});
// blocks http://169.254.169.254/... , http://2852039166/ , http://127.0.0.1:6379 ,
// http://[::1]/ , file:// , http://user:pass@host/ ...

Pattern 4 — path containment on file sinks (lib/fleet/path-guard.ts)

const expanded = containPath(payload.recipient);  // replaces unguarded resolve()
// - expands {date}/{time}/{iso} + ~, resolves (collapses ..)
// - contains within home/tmp/cwd; rejects /etc, /usr, other homes
// - realpaths the existing prefix (catches symlink escape; realpaths roots too)
// - refuses sensitive subpaths even inside home: ~/.ssh, ~/Library/LaunchAgents, .git/hooks

Attack → defense (each has a test; the test name is the evidence)

AttackDefenseTest
webhook relay launderingclassify by content, not transportfleet-trust "defeats webhook-relay-laundering"
prompt-injection → tool abuseallowedTools ⊆ safeSet(tier)fleet-trust "defeats injection-tool-abuse"
"no restriction + untrusted = full"absent tools = deny (non-trusted)fleet-trust "defeats absent-allowedTools-means-full"
silent approval bypasswhitelist tiers, fail closedfleet-trust "defeats silent-approval-bypass"
unicode/case/glob tool bypassNFC+lowercase+base-capabilityfleet-trust normalize block
SSRF (metadata/loopback/obfusc IP)assertSafeOutboundUrlfleet-url-guard (15 vectors)
path traversal / abs / symlinkcontainPathfleet-path-guard
sensitive write in homesensitive-segment denylistfleet-path-guard "defeats sensitive-subpath-write"

Anti-patterns (refuse these)

  1. Tiering on transport auth. "the webhook HMAC verified, so trust the body." No — HMAC ⇒ relay genuine, not author trusted.
  2. Denylist of dangerous tools. You cannot enumerate every dangerous tool; allowlist the safe ones per tier.
  3. Letting absent allowedTools mean "full". For untrusted triggers that is total compromise; require an explicit set.
  4. Trusting the docstring. #539's webhook output said "SSRF-guarded" and was not. Read the code; write the regression test.
  5. A weaker TS fallback verifier. If the Rust macaroon kernel is absent, fail closed — never verify with weaker semantics (caveat-omission attack).
  6. Bare green assurance. Every safety score shows its scan time and links to the underlying evidence (digest-with-zoom).
  7. Credentialless evidence mutation. Loopback, Unix socket possession, headers, process labels, and reusable actor credentials do not authorize an external caller to append, terminalize, backfill, or delete canonical transcripts. Keep evidence production daemon-owned and in-process until a one-use broker-redeemed action boundary is actually wired; remove the old route instead of preserving a downgrade.
  8. Evidence-producer laundering. A body or imported snapshot that names an internal producer, trusted tier, automatic trigger, or spawned-agent id is still caller data. Canonical lifecycle code stamps reserved provenance itself; legacy rows stay explicitly untrusted instead of being upgraded by inference.

Quality gates

  • External-triggered ships declare an explicit allowedTools; gate refuses otherwise.
  • Trust gate wired BEFORE any untrusted ingress (webhook receiver / inbound email).
  • Every outbound-URL sink calls assertSafeOutboundUrl.
  • Every file sink/trigger path goes through containPath.
  • Each new attack vector gets a named regression test.
  • Macaroon tool/tier caveats covered by ProVerif before they are foundational.
  • Durable evidence sinks publish immutable artifacts from unique private temps; shared append rollback is not process-safe. Fsync only after the complete write, atomically publish, clamp directories/files to least privilege, reject symlink/unsafe targets, and require a receipt binding the exact artifact locator/digest/size/format before pruning live evidence.
  • A terminal evidence receipt freezes every field covered by its digest. Status checks and child writes are one atomic operation; full-entry imports commit header plus children together before publication.
  • The first exact archive success is immutable under interleaved stores; a late failure or different-digest success cannot replace it. Archive the fresh private-DB snapshot before invoking listeners.
  • Archive roots, partitions, and artifacts are private; reject static symlink ancestors as well as symlink targets. If publication is still pathname-based, state the residual same-UID concurrent swap race instead of claiming an openat/dirfd guarantee.
  • Public transcript HTTP/CLI surfaces are read-only unless the action service directly redeems a one-use capability. Every retired mutator has a hostile 404/405 regression and no compatibility fallback.
  • Producer provenance is daemon-stamped at the in-process boundary; forged producer-shaped fields and pre-migration rows never acquire trusted provenance by declaration or inference.

Residual (sound fixes not yet shipped — see ADR-0093 §10)

Dual-LLM/CaMeL for 2nd-order injection; DNS-rebinding resolve-and-pin; O_NOFOLLOW for path TOCTOU; per-call gating for runtime-loaded MCP tools; signed outbox. State these; never let green over-claim.

Reference

  • ADR-0093 docs/adr/0093-event-spawn-trust-substrate.md (full threat model + log)
  • lib/fleet/trust.ts, lib/fleet/url-guard.ts, lib/fleet/path-guard.ts
  • Tests: tests/unit/fleet-trust.test.js, tests/unit/fleet-url-guard.test.js, tests/unit/fleet-path-guard.test.js
  • Builds on: ADR-0050 (Coast Guard), ADR-0053 (macaroon gate), lib/macaroon/*

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Expert in 2000s-era music visualization (Milkdrop, AVS, Geiss) and modern WebGL implementations. Specializes in Butterchurn integration, Web Audio API AnalyserNode FFT data, GLSL shaders for audio-reactive visuals, and psychedelic generative art. Activate on "Milkdrop", "music visualization", "WebGL visualizer", "Butterchurn", "audio reactive", "FFT visualization", "spectrum analyzer". NOT for simple bar charts/waveforms (use basic canvas), video editing, or non-audio visuals.

日本語の概要は準備中です。原文の説明を表示しています。

curiositech/port-daddy22026年10月8日 更新

Expert legal research agent for finding and scraping expungement data state by state. Knows authoritative sources, URL patterns, Firecrawl configuration, and 2026 legal landscape.

日本語の概要は準備中です。原文の説明を表示しています。

curiositech/port-daddy22026年10月8日 更新

Expert in 3D computer vision labeling tools, workflows, and AI-assisted annotation for LiDAR, point clouds, and sensor fusion. Covers SAM4D/Point-SAM, human-in-the-loop architectures, and vertical-specific training strategies. Activate on '3D labeling', 'point cloud annotation', 'LiDAR labeling', 'SAM 3D', 'SAM4D', 'sensor fusion annotation', '3D bounding box', 'semantic segmentation point cloud'. NOT for 2D image labeling (use clip-aware-embeddings), general ML training (use ml-engineer), video annotation without 3D (use computer-vision-pipeline), or VLM prompt engineering (use prompt-engineer).

日本語の概要は準備中です。原文の説明を表示しています。

curiositech/port-daddy22026年10月8日 更新

Apply crisis decision-making research to agent routing, uncertainty triage, and coordination failure analysis in time-pressured systems. Use when diagnosing handoff failures, analytical paralysis, or expert judgment under incomplete information. NOT for routine coding, simple CRUD design, or static single-agent tasks with complete information.

日本語の概要は準備中です。原文の説明を表示しています。

curiositech/port-daddy22026年10月8日 更新

Use for insight, reframing, contradiction, impasse, and anomaly-driven problem solving when execution effort no longer helps. NOT for routine optimization, error correction, or well-specified tasks with known solution paths.

日本語の概要は準備中です。原文の説明を表示しています。

curiositech/port-daddy22026年10月8日 更新

Apply cognitive task analysis to expert work that depends on perceptual cues, branching judgment, and recurring monitoring loops. Use when decomposing expert capability into agent structure, simulation design, or validation interviews. NOT for ordinary step-by-step SOP capture or simple pipelines with no tacit cue layer.

日本語の概要は準備中です。原文の説明を表示しています。

curiositech/port-daddy22026年10月8日 更新

curiositech のスキルをすべて見る

このスキルの問題を報告する