Back to skill

Security audit

Api Test Automation

Security checks for vulnerabilities and agentic risk

Overview

This is a real API testing toolkit, but it needs review because it can send credentials and high request volumes to arbitrary APIs and writes report files without adequate safeguards.

Install only if you are comfortable reviewing and constraining its use. Use test or scoped credentials, target only systems you own or are authorized to test, avoid production load tests, keep concurrency low unless approved, and treat generated reports and mock-server logs as potentially containing secrets. Do not pass untrusted report filenames or untrusted absolute URLs into the clients without adding validation/redaction first.

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

T09 · Insecure Skill Coding Practices

Error
Location
src/reporter.py:126
Finding
Arbitrary File Overwrite Through Unsanitized Report Filenames<![CDATA[ ## Vulnerability Details **File Location**: `src/reporter.py:126-212` **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: High ### Vulnerable Code ```python def generate_html_report(self, report: TestReport, filename: Optional[str] = None) -> str: """Generate HTML report.""" if filename is None: filename = f"test_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html" filepath = self.output_dir / filename template = Template(HTML_REPORT_TEMPLATE) html_content = template.render( timestamp=report.timestamp.strftime("%Y-%m-%d %H:%M:%S"), total=report.total, passed=report.passed, failed=report.failed, skipped=report.skipped, pass_rate=f"{report.pass_rate:.1f}", duration=f"{report.total_duration:.2f}", tests=[ { "name": r.name, "status": r.status, "duration": f"{r.duration:.3f}", "message": r.message } for r in report.results ] ) with open(filepath, "w") as f: f.write(html_content) return str(filepath) ``` The JSON and JUnit generators repeat the same unsafe construction: ```python filepath = self.output_dir / filename with open(filepath, "w") as f: json.dump(data, f, indent=2) ``` ```python filepath = self.output_dir / filename ... tree.write(filepath, encoding="utf-8", xml_declaration=True) ``` ### Technical Analysis The caller-supplied `filename` is joined directly to `self.output_dir` without validating whether it is an absolute path or contains traversal components such as `..`. `pathlib.Path` does not confine a joined path to its parent directory. An absolute filename causes the configured output directory to be discarded, while traversal components can resolve outside it. Each report generator then opens the resulting path in write mode or passes it to `ElementTree.write`, overwriting ...[truncated 1564 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict caller-provided filenames to a single basename: ```python candidate = Path(filename) if candidate.is_absolute() or candidate.name != filename: raise ValueError("Filename must be a basename without directory components") ``` 2. Resolve and verify the final path remains under the configured output directory: ```python base = self.output_dir.resolve() destination = (base / filename).resolve() if base not in destination.parents: raise ValueError("Report path escapes output directory") ``` 3. Enforce the expected extension for each report type. 4. Consider generating filenames internally rather than accepting arbitrary paths. 5. If replacing existing reports is unnecessary, use exclusive creation mode (`"x"`) to prevent accidental overwrite. 6. Apply the same centralized path-validation helper to HTML, JSON, JUnit XML, and Allure output. 7. Add regression tests covering: - `../` traversal - Nested traversal - Absolute POSIX and Windows paths - Symlinks that resolve outside the output directory - Attempts to overwrite existing files ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/reporter.py:126
Finding
Stored Script Injection in Generated HTML Reports<![CDATA[ ## Vulnerability Details **File Location**: `src/reporter.py:13-65, 126-145` **Vulnerability Type**: Stored HTML injection / cross-site scripting **Risk Level**: Medium ### Vulnerable Code The report template renders test-controlled fields into HTML: ```python {% for test in tests %} <tr class="{{ test.status }}"> <td>{{ test.name }}</td> <td>{{ test.status.upper() }}</td> <td>{{ test.duration }}s</td> <td>{{ test.message or '' }}</td> </tr> {% endfor %} ``` It is instantiated directly with `jinja2.Template`: ```python template = Template(HTML_REPORT_TEMPLATE) html_content = template.render( timestamp=report.timestamp.strftime("%Y-%m-%d %H:%M:%S"), total=report.total, passed=report.passed, failed=report.failed, skipped=report.skipped, pass_rate=f"{report.pass_rate:.1f}", duration=f"{report.total_duration:.2f}", tests=[ { "name": r.name, "status": r.status, "duration": f"{r.duration:.3f}", "message": r.message } for r in report.results ] ) with open(filepath, "w") as f: f.write(html_content) ``` ### Technical Analysis Templates created directly through `jinja2.Template` do not establish a file-type-aware environment with HTML autoescaping enabled. Consequently, HTML metacharacters in `TestResult.name`, `TestResult.status`, or `TestResult.message` can be inserted into the report as markup rather than displayed as text. Test messages commonly originate from assertion failures, server responses, endpoint names, parameterized test values, or exception messages. If any of those sources contain attacker-controlled HTML, the payload is persisted in the generated report. The `status` value is also placed inside an HTML attribute: ```html <tr class="{{ test.status }}"> ``` Because it is not validated against the expected `passed`, `failed`, and `skipped` values, it creates an additional attribute-injection surface. ### At ...[truncated 1296 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a Jinja environment with HTML autoescaping enabled: ```python from jinja2 import Environment, select_autoescape environment = Environment( autoescape=select_autoescape( enabled_extensions=("html", "htm", "xml"), default_for_string=True, ) ) template = environment.from_string(HTML_REPORT_TEMPLATE) ``` 2. Validate `TestResult.status` against a strict allowlist: ```python ALLOWED_STATUSES = {"passed", "failed", "skipped"} if result.status not in ALLOWED_STATUSES: raise ValueError("Unsupported test status") ``` 3. Treat test names, messages, server responses, and exception text as untrusted data. 4. Do not apply Jinja's `safe` filter to report fields unless the content has been sanitized by a robust HTML sanitizer. 5. For hosted reports, add a restrictive Content Security Policy that blocks inline scripts and unauthorized outbound connections. 6. Add regression tests using payloads in every rendered field, including: ```html <script>alert(1)</script> ``` and attribute-breaking payloads. Verify the generated report contains escaped text rather than executable markup. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/rest_client.py:43
Finding
Authentication Credentials Can Be Sent to an Unintended Origin<![CDATA[ ## Vulnerability Details **File Location**: `src/rest_client.py:43-78` **Vulnerability Type**: Credential disclosure through unrestricted absolute URLs **Risk Level**: High ### Vulnerable Code ```python def set_auth(self, token: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None): """Set authentication.""" if token: self.session.headers["Authorization"] = f"Bearer {token}" elif username and password: self.session.auth = (username, password) def _url(self, path: str) -> str: """Build full URL.""" if self.config.base_url: return urljoin(self.config.base_url, path) return path @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) def request(self, method: str, path: str, **kwargs) -> requests.Response: """Make HTTP request with retry.""" url = self._url(path) # Apply request interceptors request = requests.Request(method, url, **kwargs) self._apply_interceptors(request, is_request=True) response = self.session.request(method, url, timeout=self.config.timeout, **kwargs) # Apply response interceptors self._apply_interceptors(response, is_request=False) return response ``` ### Technical Analysis `urllib.parse.urljoin` permits an absolute second argument to replace the configured base URL: ```python urljoin("https://trusted.example/api/", "https://attacker.example/collect") ``` The result is `https://attacker.example/collect`. The client stores bearer authentication in the session's default headers and basic authentication in `session.auth`. It then uses the same authenticated session for the resolved URL without checking that the destination retains the configured scheme, host, and port. Therefore, an absolute `path` can redirect the initial request to an arbitrary origin while retaining session-level authentication. This is distinct from cross-origin redirect behavior: the request ...[truncated 1652 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. When `base_url` is configured, reject absolute request paths: ```python from urllib.parse import urlparse parsed_path = urlparse(path) if parsed_path.scheme or parsed_path.netloc: raise ValueError("Absolute request URLs are not allowed") ``` 2. Resolve the URL and enforce an exact same-origin policy: ```python from urllib.parse import urljoin, urlparse resolved = urljoin(self.config.base_url, path) base = urlparse(self.config.base_url) target = urlparse(resolved) if ( target.scheme.lower(), target.hostname, target.port, ) != ( base.scheme.lower(), base.hostname, base.port, ): raise ValueError("Request destination is outside the configured origin") ``` 3. Attach authentication only after destination validation rather than storing sensitive authorization globally on a session used for arbitrary URLs. 4. Disable redirects by default for authenticated clients or validate every redirect destination. Ensure authorization headers are never forwarded across origins. 5. Consider restricting schemes to HTTPS except for explicitly permitted local test servers. 6. If absolute URLs are a required feature, provide a separate unauthenticated request method and require explicit opt-in before sending credentials cross-origin. 7. Add regression tests confirming that bearer and basic credentials are not sent when paths contain: - Absolute external URLs - Scheme-relative URLs such as `//attacker.example/path` - Alternate ports - User-info URL forms - Redirects to a different origin ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (57)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a broad API test automation tool covering REST/GraphQL, interface testing, performance testing, contract testing, and Mock services. However, the supplied code chunk is narrowly focused on contract testing for OpenAPI/Swagger schemas. It validates schema-defined endpoints and responses, generates schema-based test data, extracts endpoints, and integrates with Schemathesis for contract-oriented API checks. There is no evidence in this chunk of GraphQL handling, performance/load testing, or mock service creation. This is a description-behavior mismatch because the implemented capabilities shown here are substantially narrower than the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The supplied code chunk is limited to a REST client wrapper around requests/httpx. It supports making HTTP calls, basic authentication, retries, and async usage, which could be a supporting component of an API testing tool. However, the declared description claims broader capabilities: REST/GraphQL support, interface testing, performance testing, contract testing, and Mock services. None of those higher-level testing features are present in this code, and GraphQL-specific handling is also absent. This is a description-to-behavior mismatch because the observed code provides only a subset infrastructure component, not the described multifunction API test automation tool.

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README demonstrates setting an API bearer token and sending requests to external endpoints but does not warn users that credentials and request/response data will be transmitted to third-party services. In a testing automation skill, users may paste production tokens or sensitive payloads into examples, increasing the risk of accidental credential exposure or unintended data disclosure.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The performance testing section shows concurrent load generation against a target API without any caution that such traffic can degrade service, trigger rate limits, or violate acceptable-use policies. Because this skill is specifically designed for API testing and includes concurrency, duration, and total request volume, omission of safety guidance makes accidental disruptive use more likely.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill documents functionality that inherently uses network access and likely file read/write behavior, but it declares no explicit tool scope or permissions boundaries. In agent environments, missing scope declarations can allow broader-than-expected access and make it harder for operators to understand or constrain what the skill may do.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill advertises performance testing and mock-service features without warning that these actions can stress remote systems, alter shared test environments, or be misused against unintended targets. In an agent context, omission of operational safety guidance increases the chance of accidental disruptive behavior.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The method constructs a Schemathesis schema with a caller-supplied base URL and the generated test invokes `case.call_and_validate()`, which issues network requests against the target API. While this is part of testing functionality, the code provides no visible user disclosure such as logging, confirmation, or an explicit warning that live HTTP requests will be sent.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The set_auth method places a bearer token into the Authorization header, which is then included in subsequent network calls. The file does not provide any warning or disclosure that credentials supplied here will be transmitted to an external service.

External Transmission

Medium
Category
Data Exfiltration
Content
if operation_name:
            payload["operationName"] = operation_name
            
        response = requests.post(
            self.endpoint,
            headers=self.headers,
            json=payload
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This code performs outbound HTTP requests carrying user-supplied GraphQL queries, variables, and configured headers to a remote endpoint. Although the methods have technical docstrings, there is no user-facing warning, confirmation, or disclosure that data and authentication headers may be transmitted off-system.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The mock server records the full request body and all headers for every incoming request, which can capture credentials, API keys, cookies, bearer tokens, and personal data. In a testing tool this is often intentional for debugging, but without redaction, opt-in controls, retention limits, or warnings, it creates a real risk of sensitive data exposure through logs, memory dumps, test artifacts, or downstream consumers of request_log.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The load, stress, and spike test methods are explicitly designed to generate high volumes of requests and expose easy-to-call interfaces that can direct significant traffic at arbitrary targets. In an automation skill context, the lack of guardrails, target validation, rate caps, or prominent safety warnings increases the risk of accidental misuse against production or third-party systems, effectively enabling denial-of-service style behavior.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The JSON report serializes test messages and raw output directly to disk, which can capture sensitive data such as API tokens, credentials, PII, stack traces, or request/response bodies. In an API testing tool, this context makes the risk more concrete because test failures often include secrets and full payloads, so local report artifacts can become a secondary data-exposure channel.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The Allure result files include test metadata and, on failure, persist messages and traces to disk without sanitization. In this skill's API test automation context, traces commonly contain endpoint details, credentials, tokens, or response bodies, so these artifacts may expose sensitive operational data to other local users, CI artifacts, or log collectors.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def graphql_client():
    """Create a test GraphQL client."""
    return GraphQLClient(
        endpoint="https://api.example.com/graphql",
        headers={"Authorization": "Bearer test-token"}
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The README content is presented in Chinese, and there is no indication that users may choose another language or that the skill is intentionally limited to a Chinese-language audience. This can violate language/locale policy when a specific language is imposed without opt-in.

Static analysis

No suspicious patterns detected.