Back to skill

Security audit

Cozi Family Organizer

Security checks for vulnerabilities and agentic risk

Overview

This Cozi skill appears purpose-related, but it needs review because it handles raw Cozi credentials, reads a higher-level .env file, caches bearer tokens insecurely, and can delete lists or appointments.

Review before installing. Use a dedicated Cozi account if possible, prefer environment-injected secrets over shared .env files, restrict permissions on any .env and .session.json files, and be careful with delete-list and remove-appt because the skill does not document confirmation or undo behavior.

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

Warning
Location
scripts/cozi.js:112
Finding

Bearer Session Token Stored Without Restrictive File Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/cozi.js, lines 112–114
Vulnerability Type: Plaintext sensitive token storage with permissions dependent on the process umask
Risk Level: Medium

Vulnerable Code

js
function saveSession(session) {
  fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
}

The stored session object is constructed at lines 130–135 and includes sensitive authentication and account data:

js
const session = {
  accessToken: response.accessToken,
  accountId: response.accountId,
  accountPersonId: response.accountPersonId,
  expiresAt: new Date(Date.now() + response.expiresIn * 1000).toISOString()
};

Technical Analysis

The Skill caches its Cozi bearer token in .session.json under the Skill directory. The call to fs.writeFileSync does not specify a restrictive file mode, so the permissions assigned when the file is created depend on the runtime environment's umask.

In an environment with a permissive umask or insufficiently protected Skill directory, another local user or process may be able to read the cached bearer token. Because bearer-token possession is sufficient for API authentication, no knowledge of the original Cozi password would be required to use a stolen, unexpired token.

This access is related to the declared session-caching feature, but secure plaintext secret handling requires explicit access restrictions rather than reliance on ambient process configuration.

Attack Path

  1. A victim runs any Cozi command while no valid cached session exists.
  2. The Skill authenticates to rest.cozi.com using the configured Cozi credentials.
  3. The authentication response is written to .session.json, including the bearer token and account identifiers.
  4. The file is created using permissions derived from the process umask rather than an explicit owner-only mode.
  5. A local attacker or untrusted local process with read access obtains the bearer token.
  6. Before the token exp ...[truncated 873 chars]
Remediation
View remediation

Remediation Suggestions

Create and maintain the session file with owner-only permissions:

js
function saveSession(session) {
  fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2), {
    encoding: 'utf8',
    mode: 0o600
  });
  fs.chmodSync(SESSION_FILE, 0o600);
}

Additional hardening should include:

  1. Ensure the parent Skill directory is accessible only to the owning user, such as mode 0700.
  2. Validate and repair permissions on an existing session file before reading it.
  3. Reject a session file that is not a regular file or is owned by another user, where the platform exposes ownership information.
  4. Consider storing the bearer token in an operating-system credential manager instead of a plaintext JSON file.
  5. Delete the cached token when it expires and provide an explicit logout or session-clearing command.
  6. Document that the session cache contains an authentication secret and must not be shared, backed up insecurely, or committed to source control.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The manifest description says the skill manages shopping and todo lists, but the documented commands also expose calendar access, appointment creation/removal, and local credential/session handling. This mismatch can mislead operators and agents about the skill's real authority, causing over-trust and unexpected access to sensitive family scheduling data or destructive actions.

Content

No source excerpt is available for this finding.

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/cozi.js (reported line 33)May include surrounding context.

js
const LISTS_VERSION = 'api/ext/2004';
const SESSION_FILE = path.join(__dirname, '..', '.session.json');

// Load Cozi credentials from environment or skill-level .env
function loadEnv() {
  // First check if already set in process.env
  if (process.env.COZI_EMAIL && process.env.COZI_PASSWORD) {

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/cozi.js (reported line 40)May include surrounding context.

js
const LISTS_VERSION = 'api/ext/2004';
const SESSION_FILE = path.join(__dirname, '..', '.session.json');

// Load Cozi credentials from environment or skill-level .env
function loadEnv() {
  // First check if already set in process.env
  if (process.env.COZI_EMAIL && process.env.COZI_PASSWORD) {

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/cozi.js (reported line 122)May include surrounding context.

js
const LISTS_VERSION = 'api/ext/2004';
const SESSION_FILE = path.join(__dirname, '..', '.session.json');

// Load Cozi credentials from environment or skill-level .env
function loadEnv() {
  // First check if already set in process.env
  if (process.env.COZI_EMAIL && process.env.COZI_PASSWORD) {

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/cozi.js (reported line 41)May include surrounding context.

js
}
  
  // Try skill-level .env first
  const skillEnvPath = path.join(__dirname, '..', '.env');
  if (fs.existsSync(skillEnvPath)) {
    const content = fs.readFileSync(skillEnvPath, 'utf8');
    content.split('\n').forEach(line => {

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

This code explicitly targets an agent-level .env outside the skill boundary, which broadens access to secrets in the host environment. Even with a COZI_* filter, the behavior is riskier than necessary and can unintentionally couple the skill to higher-privilege secret material.

Content

Scanner excerpt · scripts/cozi.js (reported line 52)May include surrounding context.

js
});
  }
  
  // Fallback: try agent-level .env but ONLY read COZI_* vars
  const agentEnvPath = path.join(__dirname, '..', '..', '..', '.env');
  if (fs.existsSync(agentEnvPath)) {
    const content = fs.readFileSync(agentEnvPath, 'utf8');

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

Opening and reading the parent .env file creates a cross-scope credential access path inconsistent with least privilege. In agent ecosystems, such patterns are especially sensitive because secrets may be colocated for unrelated integrations.

Content

Scanner excerpt · scripts/cozi.js (reported line 53)May include surrounding context.

js
}
  
  // Fallback: try agent-level .env but ONLY read COZI_* vars
  const agentEnvPath = path.join(__dirname, '..', '..', '..', '.env');
  if (fs.existsSync(agentEnvPath)) {
    const content = fs.readFileSync(agentEnvPath, 'utf8');
    content.split('\n').forEach(line => {

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill declares access to environment-based credentials but does not define an explicit tool scope or permission boundary. This increases the chance that an agent or user invokes the skill without understanding it needs sensitive secrets, weakening least-privilege controls and auditability.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The public description omits calendar and appointment functionality even though the skill supports reading calendars and creating or removing appointments. Hidden or under-disclosed capabilities are dangerous because users may authorize or install the skill for list management while unintentionally granting access to more sensitive family scheduling workflows.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill instructs users to store account email and password in .env files without providing security or privacy guidance. Handling raw account credentials this way can expose them to local compromise, accidental disclosure, or unsafe reuse, especially for an unofficial reverse-engineered API client.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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.

Content

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

md
## Environment Variables

Set these in your agent's `.env` (`~/.openclaw/.env`) or create a skill-level `.env` at `~/.openclaw/skills/cozi/.env`:

- `COZI_EMAIL` — Your Cozi account email
- `COZI_PASSWORD` — Your Cozi account password

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documented commands include delete-list and remove-appt operations without warning about permanence, confirmation, or recovery limitations. In an agent setting, this can lead to accidental or automated destructive actions that delete family tasks or appointments with little user awareness.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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.

Content

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

md
# Mark item as bought
node ~/.openclaw/skills/cozi/scripts/cozi.js check abc123 item456

# Create a new todo list
node ~/.openclaw/skills/cozi/scripts/cozi.js new-list "Weekend chores" todo

# View this month's calendar

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest says the skill interacts with Cozi Family Organizer for shopping lists, todo lists, and item management. However, the CLI documentation and implementation add separate calendar capabilities, including viewing, creating, and deleting appointments, which extends beyond the stated list-focused scope.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script reaches outside the skill directory and reads an agent-level .env file, expanding its access to secrets beyond the local skill scope. Even though it filters for COZI_* variables, this cross-boundary secret access violates least privilege and creates a broader credential exposure path if the skill is reused in a larger agent environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The code writes bearer-session material to .session.json on disk without setting restrictive permissions or warning the user. Local users, other processes, backups, or accidental commits could expose the token and allow unauthorized access to the Cozi account until expiry.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The header comment presents the available commands as the CLI's supported interface, but it does not list the implemented 'edit' command for list items. This creates a documentation-to-code mismatch about what the tool actually allows users to modify.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The skill is described as an unofficial API client for family organization tasks, but the code persists authenticated session data to a local .session.json file. While authentication is expected for a remote API client, local session persistence is a meaningful behavior not reflected in the description.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The call to toLocaleDateString('en-US', { weekday: 'short' }) forces U.S. English weekday formatting for all users. This is a natural-language locale policy issue because the script does not offer opt-in, configuration, or any explanation that the tool is intentionally U.S.-locale-specific.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.