Back to skill

Security audit

FastAPI Backend

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent FastAPI guide with no hidden installer, but its sample user API exposes account data without authentication and should be reviewed before use.

Review and harden the generated authentication examples before using this skill as a production template. Add authentication and authorization to user read endpoints, validate JWT claims before conversion, bound pagination, and keep real DATABASE_URL and SECRET_KEY values in protected local secrets rather than committed files.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:286
Finding

Unauthenticated API Endpoints Expose User Information

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 286–313
Vulnerability Type: Missing authentication and authorization
Risk Level: Medium

Vulnerable Code

python
@router.get("/", response_model=list[UserResponse])
async def list_users(
    skip: int = 0,
    limit: int = 100,
    db: AsyncSession = Depends(get_db),
):
    return await user_crud.get_users(db, skip=skip, limit=limit)

@router.get("/{user_id}", response_model=UserResponse)
async def get_user(
    user_id: int,
    db: AsyncSession = Depends(get_db),
):
    user = await user_crud.get_user(db, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

Technical Analysis

The user-listing and individual-user endpoints do not include the get_current_user authentication dependency. Consequently, the generated application permits anonymous clients to enumerate user records and retrieve individual users by numeric ID.

The configured UserResponse schema includes users' email addresses, IDs, activity status, and creation timestamps. Pagination does not provide an authorization boundary because an anonymous client can repeatedly change skip and limit.

The predictable integer identifiers accepted by GET /users/{user_id} also enable systematic account enumeration. Different responses for existing and nonexistent IDs further reveal which accounts exist.

Attack Path

  1. An unauthenticated attacker sends GET /api/v1/users/?skip=0&limit=100.
  2. The application returns user IDs, email addresses, activity states, and creation timestamps.
  3. The attacker increments skip to enumerate additional records.
  4. Alternatively, the attacker requests sequential IDs through GET /api/v1/users/{user_id}.
  5. The collected account identifiers can be used for phishing, credential stuffing, or user profiling.

Impact Assessment

No authenticated application privileges are required. A remote anonymous client can a ...[truncated 273 chars]

Remediation
View remediation

Remediation Suggestions

Require authentication on both endpoints and apply explicit authorization rules:

python
@router.get("/", response_model=list[UserResponse])
async def list_users(
    skip: int = 0,
    limit: int = 100,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if not current_user.is_superuser:
        raise HTTPException(status_code=403, detail="Not authorized")
    return await user_crud.get_users(db, skip=skip, limit=limit)

@router.get("/{user_id}", response_model=UserResponse)
async def get_user(
    user_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if user_id != current_user.id and not current_user.is_superuser:
        raise HTTPException(status_code=403, detail="Not authorized")

    user = await user_crud.get_user(db, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

Additional hardening measures:

  • Restrict user-list access to administrators or remove the endpoint when it is unnecessary.
  • Permit ordinary users to retrieve only their own profile.
  • Use a separate public response schema that omits email addresses and other sensitive metadata.
  • Enforce bounded pagination values to reduce bulk extraction risk.
  • Consider returning uniform authorization responses where appropriate to reduce account enumeration.
  • Add tests confirming that anonymous requests receive HTTP 401 and unauthorized authenticated requests receive HTTP 403.

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:418
Finding

Malformed JWT Subject Can Trigger an Unhandled Server Error

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 418–423
Vulnerability Type: Improper JWT claim validation and incomplete exception handling
Risk Level: Low

Vulnerable Code

python
try:
    payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
    user_id: int = int(payload.get("sub"))
    if user_id is None:
        raise credentials_exception
except JWTError:
    raise credentials_exception

Technical Analysis

The code converts the JWT sub claim to an integer before verifying that the claim exists and has a valid type. If sub is absent, payload.get("sub") returns None, and int(None) raises TypeError. If sub is a nonnumeric string, int(...) raises ValueError.

The handler catches only JWTError; it does not catch TypeError or ValueError. These malformed-claim cases can therefore escape the authentication dependency and produce HTTP 500 responses rather than controlled HTTP 401 responses. The subsequent if user_id is None check cannot handle an absent claim because conversion has already failed.

Exploitation ordinarily requires an otherwise validly signed token, so the practical attack surface depends on whether an attacker can obtain a valid token with malformed claims or whether another trusted token issuer can generate such claims.

Attack Path

  1. An attacker obtains or causes issuance of an otherwise validly signed JWT.
  2. The token contains no sub claim or contains a nonnumeric value such as "invalid".
  3. The attacker submits the token to an endpoint protected by get_current_user.
  4. Signature decoding succeeds, but integer conversion raises TypeError or ValueError.
  5. Because those exceptions are not caught, the application returns an internal server error and may record a traceback.
  6. Repeated requests can create error-log noise and consume application error-handling resources.

Impact Assessment

The direct impact is availability and error-handling degra ...[truncated 352 chars]

Remediation
View remediation

Remediation Suggestions

Validate the claim before conversion and normalize all malformed authentication data to HTTP 401:

python
try:
    payload = jwt.decode(
        token,
        settings.SECRET_KEY,
        algorithms=["HS256"],
    )
    subject = payload.get("sub")
    if subject is None:
        raise credentials_exception
    user_id = int(subject)
except (JWTError, TypeError, ValueError):
    raise credentials_exception

Additional hardening measures:

  • Define and validate all required JWT claims, including sub, exp, and, where applicable, iss and aud.
  • Ensure sub follows one documented type and format across every token issuer.
  • Keep production debug output disabled so unexpected exceptions do not expose tracebacks.
  • Add tests for absent, null, nonnumeric, expired, incorrectly signed, and incorrectly issued claims.
  • Log authentication failures without recording raw tokens or other sensitive credentials.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Vague Triggers

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill’s activation rule is overly broad and explicitly says to trigger on vague, common phrases like '写个接口', '做个后端', and '连数据库'. That can cause unintended invocation in contexts where the user did not actually request this skill, increasing the chance of irrelevant code generation, unsafe assistance, or privilege overreach by an agent system that auto-selects skills.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

│ ├── conftest.py │ └── test_users.py ├── requirements.txt └── .env

text

---

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
# CORS
    CORS_ORIGINS: list[str] = ["http://localhost:3000"]
    
    model_config = {"env_file": ".env", "case_sensitive": True}

@lru_cache
def get_settings() -> Settings:

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The skill metadata and title are written as a Chinese-language best-practices framework, but there is no indication that language output is optional or user-selected. This can violate language/locale policy when a skill implicitly constrains interaction language without opt-in or justification.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
SKILL.md:244