Back to skill

Security audit

Duffel Flights

Security checks for vulnerabilities and agentic risk

Overview

This Duffel flight skill is mostly coherent, but it can make real bookings and cancellations while relying on weak shared temporary files and limited safety warnings.

Review before installing. Prefer a Duffel test token first, require explicit user confirmation before any booking or cancellation, avoid using numeric indexes from shared cached state for real purchases, and clear the /tmp Duffel cache files after use. Production use can spend real account funds and send traveler details to Duffel and travel providers.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/duffel.py:58
Finding

Predictable Shared Temporary Files Allow Booking and Cancellation State Tampering

Content
View full analysis

Vulnerability Details

File Location: scripts/duffel.py:18, scripts/duffel.py:58-74, and scripts/duffel.py:375-399
Vulnerability Type: Predictable and insecure temporary-file handling
Risk Level: High

The CLI stores security-sensitive offer and cancellation state in fixed, globally predictable files under /tmp. These files are created and read without secure creation, ownership validation, symlink protection, restrictive permissions, session isolation, or verification that the stored cancellation belongs to the order supplied by the user.

Vulnerable code:

python
LAST_SEARCH_FILE = "/tmp/duffel-last-search.json"
python
def save_search(offers_data):
    """Save search results for index-based reference."""
    with open(LAST_SEARCH_FILE, "w") as f:
        json.dump(offers_data, f)

def load_offer(id_or_index):
    """Load an offer by ID or 1-based index from last search."""
    try:
        idx = int(id_or_index)
        with open(LAST_SEARCH_FILE) as f:
            data = json.load(f)
        offers = data if isinstance(data, list) else data.get("offers", [])
        if idx < 1 or idx > len(offers):
            print(f"Error: Index {idx} out of range (1-{len(offers)})")
            sys.exit(1)
        return offers[idx - 1]
    except (ValueError, FileNotFoundError):
        return {"id": id_or_index}
python
if not args.confirm:
    # Get cancellation quote
    payload = {"data": {"order_id": args.order_id}}
    data = api_post("/air/order_cancellations", payload)
    cancel = data.get("data", {})

    if args.json:
        print(json.dumps(cancel, indent=2))
        return

    refund = cancel.get("refund_amount", "0")
    currency = cancel.get("refund_currency", "?")
    print(f"\n⚠️  Cancellation quote for order {args.order_id}")
    print(f"   Refund: {currency} {refund}")
    print(f"   Cancellation ID: {cancel.get('id', '?'
...[truncated 4118 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace global /tmp paths with a private per-user state directory, such as an appropriate platform-specific cache directory, created with mode 0700.
  2. Create state files with mode 0600 and secure flags such as O_CREAT, O_EXCL, and O_NOFOLLOW where supported.
  3. Validate with lstat or descriptor-based checks that each state object is a regular file owned by the current user and is not a symbolic link.
  4. Write through a securely created temporary file in the private directory, flush it, and atomically rename it into place.
  5. Namespace state by Duffel account, process, workflow, or a cryptographically random session identifier rather than using one global file.
  6. Bind cached cancellation state to its order ID. Before confirmation, verify that the stored order ID exactly equals args.order_id; reject missing, stale, or mismatched state.
  7. Prefer requiring the user to pass the cancellation ID explicitly or create and confirm a cancellation within one controlled invocation.
  8. Avoid trusting cached price, currency, passenger, or offer metadata for a financial action. Re-fetch the selected offer by ID and verify its current price and passenger mapping before booking.
  9. Add expiration timestamps and reject stale offer and cancellation state.
  10. Remove sensitive temporary files after successful use and handle interruption cleanup safely.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
## Setup

Set `DUFFEL_TOKEN` env var with your Duffel API access token.
Get one at https://app.duffel.com → Developers → Access Tokens.
Test tokens (prefix `duffel_test_`) use sandbox data with unlimited balance.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
## Setup

Set `DUFFEL_TOKEN` env var with your Duffel API access token.
Get one at https://app.duffel.com → Developers → Access Tokens.
Test tokens (prefix `duffel_test_`) use sandbox data with unlimited balance.

## Commands

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/duffel.py (reported line 32)May include surrounding context.

python
## Setup

Set `DUFFEL_TOKEN` env var with your Duffel API access token.
Get one at https://app.duffel.com → Developers → Access Tokens.
Test tokens (prefix `duffel_test_`) use sandbox data with unlimited balance.

## Commands

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill advertises capabilities that involve network access, environment-variable access, and local file writes, but it does not declare any explicit tool scope or permission boundary. This creates an avoidable trust gap: an agent or reviewer cannot easily determine what operations are intended, which increases the chance of over-broad execution and unsafe use of credentials or filesystem access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The booking flow requires users to provide highly sensitive passenger PII, including full name, date of birth, email, phone number, nationality, and gender, but the skill does not prominently warn that this data will be transmitted to an external airline-booking API. In an agent setting, absent disclosure and consent handling can lead to privacy violations, accidental over-collection, and inappropriate sharing of regulated personal data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill supports real flight booking in production and notes that the account must be funded, but it does not clearly warn that using production mode can create real financial charges and reservations that may be non-refundable or costly to reverse. In an agent workflow, this omission increases the risk of unintended purchases and irreversible transactions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This documentation gives direct instructions for booking and cancelling flights, which are real-world, irreversible or financially impactful actions, but it does not include any warning, confirmation guidance, or operator safeguards. In an agent skill context, this increases the risk that an automated system could execute purchases or cancellations without clear user awareness or explicit consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The booking flow collects and transmits highly sensitive passenger PII, including full name, date of birth, email, phone number, nationality, and gender, to an external API. While this is functionally required for flight booking, the script does so without an explicit privacy notice, confirmation step, or data-minimization guidance, which increases the risk of users disclosing sensitive data without clear awareness in an agent-driven context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill states that the last search is written to /tmp/duffel-last-search.json, but it does not clearly disclose that travel-search data will persist on local disk. Even if the data is not the most sensitive category, itineraries and travel patterns can still expose personal or business-sensitive information and may be readable by other local processes depending on environment configuration.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

The dependency file specifies requests without a version pin, which makes builds non-reproducible and can cause different environments to install different releases. In a flight-booking skill that may handle API credentials and booking data, this increases supply-chain and reliability risk because a vulnerable or breaking release could be pulled in unintentionally.

Content

Scanner excerpt · scripts/requirements.txt (reported line 1)May include surrounding context.

text
requests

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding

Because requests is unpinned, it is impossible to verify from this manifest whether the deployed version includes fixes for known advisories. In this skill's context, HTTP requests are central to interacting with the Duffel Flights API, so unresolved client-library vulnerabilities could affect confidentiality of credentials, request integrity, or transport security depending on the installed version.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.