SSWConsulting / SSWConsulting/SSW.MeetupMCP
Installers persist configuration to shell history and process command lines
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Both installers take every configuration value as a command-line parameter, which writes them to several places outside the operator's control. The Claude installer tries to avoid this and the mitigation does not hold.
To be clear about scope: the RSA private key never goes near either script. What leaks is the client ID, member ID, signing key ID and the key's filesystem path. Not credential material, but enough to tell someone exactly which OAuth client and account to target, and where the key lives on disk.
Where the values end up
- PSReadLine console history, at
%APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt. Plaintext, no expiry, and it roams with OneDrive Known Folder Move or a roaming profile. PSReadLine does scrub sensitive lines, but its default filter is the regexpassword|asplaintext|token|apikey|secret, and none of-ClientId,-MemberId,-SigningKeyIdor-PrivateKeyPathmatch it. The invocation documented indocs/setup.mdis written to that file verbatim. - Child process command lines, readable by any same-user process via
Get-CimInstance Win32_Process, and captured by Windows Security Event 4688 where command-line auditing is enabled, by Sysmon Event 1, and by most EDR agents. On a managed build those records ship to a SIEM and outlive the machine. - PowerShell transcription, if enabled by Group Policy.
- The MCP client config at rest, in
~/.codex/config.tomlor~/.claude.json. This one is inherent to how stdio MCP servers receive environment variables and is not avoidable.
The Claude installer's mitigation does not work
scripts/install-claude.ps1 deliberately keeps the values off the PowerShell command line by stashing the config in $env:CLAUDE_MCP_SERVER_JSON and shelling out to a Node helper. But scripts/register-claude-mcp.mjs:11 then passes that same JSON straight back as an argv element to spawnSync.
Verified by running the real helper against a stub claude binary:
argv[6] = {"type":"stdio",...,"env":{"MEETUP_CLIENT_ID":"REAL_CLIENT_ID","MEETUP_MEMBER_ID":"REAL_MEMBER_ID","MEETUP_SIGNING_KEY_ID":"REAL_KEY_ID","MEETUP_PRIVATE_KEY_PATH":"C:\secure\meetup\key.pem",...}}
Same data, same exposure class, one process further down. scripts/install-codex.ps1:24 has the same problem more directly, via seven --env "NAME=value" arguments.
Suggested fix
Have the installer read configuration from a .env file rather than from parameters. .env.example is already in the right shape and .env is already gitignored, so this removes items 1 through 3 entirely with no new concepts for the operator.
Once configuration comes from a file, the installer has no reason to be PowerShell. A single node scripts/install.mjs would work on macOS, Linux and Windows, and both .ps1 files plus the Node helper could be deleted.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with scripts/install-claude.ps1, scripts/install-codex.ps1, and scripts/register-claude-mcp.mjs, then compare their configuration handling with .env.example and the documented setup in docs/setup.md. Confirm the replacement reads configuration from .env, avoids putting values in PowerShell or child-process arguments, and provides a cross-platform node scripts/install.mjs entry point.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, powershell, typescript
- Domain
- security, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100