Back to skill

Security audit

Nota Sign

Security checks for vulnerabilities and agentic risk

Overview

This Nota Sign skill is purpose-aligned, but needs review because it stores a long-lived private key in plaintext and relies on unpinned npm tooling while handling documents and signer data.

Review before installing. Use only with documents you are authorized to send to Nota Sign, prefer a pinned dependency workflow instead of live npx downloads, store credentials only in the home config with restrictive permissions, avoid project-local config files, and do not submit arbitrary internal or private-network URLs as document inputs.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/send_envelope.ts:525
Finding

Nota Sign private key is stored without enforced restrictive permissions

Content
View full analysis
'); if (!appKey) { return { success: false, message: 'App Key is required' }; } ``` ```typescript // Write config file try { await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf8'); return { success: true, message: 'Configuration saved successfully', configPath }; } catch (error) { return { success: false, message: `Failed to write config file: ${error}` }; } ``` The configuration object written here contains `appId`, `appKey`, `userCode`, `serverRegion`, and `environment`. The `appKey` is a Base64-encoded PKCS#8 RSA private key. ### Technical Analysis The script writes a long-lived authentication private key to a plaintext JSON file without explicitly applying a restrictive file mode. The resulting permissions depend on the process umask and any permissions on an existing file. Under a permissive environment, another local account could read the key. The README recommends manually running `chmod 600`, but the initialization implementation does not enforce this control. The private key is also collected through the ordinary `prompt` function, which reads from standard input without disabling terminal echo. Consequently, the key can be exposed on screen, in terminal recordings, or to shoulder surfing. The private key must remain locally available because it is required to sign JWT and API requests. Persisting it is therefore related to the declared functionality, but storing it without enforced access controls exceeds safe minimum-secret-handling requirements. ### Attack Path 1. A user runs `scripts/send_envelope.ts init`. 2. The user enters the Nota Sign PKCS#8 private key into an echoed terminal prompt. 3. The ...[truncated 1112 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Error
Location
SKILL.md:72
Finding

Unpinned packages are downloaded and executed through npx

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/send_envelope.ts:65
Finding

Arbitrary remote document URLs are delegated for server-side retrieval

Content
View full analysis
{ const isUrl = filePath.startsWith('http://') || filePath.startsWith('https://'); if (isUrl) { const fileName = path.basename(new URL(filePath).pathname); assertSupportedFileName(fileName); return; } ``` ```typescript async function sendDocumentForSigning( filePath: string, signers: Array<{ userName: string; userEmail: string }>, subject?: string ): Promise { const isUrl = filePath.startsWith('http://') || filePath.startsWith('https://'); const fileId = isUrl ? await convertFileWithUrl(filePath, path.basename(new URL(filePath).pathname)) : await uploadDocument(filePath); ``` The URL is then placed into an API request: ```typescript const response = await httpPost('/api/file/process', { fileUrls: [{ fileUrl, fileName, fileType: 'document' }] }, token); ``` ### Technical Analysis Remote inputs are validated only by checking whether the string begins with `http://` or `https://` and whether the URL path has a supported file extension. The implementation does not reject: - Loopback destinations - Private or link-local network addresses - Cloud metadata addresses - Embedded URL credentials - Nonstandard ports - DNS names resolving to reserved addresses - Redirect chains leading to restricted destinations The URL is not fetched locally. Instead, it is transmitted to Nota Sign's `/api/file/process` endpoint, where the downstream service is expected to retrieve it. This creates a potential server-side request forgery condition in Nota Sign infrastructure if that service does not independently enforce destination restrictions. Accepting a user-provided public document URL is part of the declared funct ...[truncated 1549 chars]
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (25)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
nvironment selection, file validation, signer collection, and execution through scripts/send_envelope.ts, with a temporary node@20 fallback when local Node.js i

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
nvironment selection, file validation, signer collection, and execution through scripts/send_envelope.ts, with a temporary node@20 fallback when local Node.js i

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
nvironment selection, file validation, signer collection, and execution through scripts/send_envelope.ts, with a temporary node@20 fallback when local Node.js i

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
nvironment selection, file validation, signer collection, and execution through scripts/send_envelope.ts, with a temporary node@20 fallback when local Node.js i

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
nvironment selection, file validation, signer collection, and execution through scripts/send_envelope.ts, with a temporary node@20 fallback when local Node.js i

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/send_envelope.ts (reported line 317)May include surrounding context.

ts
}, '');

  if (!response.success || !response.data) {
    throw new Error('Failed to get access token: ' + (response.message || response.code));
  }

  cachedAccessToken = response.data.accessToken;

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README explains how to send local files or URLs for signature but does not clearly warn that document contents, file metadata, signer names, and email addresses will be transmitted to an external e-signature provider. In a skill centered on handling potentially sensitive documents, missing disclosure can lead to inadvertent data exfiltration or policy violations by users who assume processing is local.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

The README instructs users to run npx tsx without pinning an exact package version. Because npx may fetch the latest package from the registry at execution time, a compromised upstream package, typo-squatted dependency, or breaking update could lead to unreviewed code execution on the operator's machine.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

This command again relies on unpinned npx tsx, which can download and execute whatever version is current at runtime. In a skill that handles sensitive documents and credentials, this increases supply-chain risk because execution happens before any document handling safeguards matter.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

The fallback command dynamically fetches both node@20 and tsx from npm for immediate execution, and tsx is not pinned to a specific version. This creates a broader supply-chain execution surface because multiple packages are being pulled at runtime, including in environments already lacking the preferred local runtime.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.md (reported line 98)May include surrounding context.

md
- `PROD` and `UAT` do not share the same credential set
- when switching environments, replace `appId`, `appKey`, `userCode`, and `serverRegion` with values issued for that target environment
- changing only `"environment": "PROD"` to `"environment": "UAT"` is not enough
- protect the file with `chmod 600 ~/.notasign/config.json`

## Parameters

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

This initialization command uses unpinned npx tsx, so users may execute an unreviewed version from the registry while entering or processing Nota Sign credentials. That creates a realistic path for credential theft or arbitrary code execution if the package supply chain is compromised.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

The example for sending documents to one signer uses unpinned npx tsx, which can result in execution of changing third-party code at invocation time. Because this workflow processes potentially confidential documents and signer metadata, the context makes the supply-chain risk more sensitive than a generic dev tool example.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

This multi-signer send example repeats the same unpinned npx tsx pattern. An attacker who compromises the package distribution path could gain code execution in the same session where sensitive document paths, recipient data, and service credentials are present.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

The troubleshooting guidance recommends a fallback using runtime-fetched node@20 and tsx, again with tsx unpinned and node only loosely versioned. This is especially risky because troubleshooting steps are likely to be copied verbatim by users under time pressure, increasing the chance of executing unreviewed packages from npm.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill performs operations that require filesystem, environment, and network access, but it does not declare an explicit tool/permission scope. That makes the agent's effective capabilities ambiguous and can lead to overbroad execution in environments where least-privilege controls depend on the manifest.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
91% confidence
Finding

The skill persists sensitive Nota Sign credentials, including a private key, to local config files in ./notasign-config.json or ~/.notasign/config.json. Persistent local storage of secrets increases exposure to theft by other local processes, accidental inclusion in repositories, or reuse across sessions beyond the user's immediate intent.

Content

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

md
## Workflow

1. Check whether `./notasign-config.json` exists; if not, check `~/.notasign/config.json`.
2. If config is missing, ask only for the missing credential fields and write the config file.
3. If the user wants to switch between `PROD` and `UAT`, do not only flip `environment`. Collect the full target-environment values for `appId`, `appKey`, `userCode`, and `serverRegion`, then rewrite the config.
4. Validate the file input before sending:
   - local path must exist, or

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding

The skill instructs the agent to run code through npx tsx and also to dynamically install node@20 and tsx from npm at execution time without pinning immutable versions or integrity hashes. This creates a supply-chain risk: a compromised or unexpected package version could execute arbitrary code with access to the document, credentials, config files, and network.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest says the skill can 'send a signing link,' but this file only uploads/Processes documents and calls /api/envelope/create with autoSend enabled. There is no code path to retrieve, generate, or send a signing link to users, so the implemented behavior is narrower than the declared capability.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
87% confidence
Finding

The script stores long-lived authentication material, including the private signing key, in a persistent config file under the user's home or working directory. In this context, persistent secret storage expands the window for compromise and may expose credentials to other local users, backups, or accidentally committed project files.

Content

Scanner excerpt · scripts/send_envelope.ts (reported line 526)May include surrounding context.

ts
try {
      await fs.mkdir(homeConfigDir, { recursive: true });
    } catch (error) {
      return { success: false, message: `Failed to create config directory: ${error}` };
    }
  }

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script persistently writes the Base64-encoded PKCS#8 private key to disk in plaintext JSON without warning the user or applying file-permission hardening. In a skill that handles signing credentials, local disclosure of this config would allow misuse of the private key and unauthorized API authentication/signing.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The interactive workflow switches to Chinese-only prompts and status strings such as 交互模式, 文件路径, and 信封发送成功 without asking the user for a preferred language. This is a natural-language policy issue because it imposes a locale/language choice rather than offering an option or documenting a justified regional constraint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script uploads document contents to external Nota Sign endpoints as part of normal operation, but interactive mode does not provide an explicit disclosure or confirmation at send time. In an agent-skill context, users may provide local file paths expecting local processing, so silent transfer of potentially sensitive documents to third-party services increases data exposure risk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The description advertises support for 'uploaded attachment' input, but validateInputFile and the rest of the flow only recognize local file paths and remote URLs. There is no attachment-resolution logic or integration with any attachment abstraction in this file.

Content

No source excerpt is available for this finding.