Back to skill

Security audit

Testing Gate

Security checks for vulnerabilities and agentic risk

Overview

This skill is a small testing-gate library with coherent behavior, but its quality-gate logic has validation and fail-open weaknesses users should understand before relying on it for releases.

Reasonable to install for experimentation or non-critical use, but do not rely on it as the sole merge or release gate without adding input validation, requiring all mandatory checks, and avoiding shared TestingGate instances across concurrent jobs.

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

Warning
Location
src/core.py:22
Finding

Quality-Gate Policy Bypass Through Unvalidated Thresholds

Content
View full analysis

Vulnerability Details

File Location: src/models.py:12-18, src/models.py:49-52, src/core.py:22-36, and src/core.py:66-83
Vulnerability Type: Improper input validation and fail-open policy evaluation
Risk Level: Medium

Vulnerable Code

python
@dataclass
class CoverageMetrics:
    """Coverage metrics data."""
    line_coverage: float
    branch_coverage: float
    function_coverage: float
    target_line: float = 80.0
    target_branch: float = 70.0
    target_function: float = 80.0
python
unit_tests: List[str] = field(default_factory=list)
integration_tests: List[str] = field(default_factory=list)
e2e_tests: List[str] = field(default_factory=list)
min_test_count: int = 1
python
for name, (value, target) in checks.items():
    ratio = (value / target) if target > 0 else 0.0
    ratios.append(min(ratio, 1.0))
    passed = value >= target
    if not passed:
        all_passed = False
    details[name] = {
        "value": value,
        "target": target,
        "passed": passed,
        "ratio": round(ratio, 4),
    }
python
total = strategy.total_tests()
has_unit = len(strategy.unit_tests) > 0
meets_min = total >= strategy.min_test_count

layers_present = sum([
    len(strategy.unit_tests) > 0,
    len(strategy.integration_tests) > 0,
    len(strategy.e2e_tests) > 0,
])

details = {
    "total_tests": total,
    "min_test_count": strategy.min_test_count,
    "meets_min_count": meets_min,
    "has_unit_tests": has_unit,
    "has_integration_tests": len(strategy.integration_tests) > 0,
    "has_e2e_tests": len(strategy.e2e_tests) > 0,
    "layers_present": layers_present,
}

passed = meets_min and has_unit
ratio = min(total / strategy.min_test_count, 1.0) if strategy.min_test_count > 0 else 1.0

Technical Analysis

The data models accept coverage values, coverage targets, and minimum test co ...[truncated 1887 chars]

Remediation
View remediation

Remediation Suggestions

  • Add validation in CoverageMetrics.__post_init__() requiring all coverage values and targets to be finite numeric values in the range 0 through 100.
  • Require coverage targets to be strictly positive if a zero target is not an explicitly supported policy.
  • Add validation in TestStrategy.__post_init__() requiring min_test_count to be an integer of at least 1.
  • Reject booleans, strings, non-finite floats, and other values that only incidentally support comparisons.
  • Validate that all test collections are lists of non-empty strings.
  • Treat invalid configuration as a failed gate result or raise a documented validation exception before evaluation.
  • Replace tests that expect negative thresholds to pass with tests asserting that invalid thresholds are rejected.

T09 · Insecure Skill Coding Practices

Warning
Location
src/core.py:133
Finding

Aggregate Gate Fails Open When Required Checks Are Omitted

Content
View full analysis

Vulnerability Details

File Location: src/core.py:133-148
Vulnerability Type: Missing mandatory-control enforcement
Risk Level: Medium

Vulnerable Code

python
def run_all_checks(self, context: Dict) -> List[GateResult]:
    """Run all checks based on context."""
    with self._lock:
        self.results = []
    if "coverage_metrics" in context:
        self.check_coverage(context["coverage_metrics"])
    if "test_strategy" in context:
        self.check_test_strategy(context["test_strategy"])
    if "regression_artifacts" in context:
        self.check_regression(context["regression_artifacts"])
    with self._lock:
        return list(self.results)

def overall_passed(self) -> bool:
    """Return True if all recorded results passed."""
    with self._lock:
        if not self.results:
            return False
        return all(r.passed for r in self.results)

Technical Analysis

run_all_checks() silently skips any check whose corresponding context key is absent. overall_passed() only verifies that at least one result exists and that every recorded result passed. It does not verify that coverage, strategy, and regression checks were all executed.

As a result, a partial context containing a single permissive check can produce an overall passing result. This behavior conflicts with the documented use of the method as a unified quality gate before merge or release. A security or quality control that is not run is treated as though it imposed no restriction.

Attack Path

  1. A caller invokes run_all_checks() with a context containing only one favorable input, such as passing coverage_metrics.
  2. The caller omits test_strategy and regression_artifacts.
  3. The method silently skips the two omitted checks and records only the passing coverage result.
  4. The caller invokes overall_passed().
  5. Because the result list is non-empty and its sole result pas ...[truncated 471 chars]
Remediation
View remediation

Remediation Suggestions

  • Define an immutable set of mandatory checks, such as coverage, test_strategy, and regression.
  • Make run_all_checks() reject contexts missing any mandatory input.
  • Alternatively, generate an explicit failed GateResult for every missing mandatory check.
  • Record which checks were expected and which were executed.
  • Update overall_passed() to require a result for every mandatory check in addition to requiring all results to pass.
  • If partial execution is a legitimate use case, require the caller to provide an explicit policy describing the enabled checks rather than inferring policy from omitted keys.
  • Add tests confirming that a partial context cannot produce a successful complete-gate decision.

T09 · Insecure Skill Coding Practices

Note
Location
src/core.py:133
Finding

Concurrent Aggregate Runs Can Interleave and Corrupt Gate Decisions

Content
View full analysis

Vulnerability Details

File Location: src/core.py:133-144
Vulnerability Type: Race condition and non-atomic shared-state update
Risk Level: Low

Vulnerable Code

python
def run_all_checks(self, context: Dict) -> List[GateResult]:
    """Run all checks based on context."""
    with self._lock:
        self.results = []
    if "coverage_metrics" in context:
        self.check_coverage(context["coverage_metrics"])
    if "test_strategy" in context:
        self.check_test_strategy(context["test_strategy"])
    if "regression_artifacts" in context:
        self.check_regression(context["regression_artifacts"])
    with self._lock:
        return list(self.results)

Each individual check subsequently appends to the shared list under a separate lock:

python
with self._lock:
    self.results.append(result)

Technical Analysis

The lock protects individual list assignments and append operations, but it does not make an entire run_all_checks() invocation atomic. The method releases the lock immediately after clearing self.results, performs each check separately, and reacquires the lock only when returning the list.

Two threads using the same TestingGate instance can therefore clear and append to the same shared list in an interleaved order. One run may erase results already produced by another run, or each run may return a mixture of results associated with different contexts. The implementation consequently does not fully satisfy its documented thread-safety claim for aggregate operations.

Attack Path

  1. A service or CI worker shares one TestingGate instance between concurrent requests.
  2. Thread A starts run_all_checks(context_a), clears the shared results, and appends one result.
  3. Thread B starts run_all_checks(context_b) and clears the list, deleting Thread A's recorded result.
  4. Both threads continue appending results from their respective conte ...[truncated 642 chars]
Remediation
View remediation

Remediation Suggestions

  • Compute all results in a local list that is not shared between requests.
  • Publish the completed local list to self.results in one atomic, locked assignment.
  • Alternatively, hold an operation-level lock for the complete duration of run_all_checks(), including all checks.
  • Prefer returning request-local results and avoid using shared mutable state as the authoritative result of an aggregate run.
  • If historical results are required, store them by immutable run identifier rather than replacing one global list.
  • Add deterministic concurrency tests using barriers or events to force interleaving and verify that every returned result belongs to exactly one context.
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (1)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill advertises code-related capabilities involving file reads and writes, but it does not declare any explicit tool scope such as permissions or allowed-tools. That mismatch weakens least-privilege controls and can cause an agent or reviewer to underestimate what the skill may access or modify at runtime.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.