Back to skill

Security audit

Webapp QA Loop

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed browser QA workflow with a local ledger helper and strong authorization boundaries for repair, deployment, rollback, and external actions.

Install this only if you want an agent workflow that can perform real browser QA and, when explicitly authorized, edit source, run verification, deploy, or record rollback/delivery outcomes. Keep production credentials and high-side-effect actions scoped to the current task, and do not store secrets or raw personal data in ledger evidence references.

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
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The description presents an operational browser-driven QA and test/fix/deploy skill. In contrast, this code explicitly states it 'records caller-supplied facts only' and the implementation matches that: it performs filesystem I/O for a ledger file, locking, schema validation, workflow transitions, and bookkeeping around QA/release activities. It models browser QA concepts such as scenarios and coverage, but does not actually open a browser, run a web application, collect screenshots/logs, execute commands, modify source code, or perform deployments. Its primary purpose is durable audit/validation of QA process metadata, not executing the QA/deploy work described. Therefore the description materially overstates what this code chunk does.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Resolve every path relative to this `SKILL.md`.

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · references/browser-playbook.md (reported line 84)May include surrounding context.

md
- loading prevents accidental duplicate work without trapping the user;
- empty/error/retry states explain the next action;
- overlays manage focus, escape, scroll, and stacking correctly;
- navigation, back, refresh, and deep links preserve or intentionally reset state;
- text does not clip or overlap at selected viewports;
- contrast and target size follow the product's existing system and accessibility standard.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/test_qa_ledger.py (reported line 34)May include surrounding context.

python
at = (BASE + timedelta(seconds=self.tick)).isoformat().replace("+00:00", "Z")
        self.tick += 1
        command = [sys.executable, str(SCRIPT), "--run-dir", str(self.run_dir), "--at", at, *args]
        environment = os.environ.copy()
        environment["PYTHONIOENCODING"] = "utf-8"
        result = subprocess.run(command, text=True, capture_output=True, encoding="utf-8", env=environment)
        self.assertEqual(

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/test_qa_ledger.py (reported line 46)May include surrounding context.

python
at = (BASE + timedelta(seconds=self.tick)).isoformat().replace("+00:00", "Z")
        self.tick += 1
        command = [sys.executable, str(SCRIPT), "--run-dir", str(self.run_dir), "--at", at, *args]
        environment = os.environ.copy()
        environment["PYTHONIOENCODING"] = "utf-8"
        result = subprocess.run(command, text=True, capture_output=True, encoding="utf-8", env=environment)
        self.assertEqual(

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/test_qa_ledger.py (reported line 568)May include surrounding context.

python
at = (BASE + timedelta(seconds=self.tick)).isoformat().replace("+00:00", "Z")
        self.tick += 1
        command = [sys.executable, str(SCRIPT), "--run-dir", str(self.run_dir), "--at", at, *args]
        environment = os.environ.copy()
        environment["PYTHONIOENCODING"] = "utf-8"
        result = subprocess.run(command, text=True, capture_output=True, encoding="utf-8", env=environment)
        self.assertEqual(

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill explicitly instructs reading files, writing source/tests, invoking shell commands, and performing deployment-related actions, but it does not declare an explicit tool/permission scope. That creates an authority gap where a host agent may expose broader capabilities than reviewers or users expect, increasing the chance of unintended file modification, command execution, or environment access.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/release-and-rollback.md (reported line 61)May include surrounding context.

md
- the current-task authorization source when rollback execution is authorized;
- whether authorization is unconditional or conditional on named health/regression triggers.

The user may grant conditional rollback authorization before deployment. Record each allowed condition with `--rollback-trigger`. When one exact recorded trigger occurs, execute that established application rollback without asking again, then call `record-rollback` with the matching `--trigger` and `--trigger-evidence`, restored artifact identity, and health result. A different or unevidenced trigger is not authorized. Without execution authorization, stop related production mutations/deployments, keep only safe read-only monitoring and diagnosis active, and request confirmation.

Never automatically roll back or forward production data or schema. Those actions always require a documented migration/recovery plan and separate exact authorization at execution time, even when application rollback was pre-authorized.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/scope-and-selection.md (reported line 16)May include surrounding context.

md
- existing automated coverage and known baseline failures;
- for `release` only: intended artifact, deployment path, separately authorized Git/delivery operations, and rollback readiness/execution authority.

Do not ask the user to restate facts already visible in the repository or active browser, except facts that must be explicit in the current task: production identity and external-action authorization. An `audit` does not need and must not ask for a release path.

## Separate mode from depth

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_qa_ledger.py (reported line 36)May include surrounding context.

python
command = [sys.executable, str(SCRIPT), "--run-dir", str(self.run_dir), "--at", at, *args]
        environment = os.environ.copy()
        environment["PYTHONIOENCODING"] = "utf-8"
        result = subprocess.run(command, text=True, capture_output=True, encoding="utf-8", env=environment)
        self.assertEqual(
            expected,
            result.returncode,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_qa_ledger.py (reported line 48)May include surrounding context.

python
command = [sys.executable, str(SCRIPT), "--run-dir", str(self.run_dir), "--at", at, *args]
        environment = os.environ.copy()
        environment["PYTHONIOENCODING"] = "utf-8"
        result = subprocess.run(command, text=True, capture_output=True, encoding="utf-8", env=environment)
        self.assertEqual(
            expected,
            result.returncode,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_qa_ledger.py (reported line 570)May include surrounding context.

python
def test_23_absolute_path_and_huge_id_fail_cleanly(self) -> None:
        environment = os.environ.copy()
        environment["PYTHONIOENCODING"] = "utf-8"
        relative = subprocess.run(
            [sys.executable, str(SCRIPT), "--run-dir", "~\\qa-relative", "validate"],
            text=True, capture_output=True, encoding="utf-8", env=environment,
        )

Scope Creep

Low
Category
Excessive Agency
Confidence
70% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

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

text
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/qa_ledger.py (reported line 1801)May include surrounding context.

python
raise LedgerError(f"out-of-scope issue cannot be {proposed_status}")
    changed = []
    for field in ("status", "severity", "scope_status", "root_cause", "resolution", "approach", "next_action"):
        value = getattr(args, field)
        if value is not None:
            issue[field] = value
            changed.append(field)