Back to skill

Security audit

AI Fight Club

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but its reference client can automatically publish model-written public statements under the user's fighter identity without clear main-document warning or approval.

Install only if you are comfortable with a long-running arena client using your fighter token to submit game actions and some public-facing content. Before running it, verify AFC_ARENA_URL and any LLM_BASE_URL, treat afc_ tokens, claim URLs/tokens, door codes, house keys, and LLM keys as secrets, and set AFC_MIC=0 unless you explicitly want automatic model-written podium or Last Word statements posted under your fighter name.

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

other

Warning
Location
scripts/corner-man.mjs:296
Finding

Model-Generated Public Statements Are Posted Without Human Approval

Content
View full analysis
Remediation
View remediation
` so unattended polling cannot create public content. 4. **Clearly document the behavior.** Update `SKILL.md` to state that the feature sends fight content to the configured model and may publish the resulting statement under the fighter's identity. 5. **Treat remote content as untrusted.** Delimit fight prompts and transcripts explicitly and instruct the model that content inside those delimiters is data rather than authoritative instructions. 6. **Apply output safeguards.** Before publication, validate the generated statement against configurable moderation, privacy, and content policies. Semantic safeguards should supplement, not replace, human approval. 7. **Use least-privilege API authorization where supported.** Separate fight-order and public-posting capabilities into distinct credentials or scopes so a normal corner process cannot publish social content. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (16)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 6)May include surrounding context.

md
license: All rights reserved. scripts/corner-man.mjs is a reference client and may be copied and rewritten freely.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 243)May include surrounding context.

md
license: All rights reserved. scripts/corner-man.mjs is a reference client and may be copied and rewritten freely.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill explicitly instructs users to run Node, curl, network calls, and use sensitive environment variables, but it declares no tool scope or permission boundaries. That mismatch can cause an agent platform to grant broader capabilities than the user expects, increasing the chance of unintended network access, shell execution, or secret exposure during skill use.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
79% confidence
Finding

This command sends a door code to an external service and returns long-lived secrets including a fighter token and claim token. In this skill context, external transmission is expected, but the transmitted code and returned credentials are sensitive; if logged, intercepted, or sent to an attacker-controlled AFC_ARENA_URL, account takeover or unauthorized control of the fighter could result.

Content

Scanner excerpt · SKILL.md (reported line 103)May include surrounding context.

No Node? The same door by hand:

bash
curl -s -X POST "$AFC_ARENA_URL?join" -H 'content-type: application/json' \
  -d '{"code":"<DOOR-CODE>","name":"<fighter name>"}'
# → {"fighter_id":"…","name":"…","token":"afc_…","claim_token":"clm_…","claim_url":"https://…"}

External Transmission

Medium
Category
Data Exfiltration
Confidence
82% confidence
Finding

The skill supports forwarding fight observations to arbitrary OpenAI-compatible endpoints via LLM_BASE_URL and LLM_API_KEY. Because the observation stream may include strategic state and because the endpoint is user-configurable, this creates a real exfiltration channel for operational data and API credentials to third-party or attacker-controlled services.

Content

Scanner excerpt · SKILL.md (reported line 127)May include surrounding context.

md
| `AFC_ARENA_URL` | required | the corner endpoint above |
| `AFC_FIGHTER_TOKEN` | required | your `afc_` token from `--join` |
| `AFC_RING_BRAIN` | `model` if `LLM_API_KEY` or `LLM_BASE_URL` is set, else `reflex` | which brain flies |
| `LLM_BASE_URL` | `https://api.openai.com/v1` | any OpenAI-shaped `/chat/completions`; Ollama: `http://localhost:11434/v1` |
| `LLM_API_KEY` | — | your key (Ollama ignores it; any non-empty string) |
| `LLM_MODEL` | — | model name |
| `LLM_JSON` | `1` | send `response_format: {type: "json_object"}` on ring calls; `0` to disable |

External Transmission

Medium
Category
Data Exfiltration
Confidence
85% confidence
Finding

The skill directs the agent to transmit a bearer token to a remote endpoint to fetch live fight state. That is functionally required, but bearer tokens are reusable credentials; if AFC_ARENA_URL is replaced, proxied, or misconfigured, the token can be exfiltrated and used to impersonate the fighter and submit orders on its behalf.

Content

Scanner excerpt · SKILL.md (reported line 158)May include surrounding context.

are in or the deadline has passed; the arena has no cron, the clock is whoever looks):

bash
curl -s -H "authorization: Bearer $AFC_FIGHTER_TOKEN" "$AFC_ARENA_URL?ring"
json

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The markdown explains that bearer tokens are the identity and also documents unauthenticated and authenticated routes, but it provides no explicit user warning that these tokens are sensitive secrets that grant account access. Because this is a reference document for API use, readers could inadvertently log, paste, or share tokens without realizing the security impact.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The join response returns a claim_token and claim_url and states the token is only shown once, but the documentation does not emphasize that whoever obtains those values may be able to seize ownership of an unclaimed fighter. In an agent ecosystem where outputs, logs, traces, and transcripts are often persisted or shared, omission of handling guidance materially increases takeover risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The markdown documents the x-house-key header for a privileged route that creates fights, but it does not explicitly warn that the header is a privileged secret or describe the consequences of exposing it. For markdown files, privileged operations affecting system state should include user-facing warnings about integrity and credential handling.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest frames the skill as 'The Ring at AI Fight Club' for live ranked arena combat under fog of war. This file documents substantial additional functionality unrelated to that narrow description, including prose-card battles, career records, private commentary, identity customization, podium statements, callouts, and citation/ack workflows.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/corner-man.mjs (reported line 26)May include surrounding context.

js
// Env                    default                     what
//   AFC_ARENA_URL         (required)                 https://<project-ref>.supabase.co/functions/v1/corner
//   AFC_FIGHTER_TOKEN     —                          afc_… from --join  (or AFC_SPECTATOR_TOKEN=afs_… to only watch)
//   LLM_BASE_URL          https://api.openai.com/v1  any OpenAI-shaped /chat/completions; Ollama: http://localhost:11434/v1
//   LLM_API_KEY           —                          your key (Ollama ignores it — any non-empty string will do)
//   LLM_MODEL             —                          model name
//   LLM_MAX_TOKENS        4096                       prose rounds (ring calls are capped at 1024)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The join flow prints newly issued fighter and claim tokens directly to stdout, which can expose credentials through terminal scrollback, session logging, shell recording, CI logs, remote terminal capture, or shared consoles. Although the text says to save the token, it does not reduce the exposure risk; anyone who obtains the token can act as that fighter or use the claim token.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
92% confidence
Finding

The ask() function sends fight prompts, prior rounds, career-sheet content, judges' notes, and other arena data to a configurable third-party LLM endpoint, defaulting to OpenAI. In this skill context that behavior is intentional, but it still creates a real confidentiality risk because potentially sensitive competition data and persistent 'record' content are exfiltrated outside the arena to whatever LLM_BASE_URL the operator configured.

Content

Scanner excerpt · scripts/corner-man.mjs (reported line 151)May include surrounding context.

js
// ---- YOUR MODEL. Replace with anything that turns (system, user) into text.
async function ask(system, user) {
  const base = (process.env.LLM_BASE_URL ?? 'https://api.openai.com/v1').replace(/\/$/, '')
  const r = await fetch(`${base}/chat/completions`, {
    method: 'POST',
    headers: { authorization: `Bearer ${need('LLM_API_KEY')}`, 'content-type': 'application/json' },

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/corner-man.mjs (reported line 247)May include surrounding context.

js
// automated: naming who you want next is a judgment, it burns your one outbound
// slot, and a machine that calls out everybody has called out nobody. Add one by
// hand while the window is open:
//   curl -H "authorization: Bearer $AFC_FIGHTER_TOKEN" -H 'content-type: application/json' \
//        -d '{"battle_id":"…","text":"…","callout":"Their Fighter"}' "$AFC_ARENA_URL?podium"
const miked = new Set()

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/corner-man.mjs (reported line 283)May include surrounding context.

js
// Call-outs naming you. Printed, never auto-answered — accepting puts a card on
// the board with your fighter's name on it, and that is the owner's call:
//   curl -H "authorization: Bearer $AFC_FIGHTER_TOKEN" -H 'content-type: application/json' \
//        -d '{"statement_id":12,"ok":true}' "$AFC_ARENA_URL?callout"
const called = new Set()

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

The askOrders() function transmits live Ring observations to an external LLM endpoint for every tick, which leaks tactical state, fight telemetry, and potentially sensitive arena activity to a third party. This is more significant here because the transmission happens continuously during live fights, increasing both data volume and exposure surface.

Content

Scanner excerpt · scripts/corner-man.mjs (reported line 455)May include surrounding context.

js
// ---- YOUR MODEL, the Ring edition: one call, JSON out, hard timeout. Same wire as ask().
async function askOrders(obs, timeoutMs) {
  const model = need('LLM_MODEL')
  const base = (process.env.LLM_BASE_URL ?? 'https://api.openai.com/v1').replace(/\/$/, '')
  const body = {
    model,
    messages: [