Back to skill

Security audit

Compliance Guardian

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent FinCEN helper, but it handles highly sensitive identity and tax data with unsafe browser-side patterns that users should review before installing.

Review carefully before installing or using with real client data. Avoid entering SSNs/TINs or dates of birth unless you trust the hosting origin and device, clear browser storage after use, and treat clipboard exports as sensitive documents that other local apps may access. The tool should be fixed to render tracker entries with textContent, add deletion controls, and warn or redact before export.

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
compliance-guardian.html:398
Finding

Persistent DOM-Based Cross-Site Scripting in Filing Tracker

Content
View full analysis

Vulnerability Details

File Location: compliance-guardian.html, lines 398–405 and 412–434
Vulnerability Type: Persistent DOM-based cross-site scripting (XSS)
Risk Level: High

Vulnerable Code

The application stores a user-controlled property address in browser storage without validation or encoding:

javascript
let filings = JSON.parse(localStorage.getItem('fincen-filings') || '[]');
filings.push({
  address: address,
  closing: closing,
  deadline: deadline.toISOString(),
  added: new Date().toISOString()
});
localStorage.setItem('fincen-filings', JSON.stringify(filings));

It later interpolates that value directly into an HTML template and assigns the result to innerHTML:

javascript
const filings = JSON.parse(localStorage.getItem('fincen-filings') || '[]');
const list = document.getElementById('tracker-list');

if (filings.length === 0) {
  list.innerHTML = '<p style="color:#888899;font-size:13px;text-align:center;">No pending filings. Add transactions from the Pre-Filler.</p>';
  return;
}

const today = new Date();
list.innerHTML = filings.map((f, i) => {
  const deadline = new Date(f.deadline);
  const days = Math.ceil((deadline - today) / (1000 * 60 * 60 * 24));
  let statusClass = 'status-green';
  let statusText = `${days} days`;
  if (days <= 7) {
    statusClass = 'status-red';
    statusText = days <= 0 ? 'OVERDUE' : `${days} days`;
  } else if (days <= 14) {
    statusClass = 'status-yellow';
  }

  return `
    <div class="tracker-item">
      <div>
        <div class="address">${f.address}</div>
        <div class="dates">Closed: ${new Date(f.closing).toLocaleDateString()} • Due: ${deadline.toLocaleDateString()}</div>
      </div>
      <span class="status ${statusClass}">${statusText}</span>
    </div>`;
}).join('');

Technical Analysis

The property address originates from the prop-address form field and is tre ...[truncated 2106 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not construct tracker entries with HTML strings containing untrusted data. Create DOM elements and assign property addresses through textContent.
  2. If HTML rendering is unavoidable, process every untrusted value with a well-maintained contextual HTML sanitizer before insertion. Simple character replacement should not be treated as a general-purpose sanitizer.
  3. Validate data read from localStorage against an expected schema. Require strings of reasonable lengths and valid date formats, and reject malformed records.
  4. Treat browser storage as attacker-modifiable rather than trusted merely because it is local.
  5. Add a restrictive Content Security Policy that disallows inline scripts and inline event handlers, preferably using external scripts with hashes or nonces. This should be defense in depth rather than the primary fix.
  6. Clear or migrate existing fincen-filings records after deploying the correction so previously stored payloads cannot remain active.
  7. Add regression tests using addresses containing HTML metacharacters and event-handler markup, verifying that the value is displayed only as literal text.

A safe rendering pattern is:

javascript
const item = document.createElement('div');
item.className = 'tracker-item';

const details = document.createElement('div');

const addressElement = document.createElement('div');
addressElement.className = 'address';
addressElement.textContent = f.address;

const datesElement = document.createElement('div');
datesElement.className = 'dates';
datesElement.textContent =
  `Closed: ${new Date(f.closing).toLocaleDateString()} • Due: ${deadline.toLocaleDateString()}`;

const statusElement = document.createElement('span');
statusElement.className = `status ${statusClass}`;
statusElement.textContent = statusText;

details.append(addressElement, datesElement);
item.append(details, statusElement);
list.appendChild(item);
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill explicitly states that it collects highly sensitive personal and business information, including beneficial owners' names, dates of birth, addresses, citizenship, and TINs, and stores data persistently in browser localStorage, yet it does not provide any clear privacy or security warning. This is dangerous because localStorage is readable by scripts running in the same origin, persists across sessions, is not appropriate for regulated personal data, and may expose users to privacy, compliance, and unauthorized access risks on shared or compromised devices.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The export function copies a full FinCEN pre-fill report containing names, addresses, dates of birth, and TIN/SSN data to the system clipboard without an explicit warning or redaction option. Clipboard contents may be exposed to other applications, browser extensions, remote desktop tooling, or later accidental pastes, which is especially sensitive in a financial-compliance context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The tracker persists filing data in localStorage without a just-in-time warning or consent, and localStorage is readable by any script running on the same origin and remains on the device until cleared. In a compliance tool handling real-estate transaction information, persistent client-side storage increases exposure to shared-device access, browser compromise, and unrelated same-origin script issues.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The tracker section explicitly states 'Data stored in browser only,' which conveys a local-only handling expectation. However, the code also generates a full FinCEN pre-fill report and writes it to the system clipboard via navigator.clipboard.writeText, moving sensitive data outside browser-only storage.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The date rendering forces the 'en-US' locale for the deadline string, which is a natural-language locale choice applied to all users without opt-in. The policy allows locale constraints only when users are given a choice or when the restriction is clearly documented and justified; neither is present here.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.