Back to skill

Security audit

AideNote for Hermes

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its stated AideNote purpose, but it needs Review because its credential setup can expose a long-lived API key while the skill accesses sensitive recordings and installs persistent local services.

Install only if you trust AideNote and are comfortable granting access to private recordings, transcripts, tasks, and knowledge bases. Prefer the app pairing flow over manual configuration, do not use HTTP or unfamiliar API bases, and treat the local Hermes credential file and installed bridge services as sensitive persistent access that should be removed or rotated when no longer needed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/configure.py:43
Finding

API Key Disclosure Through Arbitrary or Plaintext API Endpoints

Content
View full analysis
str: value = raw.strip().rstrip("/") parsed = parse.urlparse(value) if parsed.scheme not in {"http", "https"} or not parsed.netloc: raise ValueError("API base must be an HTTP(S) URL") if parsed.username or parsed.password: raise ValueError("API base must not contain credentials") return value def verify(api_base: str, api_key: str) -> None: endpoint = api_base + "/api/UserapikeyMstr/GetToken/" + parse.quote(api_key, safe="") payload = json.dumps({"apiKey": api_key}).encode("utf-8") req = request.Request( endpoint, data=payload, method="POST", headers={ "Accept": "application/json", "Content-Type": "application/json", "X-API-Key": api_key, }, ) try: with request.urlopen(req, timeout=15) as response: decoded = json.loads(response.read().decode("utf-8")) ``` The user-selected endpoint is subsequently persisted: ```python def main() -> int: hermes_home = Path(os.environ.get("HERMES_HOME", str(Path.home() / ".hermes"))).expanduser() credentials_path = hermes_home / "aidenote-credentials.json" try: api_base = validate_base(input(f"AideNote API base [{DEFAULT_API_BASE}]: ").strip() or DEFAULT_API_BASE) api_key = getpass.getpass("AideNote API Key: ").strip() if not api_key: raise ValueError("API Key is required") verify(api_base, api_key) write_credentials(credentials_path, api_base, api_key) ``` From `scripts ...[truncated 4093 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/configure.py:79
Finding

Owner-Only Credential Access Is Not Enforced on Windows

Content
View full analysis
None: path.parent.mkdir(mode=0o700, parents=True, exist_ok=True) if os.name != "nt": path.parent.chmod(0o700) payload = json.dumps({"apiBase": api_base, "apiKey": api_key}, ensure_ascii=False, indent=2) + "\n" descriptor, temporary_name = tempfile.mkstemp(prefix=".aidenote-", dir=path.parent) temporary_path = Path(temporary_name) try: with os.fdopen(descriptor, "w", encoding="utf-8") as handle: handle.write(payload) if os.name != "nt": temporary_path.chmod(0o600) os.replace(temporary_path, path) finally: temporary_path.unlink(missing_ok=True) ``` From `scripts/aidenote.py`: ```python if os.name != "nt" and stat.S_IMODE(credentials_path.stat().st_mode) & 0o077: raise AideNoteError( "insecure_credentials", "AideNote credentials must be readable only by the current user. Rerun scripts/configure.py.", ) try: credentials = json.loads(credentials_path.read_text(encoding="utf-8")) ``` From `scripts/bridge.py`: ```python if os.name != "nt" and stat.S_IMODE(credentials_path.stat().st_mode) & 0o077: raise BridgeError( "insecure_credentials", "AideNote credentials must be readable only by the current user.", ) try: credentials = json.loads(credentials_path.read_text(encoding="utf-8")) ``` ### Technical Analysis On POSIX systems, the configurator attempts to set the credentials directory to mode `0700` and the tempora ...[truncated 1976 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (50)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Most of the querying behavior matches the description: the script authenticates to AideNote and retrieves recordings, recording details (which may include transcript/summary data depending on API response), todos, account info, and knowledge-base data. However, the declared purpose also claims installation and operation of the local connection suite and connecting the AideNote mobile app to the computer, including mobile bridge health. This code does not install anything, configure the connection, or interact with any local/mobile bridge service. Its health command only checks API authentication and recording accessibility, not mobile bridge status. Therefore the description overstates key capabilities in a materially misleading way.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description combines two major capabilities: (1) installing/operating the AideNote local connection suite, and (2) querying real AideNote user data such as notes, transcripts, summaries, todos, knowledge bases, and account status. This code supports only the first category. It performs local installation/status checks, secure credential loading, remote pairing with AideNote services, local credential persistence, installer checksum verification, and health checks for local ports/services. There is no code to call AideNote content endpoints, fetch meeting notes/transcripts/summaries, retrieve tasks/action items, search knowledge bases, or inspect user account data except limited pairing/install state. Therefore the description materially overstates the code’s actual behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose presents a broad operational skill for installing and using an AideNote connection to retrieve multiple categories of user data and service health information. However, this code chunk only handles interactive setup: validating an API base URL, securely prompting for an API key, sending it to AideNote for verification, and saving the credentials locally. While configuration can support the declared overall goal, the actual behavior of this chunk is materially narrower and does not implement the described querying and operational capabilities. Therefore the description does not accurately represent what this supplied code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code substantially matches the core declared purpose of operating an AideNote connection and querying AideNote data such as recordings, todos, account info, and knowledge bases. However, the description also claims installation capability and mobile bridge health / connecting the mobile app, which this code does not implement. It only reads pre-existing credentials from a local file and performs API calls. Additionally, while recording detail may contain notes/transcripts/summaries, there are no dedicated capabilities specifically implementing those categories in this chunk. Therefore the description overstates the implemented functionality in material ways.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code substantially matches the installation/connection-health portion of the description: it inspects bridge status, checks ports and installed files, initiates pairing, downloads a verified installer, installs local services, and verifies bridge health. However, the declared description also claims operational data-query capabilities over AideNote content (notes, transcripts, summaries, todos, knowledge bases, account status), and none of those behaviors appear in this code chunk. The code is narrowly focused on bridge setup and status, plus pairing and credential management. That makes the description materially broader than the actual behavior shown here, so this should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

The code persists the received API key in a JSON file under the Hermes home directory. While file permissions are tightened on non-Windows systems, storing long-lived credentials on disk increases exposure to local compromise, backup leakage, or accidental disclosure, and Windows lacks equivalent ACL validation here.

Content

Scanner excerpt · scripts/bridge.py (reported line 330)May include surrounding context.

python
def write_pairing_credentials(api_key: str) -> None:
    write_private_json(
        hermes_home() / "aidenote-credentials.json",
        {"apiBase": "https://api.aidenote.cn", "apiKey": api_key},
    )

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

This script collects an AideNote API key and persists it in plaintext JSON under a user-controlled path derived from HERMES_HOME. Although directory and file permissions are tightened on Unix, the credential is still stored unencrypted and may be exposed through backups, local compromise, weak Windows ACLs, or malicious redirection of HERMES_HOME to an unintended location.

Content

Scanner excerpt · scripts/configure.py (reported line 98)May include surrounding context.

python
def main() -> int:
    hermes_home = Path(os.environ.get("HERMES_HOME", str(Path.home() / ".hermes"))).expanduser()
    credentials_path = hermes_home / "aidenote-credentials.json"
    try:
        api_base = validate_base(input(f"AideNote API base [{DEFAULT_API_BASE}]: ").strip() or DEFAULT_API_BASE)
        api_key = getpass.getpass("AideNote API Key: ").strip()

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
2. Never answer that the user has no todos or recordings unless the corresponding command completed successfully and returned an empty result.
3. Never invent IDs, titles, dates, transcript text, summaries, or task status.
4. Never ask the user to paste an API key into chat. If setup is missing during an authorized install, start the built-in pairing flow and show only its 8-character code.
5. Do not print environment variables, authorization headers, access tokens, or AideNote configuration files.
6. Summarize command JSON for the user instead of dumping raw payloads, unless raw JSON is explicitly requested.
7. Run `bridge.py install --confirm` only after the user explicitly asks to install, configure, repair, or connect AideNote on this computer. A data question alone is not installation consent.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/aidenote-hermes/SKILL.md (reported line 23)May include surrounding context.

md
2. Never answer that the user has no todos or recordings unless the corresponding command completed successfully and returned an empty result.
3. Never invent IDs, titles, dates, transcript text, summaries, or task status.
4. Never ask the user to paste an API key into chat. If setup is missing during an authorized install, start the built-in pairing flow and show only its 8-character code.
5. Do not print environment variables, authorization headers, access tokens, or AideNote configuration files.
6. Summarize command JSON for the user instead of dumping raw payloads, unless raw JSON is explicitly requested.
7. Run `bridge.py install --confirm` only after the user explicitly asks to install, configure, repair, or connect AideNote on this computer. A data question alone is not installation consent.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/api-contract.md (reported line 17)May include surrounding context.

md
| `knowledge-files` | `/api/userfolderMstr/FolderList` | Files and folders in one knowledge base |
| `knowledge-recordings` | recursive `/api/userfolderMstr/FolderList` | Audio recordings in a knowledge base and its nested folders |

The preferred first-run flow uses the relay's short-lived `/agent-pair/start`, `/status`, `/approve`, and `/complete` endpoints. The AideNote App approves the 8-character code only after confirming that its signed-in account owns the dedicated API Key. The local worker stores that key in `aidenote-credentials.json` under the active Hermes profile with owner-only permissions, then destroys the relay pairing session. `scripts/configure.py` remains the manual recovery path.

All commands emit JSON. Successful output has `"ok": true`; failures have `"ok": false`, a stable `error` code, and a safe `message`. A nonzero process exit means the requested data was not retrieved and must not be interpreted as an empty result.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/aidenote.py (reported line 146)May include surrounding context.

python
| `knowledge-files` | `/api/userfolderMstr/FolderList` | Files and folders in one knowledge base |
| `knowledge-recordings` | recursive `/api/userfolderMstr/FolderList` | Audio recordings in a knowledge base and its nested folders |

The preferred first-run flow uses the relay's short-lived `/agent-pair/start`, `/status`, `/approve`, and `/complete` endpoints. The AideNote App approves the 8-character code only after confirming that its signed-in account owns the dedicated API Key. The local worker stores that key in `aidenote-credentials.json` under the active Hermes profile with owner-only permissions, then destroys the relay pairing session. `scripts/configure.py` remains the manual recovery path.

All commands emit JSON. Successful output has `"ok": true`; failures have `"ok": false`, a stable `error` code, and a safe `message`. A nonzero process exit means the requested data was not retrieved and must not be interpreted as an empty result.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/bridge.py (reported line 60)May include surrounding context.

python
| `knowledge-files` | `/api/userfolderMstr/FolderList` | Files and folders in one knowledge base |
| `knowledge-recordings` | recursive `/api/userfolderMstr/FolderList` | Audio recordings in a knowledge base and its nested folders |

The preferred first-run flow uses the relay's short-lived `/agent-pair/start`, `/status`, `/approve`, and `/complete` endpoints. The AideNote App approves the 8-character code only after confirming that its signed-in account owns the dedicated API Key. The local worker stores that key in `aidenote-credentials.json` under the active Hermes profile with owner-only permissions, then destroys the relay pairing session. `scripts/configure.py` remains the manual recovery path.

All commands emit JSON. Successful output has `"ok": true`; failures have `"ok": false`, a stable `error` code, and a safe `message`. A nonzero process exit means the requested data was not retrieved and must not be interpreted as an empty result.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/aidenote-hermes/references/api-contract.md (reported line 17)May include surrounding context.

md
| `knowledge-files` | `/api/userfolderMstr/FolderList` | Files and folders in one knowledge base |
| `knowledge-recordings` | recursive `/api/userfolderMstr/FolderList` | Audio recordings in a knowledge base and its nested folders |

The preferred first-run flow uses the relay's short-lived `/agent-pair/start`, `/status`, `/approve`, and `/complete` endpoints. The AideNote App approves the 8-character code only after confirming that its signed-in account owns the dedicated API Key. The local worker stores that key in `aidenote-credentials.json` under the active Hermes profile with owner-only permissions, then destroys the relay pairing session. `scripts/configure.py` remains the manual recovery path.

All commands emit JSON. Successful output has `"ok": true`; failures have `"ok": false`, a stable `error` code, and a safe `message`. A nonzero process exit means the requested data was not retrieved and must not be interpreted as an empty result.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/aidenote-hermes/scripts/aidenote.py (reported line 146)May include surrounding context.

python
| `knowledge-files` | `/api/userfolderMstr/FolderList` | Files and folders in one knowledge base |
| `knowledge-recordings` | recursive `/api/userfolderMstr/FolderList` | Audio recordings in a knowledge base and its nested folders |

The preferred first-run flow uses the relay's short-lived `/agent-pair/start`, `/status`, `/approve`, and `/complete` endpoints. The AideNote App approves the 8-character code only after confirming that its signed-in account owns the dedicated API Key. The local worker stores that key in `aidenote-credentials.json` under the active Hermes profile with owner-only permissions, then destroys the relay pairing session. `scripts/configure.py` remains the manual recovery path.

All commands emit JSON. Successful output has `"ok": true`; failures have `"ok": false`, a stable `error` code, and a safe `message`. A nonzero process exit means the requested data was not retrieved and must not be interpreted as an empty result.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/aidenote-hermes/scripts/bridge.py (reported line 60)May include surrounding context.

python
| `knowledge-files` | `/api/userfolderMstr/FolderList` | Files and folders in one knowledge base |
| `knowledge-recordings` | recursive `/api/userfolderMstr/FolderList` | Audio recordings in a knowledge base and its nested folders |

The preferred first-run flow uses the relay's short-lived `/agent-pair/start`, `/status`, `/approve`, and `/complete` endpoints. The AideNote App approves the 8-character code only after confirming that its signed-in account owns the dedicated API Key. The local worker stores that key in `aidenote-credentials.json` under the active Hermes profile with owner-only permissions, then destroys the relay pairing session. `scripts/configure.py` remains the manual recovery path.

All commands emit JSON. Successful output has `"ok": true`; failures have `"ok": false`, a stable `error` code, and a safe `message`. A nonzero process exit means the requested data was not retrieved and must not be interpreted as an empty result.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/aidenote-hermes/scripts/configure.py (reported line 98)May include surrounding context.

python
| `knowledge-files` | `/api/userfolderMstr/FolderList` | Files and folders in one knowledge base |
| `knowledge-recordings` | recursive `/api/userfolderMstr/FolderList` | Audio recordings in a knowledge base and its nested folders |

The preferred first-run flow uses the relay's short-lived `/agent-pair/start`, `/status`, `/approve`, and `/complete` endpoints. The AideNote App approves the 8-character code only after confirming that its signed-in account owns the dedicated API Key. The local worker stores that key in `aidenote-credentials.json` under the active Hermes profile with owner-only permissions, then destroys the relay pairing session. `scripts/configure.py` remains the manual recovery path.

All commands emit JSON. Successful output has `"ok": true`; failures have `"ok": false`, a stable `error` code, and a safe `message`. A nonzero process exit means the requested data was not retrieved and must not be interpreted as an empty result.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/bridge.py (reported line 189)May include surrounding context.

python
workbuddy_binary = install_dir / "aidenote-workbuddy-bridge.exe"
        tunnel_service = tunnel_binary
        workbuddy_service = workbuddy_binary
        hermes_service = hermes_home() / ".env"
    else:
        install_dir = Path()
        tunnel_binary = Path()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/aidenote-hermes/scripts/bridge.py (reported line 189)May include surrounding context.

python
workbuddy_binary = install_dir / "aidenote-workbuddy-bridge.exe"
        tunnel_service = tunnel_binary
        workbuddy_service = workbuddy_binary
        hermes_service = hermes_home() / ".env"
    else:
        install_dir = Path()
        tunnel_binary = Path()

Credential Access

High
Category
Privilege Escalation
Confidence
81% confidence
Finding

The pairing worker writes the returned API key to a plaintext JSON file under the Hermes home directory. While permissions are tightened on non-Windows systems, plaintext credential storage remains a theft target for local malware, backups, or other processes with user-level access, and Windows has no equivalent ACL validation here.

Content

Scanner excerpt · skills/aidenote-hermes/scripts/bridge.py (reported line 330)May include surrounding context.

python
def write_pairing_credentials(api_key: str) -> None:
    write_private_json(
        hermes_home() / "aidenote-credentials.json",
        {"apiBase": "https://api.aidenote.cn", "apiKey": api_key},
    )

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/aidenote.py (reported line 192)May include surrounding context.

python
except (error.URLError, TimeoutError, OSError, json.JSONDecodeError) as exc:
        raise ValueError("Could not verify the API Key with AideNote") from exc
    if not find_token(decoded):
        raise ValueError("AideNote verification returned no access token")


def write_credentials(path: Path, api_base: str, api_key: str) -> None:

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/configure.py (reported line 76)May include surrounding context.

python
except (error.URLError, TimeoutError, OSError, json.JSONDecodeError) as exc:
        raise ValueError("Could not verify the API Key with AideNote") from exc
    if not find_token(decoded):
        raise ValueError("AideNote verification returned no access token")


def write_credentials(path: Path, api_base: str, api_key: str) -> None:

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/aidenote-hermes/scripts/aidenote.py (reported line 192)May include surrounding context.

python
except (error.URLError, TimeoutError, OSError, json.JSONDecodeError) as exc:
        raise ValueError("Could not verify the API Key with AideNote") from exc
    if not find_token(decoded):
        raise ValueError("AideNote verification returned no access token")


def write_credentials(path: Path, api_base: str, api_key: str) -> None:

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/aidenote-hermes/scripts/configure.py (reported line 76)May include surrounding context.

python
except (error.URLError, TimeoutError, OSError, json.JSONDecodeError) as exc:
        raise ValueError("Could not verify the API Key with AideNote") from exc
    if not find_token(decoded):
        raise ValueError("AideNote verification returned no access token")


def write_credentials(path: Path, api_base: str, api_key: str) -> None:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The README explicitly states the skill can access real recordings, transcripts, summaries, todos, and knowledge bases, but it does not clearly warn users that this is highly sensitive personal and business data. In a skill that installs local connectivity and bridges a mobile app to a computer, the lack of an explicit privacy/consent warning increases the risk of users authorizing broad data access without understanding the sensitivity or scope.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill declares and encourages use of shell, network, environment, and file capabilities but does not constrain them with an explicit tool scope. In a skill that installs software, checks service state, and handles pairing/credential flows, missing capability boundaries increases the chance of unintended command execution or broader host access than users expect.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.