Skip to content

Feature: credential profiles for secrets manager integration #159

@codyfrisch

Description

@codyfrisch

Problem

The CLI stores access tokens as plaintext in .mappsrc, a known dotfile path. This creates two concerns:

  1. Supply chain risk -- malicious npm packages can trivially scrape plaintext tokens from known dotfile paths (as demonstrated by recent supply chain attacks targeting developer machines)
  2. No multi-account support -- developers working with multiple environments (dev/prod/staging) must re-run mapps init to switch tokens

Proposal

Add named credential profiles to .mappsrc. Each profile maps a name to a shell command that prints an access token to stdout at runtime:

{
  "profiles": {
    "dev": "op read 'op://CarbonWeb/dev/credential'",
    "prod": "aws ssm get-parameter --name /app/token --query Parameter.Value --output text"
  },
  "defaultProfile": "dev"
}

Supported providers (examples)

Provider Example command
1Password op read 'op://vault/item/field'
Bitwarden bw get password item-id
HashiCorp Vault vault kv get -field=token secret/myapp
AWS SSM aws ssm get-parameter --name /path --query Parameter.Value --output text
macOS Keychain security find-generic-password -s mapps -a dev -w
Named plaintext echo my-token-here

New CLI surface

# Interactive management
mapps profile

# Subcommands
mapps profile:add --name dev --command "op read op://vault/dev/token" --set-as-default
mapps profile:remove --name dev
mapps profile:list
mapps profile:set-default --name dev
mapps profile:clear-default
mapps profile:remove-token    # Remove legacy plaintext accessToken

# Runtime override on any command
mapps code:push --profile prod
mapps code:push --ignore-profiles

Token resolution precedence

  1. --profile flag -- runs the named profile command
  2. defaultProfile in config -- runs that profile command
  3. No profile specified, no default set -- falls through silently to static token
  4. accessToken in .mappsrc -- plaintext fallback

Error handling

  • Profile command failure -- aborts with error, suggests --ignore-profiles
  • Empty output -- aborts with error
  • Profile name not found -- aborts with error listing available profiles

Security

Profile commands execute arbitrary shell code — this is by design, as it's how they delegate to secrets managers. To prevent a malicious repository from injecting attacker-controlled commands via a committed .mappsrc, profiles are only loaded from the global config. If a local project .mappsrc contains profiles, they are ignored with a warning.

Backwards compatibility

Fully backwards compatible. Existing .mappsrc files with only accessToken continue to work unchanged. Profiles are opt-in. The goal is not to remove the plaintext option, but to ensure developers have a secure alternative available so that storing credentials on disk becomes an informed choice rather than the only path offered by the tool.

Prior art

  • pnpm ships tokenHelper -- same "run a command, use stdout as token" pattern
  • npm has an open feature request for the same capability
  • AWS CLI uses named profiles for credential management
  • Docker uses credential helpers for registry authentication

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions