T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/call.mjs:45- Finding
Overbroad Collection and Remote Transmission of Sensitive Call Information
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This phone-call skill is coherent, but it needs review because it can collect sensitive call details, store account tokens locally, and send credentials and call data to an under-disclosed staging API.
Review before installing. Use it only for calls where you are comfortable with Pine AI receiving the phone number, call instructions, call context, and transcript. Avoid giving passwords, CVVs, full payment-card numbers, banking credentials, security-question answers, or one-time codes unless you have separately decided that disclosure is acceptable. Be aware that a local token is stored at ~/.pine-voice/credentials.json and that the call APIs in this artifact point to a staging hostname.
scripts/call.mjs:45Overbroad Collection and Remote Transmission of Sensitive Call Information
scripts/call.mjs:7Production Authentication Tokens and Call Data Sent to a Staging Gateway
scripts/auth-verify.mjs:6Email Verification Secrets Exposed Through Command-Line Arguments
");
process.exit(2);
}
const resp = await fetch("https://www.19pine.ai/api/v2/auth/email/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, request_token: requestToken, code }),
});
```
### Technical Analysis
The request token and email verification code are supplied through `process.argv`. Depending on the host and execution environment, command-line arguments may be exposed through:
- Process inspection utilities;
- Process-accounting systems;
- Shell history;
- Agent or orchestration command logs;
- Terminal transcripts;
- Monitoring and debugging tools.
The values are temporary authentication secrets. Although their exploitation window may be short, exposing them in argv unnecessarily broadens the set of local processes and logging systems that can observe them.
The HTTPS request itself is appropriate for transmitting these values to the authentication endpoint. The insecure behavior is placing them in the command line before transmission.
### Attack Path
1. The user receives an email verification code.
2. The agent invokes the documented command with the email, request token, and verification code as argv values.
3. A local process monitor, command logger, shell-history mechanism, or orchestration platform records the complete command line.
4. An attacker with access to that
...[truncated 834 chars]scripts/auth-verify.mjs:38Credential File Permissions Applied Non-Atomically
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import { join } from "node:path";
import { homedir } from "node:os";
const credPath = join(homedir(), ".pine-voice", "credentials.json");
try {
const creds = JSON.parse(readFileSync(credPath, "utf-8"));
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import { join } from "node:path";
import { homedir } from "node:os";
const credPath = join(homedir(), ".pine-voice", "credentials.json");
try {
const creds = JSON.parse(readFileSync(credPath, "utf-8"));
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import { join } from "node:path";
import { homedir } from "node:os";
const credPath = join(homedir(), ".pine-voice", "credentials.json");
try {
const creds = JSON.parse(readFileSync(credPath, "utf-8"));
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import { join } from "node:path";
import { homedir } from "node:os";
const credPath = join(homedir(), ".pine-voice", "credentials.json");
try {
const creds = JSON.parse(readFileSync(credPath, "utf-8"));
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import { join } from "node:path";
import { homedir } from "node:os";
const credPath = join(homedir(), ".pine-voice", "credentials.json");
try {
const creds = JSON.parse(readFileSync(credPath, "utf-8"));
The script stores an access token in a predictable plaintext file path under ~/.pine-voice/credentials.json, creating credential material at rest that can be reused if obtained by malware, local attackers, insecure backups, or accidental file sharing. Because this token likely grants API access without re-entering the verification code, compromise of the file can directly lead to account or service abuse.
}
const dir = join(homedir(), ".pine-voice");
const credPath = join(dir, "credentials.json");
mkdirSync(dir, { recursive: true });
writeFileSync(credPath, JSON.stringify({ access_token: accessToken, user_id: userId }, null, 2) + "\n");
The skill invokes Node scripts that perform authenticated network actions and place real phone calls, but the manifest does not declare an explicit tool scope such as allowed-tools or permissions. This weakens least-privilege controls and makes it harder for a host agent or reviewer to understand and constrain what external capabilities the skill requires before execution.
The skill instructs the agent to collect sensitive information the callee might need, including authentication and payment details, and then send call context and transcripts through Pine AI's service. Although a privacy section exists later, the operational steps do not require an explicit just-in-time user warning or consent before transmitting this third-party data, creating a meaningful privacy and data-disclosure risk.
The file states that the voice agent can only speak English, which is a language restriction affecting user experience and accessibility. The documentation does not offer a language choice, opt-in, or a clear organizational or compliance justification for imposing this locale constraint.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
process.exit(2);
}
const resp = await fetch("https://www.19pine.ai/api/v2/auth/email/request", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
process.exit(2);
}
const resp = await fetch("https://www.19pine.ai/api/v2/auth/email/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, request_token: requestToken, code }),
The script persists a bearer-style access token to disk in the user's home directory without any interactive disclosure, consent prompt, or code comment warning that long-lived authentication material will be stored locally. Although the file permissions are tightened to 0600, local token storage still increases the attack surface because other local processes, backups, terminal history workflows, or later misconfigurations could expose the token and enable account access.
The code loads credentials from ~/.pine-voice/credentials.json and transmits the access token and user ID to a remote gateway. While failures are logged, there is no comment, docstring, or user-facing disclosure explaining that sensitive credentials will be read and sent over the network.
No suspicious patterns detected.