Back to skill

Security audit

yc

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent with its startup-program automation purpose, but it uses browser session cookies and has risky install-time side effects that warrant review before installation.

Review this carefully before installing. Only use it if you are comfortable granting an agent access to your YC browser session, allowing it to submit updates or applications on your behalf, and accepting install-time changes under ~/.claude/skills. Prefer dry runs and manual review before any submission, avoid persistent Keychain approval where possible, and back up or inspect any existing ~/.claude/skills/yc-cli path before installation.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
src/lib/cookies.ts:146
Finding
Cross-Domain Forwarding of Authentication Cookies<![CDATA[ ## Vulnerability Details **File Location**: `src/lib/cookies.ts:146-164`; authentication header construction occurs at `src/lib/client.ts:104-109` **Vulnerability Type**: Cross-domain cookie-scope violation **Risk Level**: High ### Vulnerable Code ```ts // We need cookies from both startupschool.org and ycombinator.com // (SSO cookies may be on the ycombinator.com domain) const domains = [ "https://www.startupschool.org/", "https://account.ycombinator.com/", ]; if (chromeProfile || source !== "chrome") { log(`Reading cookies from ${source}${chromeProfile ? ` (profile: ${chromeProfile})` : ""}...`); const allCookies: Record<string, string> = {}; for (const url of domains) { const result = await getCookies({ url, browsers: [source], timeoutMs: 30_000, ...(chromeProfile ? { chromeProfile } : {}), }); Object.assign(allCookies, toCookieMap(result.cookies)); } ``` The merged collection is subsequently serialized without domain filtering: ```ts private baseHeaders(): Record<string, string> { return { "User-Agent": USER_AGENT, Cookie: cookiesToString(this.cookies), }; } ``` ### Technical Analysis The cookie extraction module retrieves cookies applicable to two distinct HTTPS origins: - `www.startupschool.org` - `account.ycombinator.com` It then merges the results into a single map that preserves only cookie names and values. Domain, path, security, expiration, and SameSite metadata are discarded. `YcClient` serializes every entry in this merged map into one `Cookie` header and sends it to `www.startupschool.org`. This behavior reproduces neither browser cookie-domain isolation nor standard cookie-jar matching. A cookie returned specifically for `account.ycombinator.com` can therefore be forwarded to `www.startupschool.org`, even if a browser would not attach that cookie to the request. Cookie-name collisions create an additional correctness issue: `Object.assign` causes cookies collected ...[truncated 1666 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Preserve full cookie metadata, including domain, host-only status, path, Secure, and expiration attributes. 2. Maintain separate cookie jars for `startupschool.org` and `account.ycombinator.com`. 3. Before each request, select cookies using standard URL matching rules rather than merging by name. 4. Explicitly allowlist the minimum cookie names required by Startup School. 5. Do not forward account-origin cookies merely because they appear session-related. 6. Handle duplicate cookie names according to domain and path specificity instead of using `Object.assign`. 7. Add tests proving that an account-only cookie is never attached to a Startup School request. 8. Consider using a standards-compliant cookie-jar library instead of constructing the `Cookie` header manually. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/postinstall.js:29
Finding
Destructive Replacement of an Existing Claude Skill Directory During Installation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/postinstall.js:29-45` **Vulnerability Type**: Unconditional recursive deletion during package installation **Risk Level**: High ### Vulnerable Code ```js if (existsSync(SKILL_LINK)) { try { const stats = lstatSync(SKILL_LINK); if (stats.isSymbolicLink()) { const currentTarget = readlinkSync(SKILL_LINK); if (currentTarget === PACKAGE_ROOT) { console.log('[yc-cli] Claude Code skill already configured.'); return true; } unlinkSync(SKILL_LINK); } else { rmSync(SKILL_LINK, { recursive: true }); } } catch (err) { console.log(`[yc-cli] Warning: ${err.message}`); } } symlinkSync(PACKAGE_ROOT, SKILL_LINK); ``` ### Technical Analysis The npm `postinstall` lifecycle hook automatically claims the fixed path: ```text ~/.claude/skills/yc-cli ``` If that path already exists and is not a symbolic link, the script recursively removes it without: - Asking for user confirmation. - Verifying that this package created or owns the path. - Checking whether the directory contains user data. - Creating a backup. - Restricting removal to known package-generated files. If the path is a symbolic link pointing elsewhere, that link is also unconditionally removed. The package then creates a new symlink pointing to its own installation directory. This is not necessary for the declared CLI functionality. Automatic Skill registration could safely stop on a collision or require explicit user action. The current behavior violates least-destructive installation principles and can replace another tool or user-maintained Skill. The uninstall hook only removes a matching symlink. It cannot restore a directory or link deleted by `postinstall`. ### Attack Path 1. The user already has a directory, file, or symbolic link at `~/.claude/skills/yc-cli`. 2. The path contains a separately installed Skill, customized instructions, or other user data. 3. The user ...[truncated 1169 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Abort installation when `~/.claude/skills/yc-cli` already exists and does not point to this exact package. 2. Never recursively delete a pre-existing user path from an npm lifecycle hook. 3. Require an explicit command, such as `yc setup-skill --replace`, for replacement behavior. 4. Display the existing path and require interactive confirmation before any explicit replacement. 5. If replacement is requested, move the existing path to a timestamped backup rather than deleting it. 6. Record package ownership in a manifest and remove only artifacts demonstrably created by this package. 7. Compare canonicalized targets using `realpath` to avoid brittle raw symlink-target comparisons. 8. Make automatic Skill registration opt-in, or document a non-destructive manual symlink command. ]]>

T07 · Tool Hijacking and Spoofing

Warning
Location
scripts/postinstall.js:58
Finding
Install-Time Mutation of Credential-Handling Dependency Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/postinstall.js:58-111` **Vulnerability Type**: Third-party tool mutation through npm lifecycle hooks **Risk Level**: Medium ### Vulnerable Code ```js function patchSweetCookieTimeout() { const target = join( PACKAGE_ROOT, 'node_modules', '@steipete', 'sweet-cookie', 'dist', 'providers', 'chromeSqliteMac.js' ); if (!existsSync(target)) return; try { const content = readFileSync(target, 'utf-8'); const needle = 'timeoutMs: 3_000,'; if (!content.includes(needle)) return; const patched = content.replace(needle, 'timeoutMs: options.timeoutMs ?? 30_000,'); writeFileSync(target, patched, 'utf-8'); console.log('[yc-cli] Patched sweet-cookie keychain timeout (3s -> 30s).'); } catch (err) { console.log(`[yc-cli] Warning: could not patch sweet-cookie: ${err.message}`); } } function patchSweetCookieBigInt() { const target = join( PACKAGE_ROOT, 'node_modules', '@steipete', 'sweet-cookie', 'dist', 'providers', 'chromeSqlite', 'shared.js' ); if (!existsSync(target)) return; try { const content = readFileSync(target, 'utf-8'); const needle = 'SELECT name, value, host_key, path, expires_utc, samesite, encrypted_value,'; if (!content.includes(needle)) return; const patched = content.replace( needle, 'SELECT name, value, host_key, path, CAST(expires_utc AS TEXT) AS expires_utc, samesite, encrypted_value,' ); writeFileSync(target, patched, 'utf-8'); console.log('[yc-cli] Patched sweet-cookie BigInt overflow.'); } catch (err) { console.log(`[yc-cli] Warning: could not patch sweet-cookie BigInt: ${err.message}`); } } ``` ### Technical Analysis The package modifies the installed implementation of `@steipete/sweet-cookie` during `postinstall`. This dependency is security-sensitive because it accesses browser cookie databases and decrypts browser credentials th ...[truncated 2222 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Submit the fixes upstream and depend on a released version containing them. 2. If upstream release timing is unsuitable, maintain a clearly named fork with reviewed source and a pinned exact version. 3. Do not modify `node_modules` from package lifecycle scripts. 4. Pin security-sensitive dependencies to exact reviewed versions instead of permissive ranges. 5. Add regression tests for keychain timeout handling and SQLite expiration-value parsing. 6. If temporary patching is unavoidable, apply patches during a controlled build process and publish the resulting reviewed package under a distinct version; do not patch on end-user systems. 7. Document the effective source provenance and preserve integrity hashes for all shipped credential-handling code. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/lib/spc.ts:232
Finding
Sensitive SPC Application Screenshots Stored in Predictable Shared Temporary Files<![CDATA[ ## Vulnerability Details **File Location**: `src/lib/spc.ts:232-249` **Vulnerability Type**: Plaintext sensitive-data retention in unsafe temporary files **Risk Level**: Medium ### Vulnerable Code ```ts // Screenshot for review const screenshotPath = `/tmp/spc-form-${Date.now()}.png`; await page.screenshot({ path: screenshotPath, fullPage: true }); onStatus(`Screenshot saved: ${screenshotPath}`); if (dryRun) { onStatus("Dry run — not submitting."); return { submitted: false, screenshotPath }; } // Submit const submitBtn = page.getByRole("button", { name: /submit/i }); if (await submitBtn.count() > 0) { await submitBtn.click(); await page.waitForTimeout(3000); const afterScreenshot = `/tmp/spc-form-submitted-${Date.now()}.png`; await page.screenshot({ path: afterScreenshot, fullPage: true }); onStatus("Form submitted!"); return { submitted: true, screenshotPath: afterScreenshot }; } ``` ### Technical Analysis The SPC automation captures full-page screenshots after populating the application and again after submission. The form can contain sensitive personal and business information, including: - Founder names. - Email addresses and phone numbers. - LinkedIn profiles. - Financing history. - Accomplishments and personal decision narratives. - Startup ideas, progress, expertise, and demo links. Screenshots are written directly into the shared `/tmp` namespace using filenames derived solely from the current timestamp. The code does not: - Create a private per-user temporary directory. - Request explicit consent before persisting the image. - Apply explicit restrictive permissions. - Redact sensitive fields. - Delete the images after review. - Establish an expiration or cleanup policy. Timestamp-based names are guessable within a narrow execution window. Depending on operating-system temporary-directory semantics, local processes may be able to observe, race, or later read the generated files. Even where default permissions reduce ...[truncated 1099 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make screenshots opt-in rather than creating them for every application. 2. Clearly warn users that screenshots can contain sensitive application data. 3. Create a private temporary directory with `mkdtemp` and permissions limited to the current user. 4. Use exclusive file creation and ensure resulting files have mode `0600`. 5. Generate filenames using cryptographically random values instead of timestamps. 6. Redact or mask email addresses, phone numbers, financing information, and other sensitive fields before capture where practical. 7. Automatically delete screenshots after review or command completion unless the user explicitly requests retention. 8. Provide a user-selected output path when persistent screenshots are requested. 9. Add cleanup handling for normal completion, exceptions, and termination signals. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (40)

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
s://www.startupschool.org/) in Chrome first.
>
> Once installed, try: **"Check my YC Startup School dashboard and submit this week's update"** — the agent will pull your streak, curriculum progress, and walk you through submitting.

## Install

```bash
npm install -g @lucasygu/yc
# Or via ClawHub (OpenClaw ecosystem)
clawhub install yc
```

Requires Node.js >= 22.

- **YC Startup School**: Uses cookies from your Chrome browser session — log into [startupschool.org](https://www.startupschool.org/) in Chrome first.
- **a16z Speedrun**: No authentication needed (public API).
- **South Park Commons**: Uses Playwright (headless Chromium) to fill Airtable forms — no auth needed.

After installing, run `yc whoami` to verify the connection. If macOS shows a Keychain prompt, click "Always Allow". The CLI auto-detects all Chrome profiles to find your YC session.

## What You Can Do

- **Dashboard tracking** — Check your streak, curriculum progress, and weekly update status
- **Weekly upd
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill supports submitting founder and company application data to a16z Speedrun APIs and uploading decks to external storage. This creates privacy and data-exfiltration risk if users are not clearly informed that sensitive business information and files will be transmitted off-host to third parties.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill supports submitting founder and company application data to a16z Speedrun APIs and uploading decks to external storage. This creates privacy and data-exfiltration risk if users are not clearly informed that sensitive business information and files will be transmitted off-host to third parties.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill supports submitting founder and company application data to a16z Speedrun APIs and uploading decks to external storage. This creates privacy and data-exfiltration risk if users are not clearly informed that sensitive business information and files will be transmitted off-host to third parties.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill supports submitting founder and company application data to a16z Speedrun APIs and uploading decks to external storage. This creates privacy and data-exfiltration risk if users are not clearly informed that sensitive business information and files will be transmitted off-host to third parties.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill supports submitting founder and company application data to a16z Speedrun APIs and uploading decks to external storage. This creates privacy and data-exfiltration risk if users are not clearly informed that sensitive business information and files will be transmitted off-host to third parties.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill supports submitting founder and company application data to a16z Speedrun APIs and uploading decks to external storage. This creates privacy and data-exfiltration risk if users are not clearly informed that sensitive business information and files will be transmitted off-host to third parties.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill supports submitting founder and company application data to a16z Speedrun APIs and uploading decks to external storage. This creates privacy and data-exfiltration risk if users are not clearly informed that sensitive business information and files will be transmitted off-host to third parties.

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
tch deck PDF and get the GCS URL to include in your application.

```bash
yc speedrun upload-deck pitch.pdf
```

### Speedrun Categories
```
B2B / Enterprise Applications
Consumer Applications
Deep Tech
Gaming / Entertainment Studio
Infrastructure / Dev Tools
Healthcare
GovTech
Web3
Other
```

## Global Options

YC Startup School commands support:
- `--cookie-source <browser>` — Browser to read cookies from (chrome, safari, firefox). Default: chrome
- `--chrome-profile <name>` — Specific Chrome profile directory name
- `--json` — Output raw JSON (for scripting)

## South Park Commons Commands

SPC uses Airtable Interface forms. The CLI fills and submits via Playwright (headless Chromium).

### `yc spc info`
Show available SPC programs and their Airtable form URLs.

### `yc spc template`
Generate a JSON template for an SPC application.

```bash
yc spc template                    # Founder Fellowship (default)
yc spc template --type membership  # Community Membership
```

### `yc s
Confidence
88% confidence
Finding
The YARA hit is triggered by documented browser-cookie access, which is a credential-harvesting pattern commonly associated with infostealers. In this skill's context, the feature may be intended to reuse the user's existing authenticated session rather than to steal credentials, but it is still dangerous because it accesses high-value authentication artifacts from local browser storage.

Credential Access

High
Category
Privilege Escalation
Content
}

/**
 * Patch @steipete/sweet-cookie keychain timeout bug.
 */
function patchSweetCookieTimeout() {
  const target = join(
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
}

/**
 * Patch @steipete/sweet-cookie keychain timeout bug.
 */
function patchSweetCookieTimeout() {
  const target = join(
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
earchPrograms,
  type Program,
  type ProgramType,
} from "./lib/programs.js";

const program = new Command();

program
  .name("yc")
  .description("CLI for YC Startup School, a16z Speedrun, and South Park Commons")
  .version("0.2.0");

// --- Global options ---

function addCookieOption(cmd: Command): Command {
  return cmd
    .option(
      "--cookie-source <browser>",
      "Browser to read cookies from (chrome, safari, firefox)",
      "chrome"
    )
    .option("--chrome-profile <name>", "Chrome profile directory name");
}

function addJsonOption(cmd: Command): Command {
  return cmd.option("--json", "Output raw JSON");
}

async function getClient(cookieSource?: string, chromeProfile?: string): Promise<YcClient> {
  const source = (cookieSource || "chrome") as CookieSource;
  const cookies = await extractCookies(source, chromeProfile);
  return new YcClient(cookies);
}

function handleError(err: unknown): never {
  if (err instanceof NotAuthenticatedError) {
    console.error(k
Confidence
92% confidence
Finding
The CLI reads authentication cookies directly from local browser profiles (Chrome/Safari/Firefox) and uses them to authenticate to YC services. Accessing browser cookie stores is highly sensitive because it can expose session material and bypass normal login flows; in an agent/skill context, that meaningfully raises the risk of credential theft or unintended account access if the helper library is overly broad, compromised, or reused beyond the stated purpose.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'network' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
#!/usr/bin/env node
/**
 * Cookie extraction module — wraps @steipete/sweet-cookie
 * Extracts YC session cookies from Chrome/Safari/Firefox
 */

import { getCookies } from "@steipete/sweet-cookie";
import { readFileSync, existsSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import kleur from "kleur";

export interface YcCookies {
  [key: string]: string;
}

export type CookieSource = "chrome" | "safari" | "firefox";

interface ChromeProfileInfo {
  dirName: string;
  displayName: string;
}

/**
 * Discover Chrome profiles from Local State file.
 */
function discoverChromeProfiles(): ChromeProfileInfo[] {
  if (process.platform !== "darwin") return [];
Confidence
88% confidence
Finding
The information-stealer signature is substantively supported here because the code targets browser cookies, enumerates profiles, and reconstructs session headers that can be used to impersonate the user. In context, this may be for legitimate automation against YC-related sites, but the capability is still credential access with account-takeover potential if abused or compromised.

Credential Access

High
Category
Privilege Escalation
Content
`  - Cookie source: ${source}`,
    "",
    "Troubleshooting:",
    "  1. Keychain access: when macOS prompts for your password, click 'Always Allow'",
    "     to avoid being asked again.",
    "  2. Login: open Chrome and visit https://www.startupschool.org/ — make sure you",
    "     are logged in and can see your dashboard.",
Confidence
90% confidence
Finding
The troubleshooting text instructs users to click 'Always Allow' for macOS Keychain prompts, encouraging persistent access to browser-stored secrets. That expands the blast radius beyond a one-time action by making future secret extraction easier and less visible, which is dangerous for any tool handling session cookies.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
This file implements active submission capabilities for emails, full applications, video metadata, and arbitrary file uploads to remote endpoints, while the skill metadata describes discovery, dashboard, and deadline tracking. That scope expansion is dangerous because it enables collection and transmission of highly sensitive founder, employee, investor, and fundraising data to a third party, creating a material risk of unexpected exfiltration or misuse if users invoke the skill assuming it is read-only or informational.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The getting-started guidance tells users to install the tool and let the agent handle connection and cookie issues, then suggests submitting weekly updates, but it does not prominently warn that the tool can act using browser-derived authenticated sessions. Users may not realize the agent can access account context and initiate submissions under their identity.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The README explicitly encourages broad natural-language requests such as checking dashboards and submitting updates, but it does not define clear consent or confirmation boundaries before state-changing actions occur. In an agent-integrated environment, this can cause an AI assistant to over-trigger the skill and perform unintended authenticated actions on the user's behalf.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documented AI-agent workflows describe generating application JSON, filling it with startup details, validating, and then submitting to third-party services, but they do not strongly warn that sensitive personal, company, and application data will be transmitted externally. This creates a meaningful privacy and consent risk, especially when an agent is orchestrating the workflow end-to-end.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The AI agent integration section invites open-ended requests like 'Fill out an SPC Founder Fellowship application for me' and says the agent will automatically generate, fill, validate, and submit. That broad delegation increases the risk of unintended submissions, privacy mistakes, or actions taken without sufficiently granular user consent.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The workflow instructions encourage direct application submission through generated JSON and CLI automation but do not prominently warn that personal, founder, company, and attachment data will be transmitted to external services. In an agent context, that omission can cause users to authorize sensitive submissions without understanding the privacy and irreversibility implications.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill describes reading browser cookies from Chrome, Safari, or Firefox without an explicit high-visibility warning that this accesses sensitive authentication material. Session cookies can enable direct authenticated access to user accounts, so silently or casually normalizing cookie extraction materially increases the danger in a tool-execution context.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The package defines postinstall and preuninstall lifecycle hooks that automatically execute local Node.js scripts during install/removal. For a CLI whose stated purpose is startup-program discovery, this adds implicit code execution at package-management time, which expands the attack surface and could run with the user's privileges without an explicit CLI invocation.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The post-install script performs non-obvious side effects during package installation: it modifies the user's Claude skill configuration and rewrites vendored dependency source files under node_modules. Even if intended for convenience and compatibility, this behavior exceeds what users would reasonably expect from a CLI for startup-program discovery and creates supply-chain risk because install-time code runs automatically with the user's privileges.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The script automatically creates or replaces ~/.claude/skills/yc-cli with a symlink to the package, altering Claude Code's local trust/configuration surface during npm install. This is risky because it persists package-controlled content into an execution path for another tool without explicit user consent, and it may delete or replace an existing path at that location.

Static analysis

No suspicious patterns detected.