T05 · Unauthorized Access and Privilege Escalation
Warning
- Location
- scripts/linkedin.mjs:102
- Finding
- Legacy LinkedIn Session Secret Is Unnecessarily Read into Process Memory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/linkedin.mjs:102-114` and `scripts/linkedin.mjs:189-190` **Vulnerability Type**: Excessive credential access and sensitive data exposure **Risk Level**: Medium ### Vulnerable Code ```js function keychainGet() { try { const argv = KEYCHAIN_BIN === 'security' ? ['find-generic-password', '-s', KEYRING_SERVICE, '-a', KEYRING_ACCOUNT, '-w'] : ['lookup', 'service', KEYRING_SERVICE, 'account', KEYRING_ACCOUNT]; const v = execFileSync(KEYCHAIN_BIN, argv, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], }); const trimmed = String(v || '').replace(/\n$/, ''); return trimmed || null; } catch { return null; } } ``` ```js function legacyCredsPresent() { return Boolean(keychainGet()) || existsSync(TOKEN_FILE); } ``` ### Technical Analysis The `session status` operation only needs to determine whether a legacy keychain entry exists. However, `legacyCredsPresent()` calls `keychainGet()`, which retrieves the complete stored credential. On macOS, the `security find-generic-password` command uses the `-w` option, which prints the password. On Linux, `secret-tool lookup` similarly writes the matching secret to standard output. The process captures that output through `execFileSync`, creating JavaScript strings containing the password-equivalent LinkedIn session credential. This behavior exceeds the minimum privileges necessary for a presence check. It also contradicts the Skill documentation stating that no credential value is read into the process and that the keychain functionality is limited to detecting and deleting legacy entries. The value is not directly printed or transmitted by the reviewed code. Nevertheless, unnecessary retrieval expands the credential's exposure surface to Node.js process memory, runtime instrumentation, debuggers, crash collection, malicious preload hooks, and future accidental logging. ### Attack Path 1. ...[truncated 1789 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace secret retrieval with a metadata-only keychain existence check. - On macOS, invoke `security find-generic-password` without `-w` and use only the process exit status. - Do not pipe or capture password output. 2. On Linux, use a keyring interface that can query item metadata or existence without returning the secret. 3. If the available Linux command cannot check existence without disclosing the value, remove the keychain-presence indicator from `session status`. Preserve fixed-argument deletion only during the explicit `session logout` operation. 4. Separate the APIs by intent: - `keychainExists()` must never return or capture credential contents. - `keychainDelete()` should remain deletion-only. - No general-purpose credential getter should exist in this version. 5. Add platform-specific tests with mocked `security` and `secret-tool` binaries. The tests should verify that: - macOS arguments never include `-w`. - Status checks do not emit or capture a sentinel secret. - Logout still deletes the fixed legacy keychain entry. 6. Update documentation only after implementation and tests confirm that no legacy credential value enters process memory. ]]>
