Back to skill

Security audit

birdx

Security checks for vulnerabilities and agentic risk

Overview

This Twitter/X CLI is purpose-aligned, but it handles browser session cookies unsafely and has a command-execution flaw, so users should review it carefully before installing.

Install only if you are comfortable with a local CLI reading and decrypting your Chrome X/Twitter session cookies, storing them in plaintext under your home directory, and installing live npm dependencies. Prefer a version that uses an explicit OAuth-style flow or OS-backed secret storage, validates usernames before shell use, pins dependencies, and provides a clear cache deletion and session revocation path.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/birdx.js:613
Finding

Shell Command Injection Through an Unvalidated Username

Content
View full analysis
/dev/null`, { timeout: 15000 }).toString().trim(); const data = JSON.parse(out); ``` ### Technical Analysis The `username` value originates from a positional command-line argument. Removing an optional leading `@` does not validate or escape the value. The resulting `handle` is interpolated directly into a command string passed to `execSync()`. Because the string form of `execSync()` invokes a shell, shell metacharacters contained in the username can introduce additional commands, pipelines, substitutions, or redirections. The vulnerable path is reachable through both the `followers` and `following` commands. The external command also redirects standard error through shell syntax, confirming that the command is deliberately interpreted by a shell rather than executed as a fixed executable with an argument array. ### Attack Path 1. An attacker persuades a user, automation agent, or service to invoke `birdx followers` or `birdx following` with an attacker-controlled username. 2. CLI parsing accepts the positional value without validating it as an X username. 3. `cmdFollowers()` or `cmdFollowing()` passes the value to `resolveUsernameToId()`. 4. `resolveUsernameToId()` interpolates the value into the `bird user-tweets ...` shell command. 5. Shell metacharacters in the supplied value alter the command structure. 6. The injected command executes with the privileges and environment of the user running `birdx`. For example, a value structurally equivalent to `validname; additional-command` would cause the shell to interpret the text following the semicolon as another command. ### Impact Assessment Successful ...[truncated 629 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/birdx.js:73
Finding

Excessive Browser Cookie Extraction and Plaintext Session Credential Storage

Content
View full analysis
0) { val = decryptChromeCookie(encBuf, key, stripHashPrefix) || ''; } if (val) cookiePairs.push(`${row.name}=${val}`); } const cookieStr = cookiePairs.join('; '); return { cookieStr, ct0, authToken, source: 'chrome-disk' }; ``` CDP cookie collection: ```js const ws = new WebSocket(anyPage.webSocketDebuggerUrl); const timer = setTimeout(() => { ws.close(); reject(new Error('CDP timeout')); }, 10000); ws.on('open', () => { ws.send(JSON.stringify({ id: 1, method: 'Network.enable' })); ws.send(JSON.stringify({ id: 2, method: 'Network.getAllCookies' })); }); ws.on('message', (data) => { const msg = JSON.parse(data.toString()); if (msg.id === 2) { clearTimeout(timer); ws.close(); const xCookies = (msg.result?.cookies || []) .filter(c => c.domain.includes('x.com') || c.domain.includes('twitter.com')); const cookieStr = xCookies.map(c => `${c.name}=${c.value}`).join('; '); cons ...[truncated 3491 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/install.sh:28
Finding

Unpinned and Integrity-Unverified npm Dependencies

Content
View full analysis
package.json npm install --prefix "$HOME/clawd" ws jsdom x-client-transaction-id --save-exact --silent echo "✅ Deps installed to $MODULES_DIR" ``` ### Technical Analysis The installer requests three packages without specifying reviewed version numbers and the project contains no reviewed lockfile in the supplied directory. The `--save-exact` option records the versions selected during that particular installation, but it does not pin versions in advance or guarantee that different users receive the same audited dependency graph. The installation also does not use `npm ci`, a committed lockfile, or independent integrity verification. npm package lifecycle scripts are not disabled. Consequently, package or transitive-dependency code selected at installation time may execute installation scripts and is later imported into the same Node.js process that handles decrypted browser credentials. This creates a significant trust-boundary concern because `jsdom`, `ws`, and `x-client-transaction-id`, along with their transitive dependencies, run with the user's filesystem and network privileges. ### Attack Path 1. A user follows `SKILL.md` or runs `scripts/install.sh`. 2. `npm install` resolves the current registry versions of the named packages and their transitive dependencies. 3. A compromised package release, compromised maintainer account, registry substitution, or malicious registry configuration supplies unexpected code. 4. npm lifecycle code may execute during installation, or the package code executes when loaded by `birdx.js`. 5. The dependency runs with the privileges of the ...[truncated 786 chars]
Remediation
View remediation
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 (15)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The markdown describes reading Chrome cookies from disk and caching them locally, but does not present an explicit privacy or safety warning about the sensitivity of session cookies and the risks of local persistence. Session cookies can enable account access or impersonation if mishandled, and local caches increase exposure to theft, reuse, or accidental disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The tool writes decrypted X/Twitter session cookies to a predictable file under the user's home directory, creating a reusable local credential cache. If local filesystem protections are weak, backups are exposed, or another process/user can read the file, the session may be hijacked.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
96% confidence
Finding

The section documents deriving a decryption key from the macOS Keychain for Chrome cookie decryption, which is a credential-access pattern commonly used to obtain reusable browser secrets. In this skill's context, that directly supports extraction of authenticated session material for another service.

Content

Scanner excerpt · scripts/birdx.js (reported line 90)May include surrounding context.

js
// ── Chrome direct disk reader (no browser needed) ─────────────────────────────

/**
 * Derives the AES key from macOS Keychain (bird's A$ function).
 * Uses raw password string bytes (NOT base64-decoded) with PBKDF2.
 */
function deriveChromeCookieKey(password) {

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code explicitly retrieves the Chrome Safe Storage secret from the macOS Keychain, decrypts Chrome's cookie database, extracts X/Twitter session cookies, and persists them to disk for later reuse. That is credential harvesting behavior beyond a normal read-only Twitter CLI and creates reusable session material that could enable account access if the local cache is exposed or abused.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code accesses the macOS Keychain to derive Chrome's cookie decryption key and then reads browser-stored credentials without a clear upfront warning or consent flow tailored to this sensitive operation. Accessing OS credential storage and decrypting browser secrets is highly sensitive and inconsistent with least surprise for a data-reading CLI.

Content

No source excerpt is available for this finding.

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

High
Category
YARA Match
Confidence
99% confidence
Finding

The YARA match is justified: the code contains multiple information-stealer characteristics, including obtaining a browser decryption key from Keychain, reading Chrome's cookies database, decrypting session cookies, and caching them for later use. Even if framed as convenience functionality, this behavior materially resembles credential-harvesting tooling.

Content

Scanner excerpt · scripts/birdx.js (reported line 130)May include surrounding context.

js
p 32-byte random prefix if version >= 24 (bird's stripHashPrefix)
    if (stripHashPrefix && dec.length >= 32) dec = dec.slice(32);
    return dec.toString('utf8').replace(/^[\x00-\x1f]+/, '');
  } catch {
    return null;
  }
}

/**
 * Reads auth_token and ct0 directly from Chrome's SQLite cookie database.
 * No browser required — works while Chrome is running or closed.
 */
async function loadCookiesFromChrome() {
  // 1. Get Chrome Safe Storage password from macOS Keychain
  let pw;
  try {
    pw = execSync('security find-generic-password -w -s "Chrome Safe Storage" -a "Chrome"',
      { encoding: 'utf8', timeout: 5000 }).trim();
  } catch (e) {
    throw new Error(`Failed to read Chrome Safe Storage from Keychain: ${e.message}`);
  }
  if (!pw) throw new Error('Chrome Safe Storage password is empty');

  const key = deriveChromeCookieKey(pw);

  // 2. Locate and copy the Cookies database
  const chromeCookieDb = path.join(
    process.env.HOME,
    'Library/Application Support/G

Credential Access

High
Category
Privilege Escalation
Confidence
99% confidence
Finding

This code executes the security tool to read the Chrome Safe Storage password from the macOS Keychain, enabling decryption of browser-stored cookies. That is direct credential access to OS-protected secrets and can facilitate unauthorized reuse of web sessions.

Content

Scanner excerpt · scripts/birdx.js (reported line 131)May include surrounding context.

js
* No browser required — works while Chrome is running or closed.
 */
async function loadCookiesFromChrome() {
  // 1. Get Chrome Safe Storage password from macOS Keychain
  let pw;
  try {
    pw = execSync('security find-generic-password -w -s "Chrome Safe Storage" -a "Chrome"',

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

This line is an error message referencing Keychain access failure, not additional credential-access behavior by itself. The real vulnerability is the surrounding logic that attempts the Keychain read, which is already captured elsewhere.

Content

Scanner excerpt · scripts/birdx.js (reported line 137)May include surrounding context.

js
pw = execSync('security find-generic-password -w -s "Chrome Safe Storage" -a "Chrome"',
      { encoding: 'utf8', timeout: 5000 }).trim();
  } catch (e) {
    throw new Error(`Failed to read Chrome Safe Storage from Keychain: ${e.message}`);
  }
  if (!pw) throw new Error('Chrome Safe Storage password is empty');

Credential Access

High
Category
Privilege Escalation
Confidence
99% confidence
Finding

The code targets Chrome's on-disk cookies database in the user's profile, a classic browser credential store containing authenticated session material. Reading that database to recover auth_token and ct0 enables reuse of the user's X/Twitter session outside the browser.

Content

Scanner excerpt · scripts/birdx.js (reported line 146)May include surrounding context.

js
// 2. Locate and copy the Cookies database
  const chromeCookieDb = path.join(
    process.env.HOME,
    'Library/Application Support/Google/Chrome/Default/Cookies'
  );
  if (!fs.existsSync(chromeCookieDb)) {
    throw new Error(`Chrome Cookies DB not found at ${chromeCookieDb}`);

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill exposes capabilities that imply shell execution, network access, and environment interaction, but the manifest does not declare any tool scope or permission boundaries. In a skill that installs packages, symlinks binaries, reads browser cookies from disk, and performs live requests to X/Twitter, this omission prevents informed consent and weakens sandboxing and policy enforcement.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The description encourages broad use ('Use when you need to fetch Twitter/X data...') without clearly constraining when the skill should be invoked or warning that it reads Chrome cookies from disk. That increases the chance an agent will invoke it in situations involving unrelated user data or without the user's informed approval for credential-adjacent access.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The header comment repeatedly states that no browser is required and that the tool works by reading Chrome cookies directly from disk. However, the code later implements fetchCookiesViaCDP() and uses it as a fallback authentication path, which depends on a running browser exposing a debugging endpoint.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

When cached or disk cookies are unavailable, the tool silently falls back to extracting cookies from a live browser via CDP, expanding the credential-access surface without a strong, explicit opt-in at the time of use. This makes the behavior less transparent and can unexpectedly pull authenticated browser session data from a running browser context.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code shells out to an external bird command using a username-derived value, which introduces unnecessary command-execution risk and a hidden dependency unrelated to the stated core purpose. Although the handle is somewhat constrained, invoking a shell for simple user resolution increases attack surface and can be abused if input validation or execution context changes.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The date formatter unconditionally calls toLocaleString with zh-CN and Asia/Shanghai, overriding the user's system locale and regional settings. This is a natural-language/locale policy concern because it imposes a specific language/locale presentation without offering a choice or documenting a justified regional restriction.

Content

No source excerpt is available for this finding.