Back to skill

Security audit

eml to sqlite indexer

Security checks for vulnerabilities and agentic risk

Overview

This email-indexing skill matches its stated purpose, but it needs review because it handles sensitive mail data and includes weak admin/security controls that could expose or delete files.

Install only in a controlled local environment, change the admin password before use, avoid exposing the Flask server beyond trusted hosts, and treat all indexed emails and restored backups as untrusted. Review or patch the XSS, default-credential, restore validation, resource-limit, and file-deletion path-containment issues before using it on important mail archives.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
templates/index.html:232
Finding
Stored DOM-Based Cross-Site Scripting Through Indexed Email Metadata<![CDATA[ ## Vulnerability Details **File Location**: `templates/index.html`, lines 232-241 **Vulnerability Type**: Stored DOM-based cross-site scripting **Risk Level**: High ### Vulnerable Code ```javascript data.results.forEach(email => { const row = document.createElement('tr'); row.className = 'email-row'; let html = ` <td class="text-muted small" onclick="window.open('/email/${email.id}', '_blank')">${email.sent_time}</td> <td class="text-truncate" style="max-width: 250px;" onclick="window.open('/email/${email.id}', '_blank')">${email.sender}</td> <td class="fw-medium" onclick="window.open('/email/${email.id}', '_blank')">${email.subject}</td> `; if (showAdmin) { html += `<td><span class="delete-btn" onclick="deleteEmail(event, ${email.id})">🗑️</span></td>`; } row.innerHTML = html; resultsBody.appendChild(row); }); ``` ### Technical Analysis The sender, subject, and date values originate in EML headers and are stored without content sanitization. The search API returns these fields as JSON, after which the browser interpolates them into an HTML string and assigns that string to `innerHTML`. JSON encoding does not make values safe for insertion into an HTML parsing context. A malicious EML header containing HTML event handlers or other executable markup will therefore be parsed and executed by the browser. The detail template uses Jinja autoescaping, but that protection does not apply to this client-side `innerHTML` operation. ### Attack Path 1. An attacker sends or supplies an EML file with a malicious `Subject`, `From`, or `Date` header. 2. `indexer.py` parses the header and stores the malicious value in SQLite. 3. A permitted user or administrator opens the search page. 4. The `/search` endpoint returns the malicious value in JSON. 5. The page inserts it into `row.innerHTML`. 6. The payload executes in the EML indexer's browser origin. 7. If the victim has entered administrator Basi ...[truncated 570 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not concatenate email fields into HTML strings. - Create each table cell with DOM APIs and assign untrusted values through `textContent`. - Register click handlers with `addEventListener` instead of inline `onclick` attributes. - Apply a restrictive Content Security Policy that disallows inline scripts and event handlers. - Treat all parsed EML fields as untrusted, regardless of who imported the source directory. Example: ```javascript const senderCell = document.createElement('td'); senderCell.textContent = email.sender; senderCell.addEventListener('click', () => { window.open(`/email/${encodeURIComponent(email.id)}`, '_blank'); }); row.appendChild(senderCell); ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
app.py:21
Finding
Known Default Administrator Credential Transmitted Using Cleartext HTTP Basic Authentication<![CDATA[ ## Vulnerability Details **File Location**: `app.py`, lines 21-25, 60-73, and 365; `config.json`, line 7 **Vulnerability Type**: Hardcoded default credential and insecure authentication transport **Risk Level**: High ### Vulnerable Code ```python DEFAULT_CONFIG = { "allowed_ips": ["127.0.0.1", "localhost", "::1"], "admin_password": "change_me_now", "backup_interval_days": 3, "backup_hour": 2, "max_backups": 5, "last_backup_date": "" } ``` ```python def check_auth(username, password): config = load_config() return username == 'admin' and password == config.get("admin_password", "change_me_now") def authenticate(): return Response( 'Administrator privileges are required.', 401, {'WWW-Authenticate': 'Basic realm="Admin Access"'}) ``` ```python if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False) ``` The shipped configuration also contains: ```json "admin_password": "change_me_now" ``` ### Technical Analysis Every installation starts with the same publicly knowable administrator password, and the application does not require it to be changed before privileged functionality becomes available. The password is stored in plaintext and compared directly. The application uses HTTP Basic Authentication, which only encodes credentials and does not encrypt them. The Flask development server is exposed on all network interfaces without TLS. Although the default IP allowlist is limited to loopback addresses, the application explicitly allows administrators to expand that list. Once remote access is enabled, credentials and sensitive email data may cross the network over cleartext HTTP. IP allowlisting is not a substitute for strong authentication or encrypted transport. ### Attack Path 1. The operator starts the application without changing the shipped password. 2. The attacker gains access from a permitted address or through local browser access, port forwarding, or another ...[truncated 887 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Refuse to start privileged endpoints until a unique, sufficiently strong administrator secret is configured. - Remove the fallback password from source code and configuration templates. - Store only a salted password hash using a suitable password-hashing algorithm such as Argon2id, scrypt, or bcrypt. - Use a constant-time verification function. - Terminate TLS in a hardened production web server or reverse proxy. - Bind to `127.0.0.1` by default rather than `0.0.0.0`. - Do not use Flask's development server for production deployment. - Add authentication rate limiting and audit logging. - Avoid returning the administrator password through the configuration API. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
app.py:287
Finding
Physical File Deletion Is Not Confined to an Authorized Email Directory<![CDATA[ ## Vulnerability Details **File Location**: `app.py`, lines 287-300; related restore path handling at lines 101-110 and 349-357 **Vulnerability Type**: Insufficient path authorization for destructive filesystem operations **Risk Level**: High ### Vulnerable Code Restore accepts absolute paths as long as they end in `.eml`: ```python file_path = item.get('file_path', '') if '..' in file_path or (file_path and not file_path.lower().endswith('.eml')): file_path = "[SECURITY_RESTRICTED_INVALID_PATH]" cursor.execute(''' INSERT OR IGNORE INTO emails (file_hash, subject, sender, recipient, sent_time, body, file_path) VALUES (?, ?, ?, ?, ?, ?, ?) ''', (item['file_hash'], item['subject'], item['sender'], item['recipient'], item['sent_time'], item['body'], file_path)) ``` Deletion resolves and removes that path without enforcing an approved root: ```python file_path = row['file_path'] cursor.execute("DELETE FROM emails WHERE id = ?", (email_id,)) conn.commit() conn.close() if file_path and '..' not in file_path: abs_path = os.path.abspath(file_path) if abs_path.lower().endswith('.eml') and os.path.exists(abs_path) and os.path.isfile(abs_path): try: os.remove(abs_path) except Exception: pass ``` ### Technical Analysis Checking for the substring `..` and an `.eml` suffix does not establish that a file is inside the directory the indexer was authorized to manage. Absolute paths such as `/sensitive/location/message.eml` contain no `..` component and pass the restore validation. The deletion endpoint subsequently converts the stored path to an absolute path and deletes it if it exists and ends in `.eml`. No immutable indexing root is stored or checked, and no `realpath`/`commonpath` containment verification is performed. Symlinks and path aliases can further complicate containment. The ability to delete physical EML files is declared, but deleting any process-accessible `.eml` file exceed ...[truncated 1063 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Define an immutable, canonical email root directory outside restore data. - Resolve both the root and candidate using `os.path.realpath`. - Verify containment with `os.path.commonpath([root, candidate]) == root`. - Reject absolute paths and restored filesystem paths unless they are explicitly remapped under the authorized root. - Use stable internal identifiers rather than trusting backup-provided paths. - Reject symlinks or verify the final resolved target immediately before deletion. - Consider making physical deletion an independently enabled feature. - Record filesystem deletion attempts and failures in an audit log. - Perform the filesystem validation before deleting the database record, and use recoverable deletion or quarantine where possible. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
app.py:245
Finding
CSV Formula Injection Through Exported Email Fields<![CDATA[ ## Vulnerability Details **File Location**: `app.py`, lines 245-252 **Vulnerability Type**: Spreadsheet formula injection **Risk Level**: Medium ### Vulnerable Code ```python output = io.StringIO() output.write('\ufeff') writer = csv.writer(output) writer.writerow(['Subject', 'Sender', 'Recipient', 'Sent Time', 'Body Preview', 'File Path']) show_path = is_localhost(get_client_ip()) for row in rows: body_preview = row['body'][:200].replace('\n', ' ') + '...' if row['body'] else '' file_path_display = row['file_path'] if show_path else '[PATH HIDDEN]' writer.writerow([ row['subject'], row['sender'], row['recipient'], row['sent_time'], body_preview, file_path_display ]) ``` ### Technical Analysis Email headers and body text are attacker-controlled values. The CSV writer correctly handles CSV delimiters and quoting, but CSV quoting does not neutralize spreadsheet formulas. When a cell begins with characters such as `=`, `+`, `-`, or `@`, spreadsheet applications may interpret it as a formula rather than plain text. Depending on the spreadsheet product and its security settings, a formula can initiate external requests, disclose information through attacker-controlled URLs, or invoke dangerous legacy functionality. ### Attack Path 1. An attacker creates an EML message with a subject, sender, recipient, or body beginning with a spreadsheet formula marker. 2. The message is indexed into SQLite. 3. A user exports matching results through `/export_excel`. 4. The malicious value is written unchanged to the CSV. 5. The user opens the CSV in a spreadsheet application. 6. The spreadsheet evaluates or prompts to evaluate the malicious formula. ### Impact Assessment Exploitation occurs on the workstation of the user opening the export. Potential consequences include outbound requests containing spreadsheet data, tracking of document access, exposure of local information, or execution of dange ...[truncated 150 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Sanitize every exported text cell, not only the subject. - If a cell begins with `=`, `+`, `-`, `@`, tab, carriage return, or another product-specific formula trigger, prefix it with a single quote or another safe text marker. - Normalize leading whitespace before checking for formula markers. - Use a well-reviewed spreadsheet-export library with explicit string cell types when producing XLSX files. - Document that exported content is untrusted and test behavior in supported spreadsheet applications. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
app.py:349
Finding
Unbounded Search, Export, and Restore Operations Permit Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `app.py`, lines 180-181, 241-248, and 349-357 **Vulnerability Type**: Missing resource limits and unsafe bulk processing **Risk Level**: Medium ### Vulnerable Code Client-controlled pagination has no upper bound: ```python page = int(request.args.get('page', 1)) per_page = int(request.args.get('per_page', 50)) offset = (page - 1) * per_page ``` Export loads all matching records into memory: ```python cursor.execute( "SELECT subject, sender, recipient, sent_time, body, file_path" + base_sql + " ORDER BY sent_time DESC", params ) rows = cursor.fetchall() conn.close() output = io.StringIO() ``` Restore has no request-size or decompressed-size limit: ```python file = request.files.get('file') if not file: return jsonify({'error': 'No file'}), 400 temp_zip = "temp_restore.zip" file.save(temp_zip) try: with zipfile.ZipFile(temp_zip, 'r') as zipf: with zipf.open('emails_data.json') as f: new_count = import_from_json(json.load(f)) ``` ### Technical Analysis The search endpoint accepts an arbitrary `per_page` value and passes it to SQLite. The export endpoint retrieves every matching row and constructs the entire CSV in memory. Since email bodies may be large and the documented use case includes millions of records, broad exports can consume substantial memory. The restore endpoint accepts a ZIP upload without enforcing HTTP request size, archive entry size, compression ratio, JSON depth, or record count. `json.load` materializes the complete decompressed JSON document in memory, allowing a small compressed archive to cause disproportionate memory and disk consumption. ### Attack Path Search exhaustion: 1. A permitted client requests `/search` with an extremely large `per_page`. 2. SQLite returns a very large result set. 3. The server converts all returned rows to dictionaries and JSON. 4. Memory and CPU consumption degrade or terminate the service. Restore exh ...[truncated 611 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate `page` and cap `per_page` to a small fixed maximum. - Reject negative, zero, malformed, and excessively large pagination values. - Stream CSV output instead of using `fetchall` and an in-memory `StringIO`. - Require narrower export filters or enforce a maximum number of exported rows. - Set Flask's maximum request size. - Inspect ZIP metadata before reading entries and enforce compressed and decompressed size limits. - Enforce compression-ratio, JSON-depth, field-length, and record-count limits. - Process restore records incrementally rather than calling `json.load` on the complete document. - Use unique securely created temporary files and prevent concurrent restore requests from sharing one filename. - Add request timeouts, rate limits, and operational monitoring. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Third-Party Dependencies Are Not Version-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt`, lines 1-2 **Vulnerability Type**: Non-reproducible and insufficiently constrained dependencies **Risk Level**: Low ### Vulnerable Code ```text flask tqdm ``` ### Technical Analysis The dependency file specifies package names without versions or integrity hashes. Each installation may therefore resolve to different package releases, including future releases that have not been reviewed with this project. The package names themselves appear legitimate, and the audit found no evidence of dependency confusion, typosquatting, or a malicious package. The issue is the absence of reproducible dependency constraints, which increases exposure to future supply-chain compromise, incompatible updates, and newly introduced vulnerabilities. ### Attack Path 1. A user installs the project at a later date using `pip install -r requirements.txt`. 2. The package index resolves an unreviewed newer release or a compromised release. 3. Package installation or import executes behavior not covered by the original audit. 4. The application runs with the permissions and access available to the EML indexer process. ### Impact Assessment Potential impact depends on the behavior of the resolved dependency release. A compromised dependency could access indexed emails, backup files, configuration, and other files available to the process. More commonly, unexpected upgrades may introduce vulnerabilities or break security assumptions. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Pin dependencies to reviewed versions using exact constraints. - Generate and commit a lock file. - Use hashes with `pip --require-hashes` where practical. - Run dependency vulnerability scanning in continuous integration. - Review and deliberately update dependency versions on a controlled schedule. - Install dependencies from the official package index or an authenticated internal mirror. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (32)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The supplied code chunk does implement part of the declared purpose: indexing EML files into an SQLite database with deduplication. However, the description claims several major capabilities not present in this code, including a web interface, search/management operations, Excel export, file deletion, IP-based access control, and JSON automated backup/restore. The actual code is a standalone indexer script invoked from the command line and does not expose those features. Therefore, the declared description materially overstates what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The supplied code chunk is a third-party Bootstrap JavaScript library file, not application logic for indexing EML files or managing an email database. Its purpose is to provide reusable browser UI behaviors and event handling. While such a library could support a web interface, this chunk does not implement the declared functionality and instead has a materially different primary purpose. Therefore, the description does not accurately represent what this specific code chunk actually does.

Missing User Warnings

High
Confidence
96% confidence
Finding
The file states that an administrator can delete a message from the web UI, removing both the database record and the physical disk file. Although the behavior is described, there is no explicit warning that this action is destructive and may be irreversible, which is important for user safety in markdown documentation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill describes capabilities that inherently require reading and writing local files, but it does not declare any explicit tool scope or permission boundaries. This creates a transparency and least-privilege problem: operators cannot easily assess what filesystem access the skill expects, and a deployment system may grant broader access than necessary.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The documentation advertises deletion of physical email files but does not clearly warn about irreversible data loss or describe safeguards such as confirmation prompts, backups, or scope restrictions. In a management interface, destructive actions against disk files can lead to accidental or unauthorized loss of evidence, records, or business data.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
Restore functionality can overwrite current indexed data, reintroduce stale or tampered records, or destroy existing state if used incorrectly, yet the documentation does not warn about overwrite and data-loss risks. Because restore is exposed through a web interface, misuse becomes more dangerous in operational environments where non-expert users may trigger it.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The delete endpoint removes the database record first and then attempts to delete whatever absolute .eml path is stored for that record, without constraining deletion to an application-owned mail store directory. If an attacker or corrupted restore inserts an arbitrary absolute path ending in .eml, an authenticated localhost admin action can delete unrelated files on disk, making this a real destructive capability beyond simple indexing.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This route deletes a database record and may also remove the underlying .eml file from disk, which is a destructive and potentially irreversible operation. Although the endpoint is admin-protected, there is no confirmation prompt, user-visible warning, logging statement, or explanatory docstring/comment that clearly discloses the deletion impact to the user.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The code treats any existing absolute path ending in .eml as deletable and only blocks simple '..' patterns, which is not a sufficient filesystem boundary. Because restored JSON can populate file_path and deletion later trusts that value, this creates an arbitrary file deletion path for any .eml file reachable by the process.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The restore endpoint accepts an uploaded ZIP, writes it to a fixed filename on disk, and imports untrusted JSON into the database with minimal validation. In this app's context, restored data can influence later behavior such as file deletion targets and can also enable local disk clobbering/race issues because all restores use the same temp_restore.zip path.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code includes user-visible strings and instructional text entirely in Traditional Chinese, such as docstrings and console output, without offering any language selection or documenting a locale-specific constraint. That creates a natural-language policy issue if the skill is expected to be generally usable rather than explicitly region-specific.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The markdown describes that users can upload a ZIP backup to restore data, but it does not warn that restoration may overwrite current indexed data or otherwise alter existing records. Because this is a data-affecting operation in a markdown skill description, a user-facing warning about the impact to existing data should be present.

Unbounded Resource Access

Medium
Category
Excessive Agency
Content
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,(t,e)=>`#${CSS.escape(e)}`)),t),s=t=>null==t?`${t}`:Object.prototype.toString.call(t).match(/\s([a-z]+)/i)[1].toLowerCase(),o=t=>{t.dispatchEvent(new Event(i))},r=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),a=t=>r(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,l=t=>{if(!r(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,p=[],m
...[truncated 27 chars]
Confidence
75% confidence
Finding
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Session Persistence

Medium
Category
Rogue Agent
Content
<div class="card-container">
                            <h5 class="mb-3">🛡️ IP 存取控制</h5>
                            <p class="text-muted small">設定允許存取此網頁的 IP 位址。Localhost 永遠被允許。</p>
                            <div id="ipList" class="mb-3"></div>
                            <div class="input-group">
                                <input type="text" id="newIp" class="form-control" placeholder="輸入新 IP (例如 192.168.1.10)">
                                <button id="addIpBtn" class="btn btn-success">➕ 新增</button>
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
<div class="card-container">
                            <h5 class="mb-3">🛡️ IP 存取控制</h5>
                            <p class="text-muted small">設定允許存取此網頁的 IP 位址。Localhost 永遠被允許。</p>
                            <div id="ipList" class="mb-3"></div>
                            <div class="input-group">
                                <input type="text" id="newIp" class="form-control" placeholder="輸入新 IP (例如 192.168.1.10)">
                                <button id="addIpBtn" class="btn btn-success">➕ 新增</button>
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
<div class="card-container">
                            <h5 class="mb-3">🛡️ IP 存取控制</h5>
                            <p class="text-muted small">設定允許存取此網頁的 IP 位址。Localhost 永遠被允許。</p>
                            <div id="ipList" class="mb-3"></div>
                            <div class="input-group">
                                <input type="text" id="newIp" class="form-control" placeholder="輸入新 IP (例如 192.168.1.10)">
                                <button id="addIpBtn" class="btn btn-success">➕ 新增</button>
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
<div class="card-container">
                            <h5 class="mb-3">🛡️ IP 存取控制</h5>
                            <p class="text-muted small">設定允許存取此網頁的 IP 位址。Localhost 永遠被允許。</p>
                            <div id="ipList" class="mb-3"></div>
                            <div class="input-group">
                                <input type="text" id="newIp" class="form-control" placeholder="輸入新 IP (例如 192.168.1.10)">
                                <button id="addIpBtn" class="btn btn-success">➕ 新增</button>
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
<div class="card-container">
                            <h5 class="mb-3">🛡️ IP 存取控制</h5>
                            <p class="text-muted small">設定允許存取此網頁的 IP 位址。Localhost 永遠被允許。</p>
                            <div id="ipList" class="mb-3"></div>
                            <div class="input-group">
                                <input type="text" id="newIp" class="form-control" placeholder="輸入新 IP (例如 192.168.1.10)">
                                <button id="addIpBtn" class="btn btn-success">➕ 新增</button>
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
<div class="card-container">
                            <h5 class="mb-3">🛡️ IP 存取控制</h5>
                            <p class="text-muted small">設定允許存取此網頁的 IP 位址。Localhost 永遠被允許。</p>
                            <div id="ipList" class="mb-3"></div>
                            <div class="input-group">
                                <input type="text" id="newIp" class="form-control" placeholder="輸入新 IP (例如 192.168.1.10)">
                                <button id="addIpBtn" class="btn btn-success">➕ 新增</button>
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
<div class="card-container">
                            <h5 class="mb-3">🛡️ IP 存取控制</h5>
                            <p class="text-muted small">設定允許存取此網頁的 IP 位址。Localhost 永遠被允許。</p>
                            <div id="ipList" class="mb-3"></div>
                            <div class="input-group">
                                <input type="text" id="newIp" class="form-control" placeholder="輸入新 IP (例如 192.168.1.10)">
                                <button id="addIpBtn" class="btn btn-success">➕ 新增</button>
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code wires the restore button directly to file selection and posts the chosen ZIP to `/api/restore`, then reloads the page, but provides no warning that restoring may overwrite existing data or change system state. For a safety-critical restore action, the UI should disclose the impact before the upload begins.

Missing User Warnings

Low
Confidence
83% confidence
Finding
Exporting original file paths can disclose sensitive host information such as usernames, mount points, project names, or directory structures, but the documentation does not warn users about this privacy exposure. While lower severity than destructive actions, path disclosure can aid reconnaissance and unintentionally leak internal environment details when CSVs are shared externally.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The file structure labels SKILL.md specifically as 'Skill documentation (English)', while a Traditional Chinese version is placed separately in references. This suggests a default language requirement rather than presenting language choice up front, which may conflict with language/locale flexibility expectations.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
Multiple user-visible strings, comments, and responses are written exclusively in Traditional Chinese, and the application does not provide any language selection or opt-in behavior. Under the policy, forcing a specific language without user choice is a natural-language policy violation unless the locale restriction is clearly documented and justified.

Context-Inappropriate Capability

Low
Confidence
83% confidence
Finding
The manifest mentions IP access control and integrated backup/restore, but the code also provides a mutable admin configuration API that can change the administrator password, backup schedule, retention, and network allowlist at runtime. That capability is an operational control plane rather than an obvious requirement of indexing EML messages itself.

Static analysis

No suspicious patterns detected.