mcpField
G
gpc-security

gpc-security

github.com/yasserstudioSecurity141 installsUpdated 1w ago
Repository

About gpc-security

gpc-security is a security skill for GPC users, covering credential protection, rotation, audit logs, and CI/CD practices. It also provides guidance for compromised-key response, least privilege, and sensitive-data redaction.

Key features

  1. 01Explains credential storage locations
  2. 02Guides service account key rotation
  3. 03Reviews JSON Lines audit logs
  4. 04Advises on CI/CD secret handling
  5. 05Outlines compromised-key response
  6. 06Covers least privilege and data redaction

Use cases

  1. 01Configure service account credentials safely in CI/CD
  2. 02Rotate a service account key
  3. 03Review GPC command audit records
  4. 04Respond to a compromised key

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/yasserstudio/gpc-skills --skill gpc-security

After installing, type/gpc-securityin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

evals/
evals.json
references/
credential-storage.md
key-rotation.md
runtime-security.md
scripts/
detect_gpc.mjs

Details

From the project README

gpc-security

Credential management, audit logging, and security best practices for GPC.

When to use

  • Securing service account keys and credentials
  • Setting up credential rotation
  • Reviewing audit logs for compliance
  • Handling a compromised service account key
  • Securing GPC in CI/CD pipelines
  • Understanding where GPC stores sensitive data

Inputs required

  • GPC installed and authenticated — gpc auth status
  • Service account key files — for rotation procedures
  • CI/CD platform access — for updating secrets

Procedure

0. Credential storage locations

GPC stores credentials in platform-appropriate secure locations:

DataLocationSecurity
OAuth tokensOS keychain (macOS/Linux/Windows)OS-managed encryption
Token cache~/.cache/gpc/tokens/File permissions (0600)
User config~/.config/gpc/config.jsonFile permissions
Project config.gpcrc.jsonVersion-controlled (no secrets!)
Audit log~/.config/gpc/audit.logJSON Lines, append-only

XDG overrides: XDG_CONFIG_HOME, XDG_CACHE_HOME, XDG_DATA_HOME

Read: references/credential-storage.md for detailed storage architecture and security model.

1. Service account key security
Never commit keys to git
# .gitignore
*.json.key
*-sa.json
service-account*.json
play-store-key.json
Use environment variables in CI
# GitHub Actions — key stored as secret
env:
  GPC_SERVICE_ACCOUNT: ${{ secrets.PLAY_SA_KEY }}

Never store keys in:

  • .gpcrc.json (version-controlled)
  • Dockerfiles or docker-compose files
  • Shell scripts committed to git
  • CI config files (even if they seem private)
2. Key rotation

Rotate service account keys periodically (recommended: every 90 days).

# 1. Create new key in Google Cloud Console
# IAM & Admin → Service Accounts → Keys → Add Key

# 2. Test new key locally
gpc auth login --service-account /path/to/new-key.json
gpc doctor

# 3. Update CI secrets with new key
# GitHub: Settings → Secrets → PLAY_SA_KEY → Update
# GitLab: Settings → CI/CD → Variables → PLAY_SA_KEY → Update

# 4. Verify CI works with new key
# Trigger a test pipeline

# 5. Delete old key in Google Cloud Console
# IAM & Admin → Service Accounts → Keys → Delete old key

# 6. Clear local token cache
rm -rf ~/.cache/gpc/tokens/

Read: references/key-rotation.md for automated rotation patterns and multi-environment strategies.

3. Audit logging

GPC logs all commands to ~/.config/gpc/audit.log in JSON Lines format:

# View recent audit entries
tail -20 ~/.config/gpc/audit.log | jq .

# Filter by command
cat ~/.config/gpc/audit.log | jq 'select(.command == "releases upload")'

# Filter by app
cat ~/.config/gpc/audit.log | jq 'select(.app == "com.example.app")'

# Filter failures
cat ~/.config/gpc/audit.log | jq 'select(.success == false)'

# Filter by date range
cat ~/.config/gpc/audit.log | jq 'select(.timestamp >= "2025-03-01")'
Audit entry structure
{
  "timestamp": "2025-03-09T14:30:00.000Z",
  "command": "releases upload",
  "app": "com.example.app",
  "args": { "track": "beta", "file": "app-release.aab" },
  "user": "[email protected]",
  "success": true,
  "durationMs": 12340
}
4. Secrets redaction

GPC automatically redacts sensitive data in all output:

  • Service account JSON content is never logged
  • Access tokens are never shown in verbose output
  • Private keys are never included in error messages
  • --json output redacts credential fields
  • Webhook payloads are redacted via redactSensitive() before dispatch to Slack/Discord/custom endpoints (v0.9.80+)
  • Auth error messages redact long inputs that look like pasted credentials (v0.9.80+)
  • The --pending-refund-token passed to gpc purchases orders review-refund is kept out of --notify webhook messages and plugin hook data (v1.0.0-rc.1+)
  • ADC token cache uses hash-based keys per credential source to prevent multi-account confusion (v0.9.80+)
  • Project .gpcrc.json cannot self-approve plugins -- approvedPlugins is only trusted from user config (v0.9.80+)
5. Least-privilege permissions

Grant only the permissions each service account needs:

Upload-only service account

Play Console permissions:

  • View app information
  • Manage testing (for internal/alpha/beta)
  • Release to production (only if needed)
Read-only monitoring service account

Play Console permissions:

  • View app information
  • View financial data (for reports)
# Verify what a service account can do
gpc auth status --json | jq '.email'
# Then check that email's permissions in Play Console
6. Handling compromised keys

If a service account key is leaked:

# 1. IMMEDIATELY delete the compromised key in Google Cloud Console
# IAM & Admin → Service Accounts → Keys → Delete

# 2. Create a new key
# Same page → Add Key → JSON

# 3. Update all locations using the key
gpc auth login --service-account /path/to/new-key.json

# 4. Update CI secrets
# All platforms using the old key

# 5. Clear token cache
rm -rf ~/.cache/gpc/tokens/

# 6. Review audit log for unauthorized actions
cat ~/.config/gpc/audit.log | jq 'select(.timestamp >= "LEAK_DATE")'

# 7. Review Google Cloud audit logs
# Cloud Console → IAM & Admin → Audit Logs
7. CI/CD security patterns
GitHub Actions
# npm publish: OIDC via Trusted Publisher (v0.9.77+, no stored NPM_TOKEN)
permissions:
  id-token: write  # Required for OIDC token exchange with npm

# Google Play service account: encrypted secret
env:
  GPC_SERVICE_ACCOUNT: ${{ secrets.PLAY_SA_KEY }}

# Restrict to specific branches
if: github.ref == 'refs/heads/main'

# Use environments for approval gates (staged publishing uses this)
environment: production
Secret scanning
# Check if keys are in git history
git log --all --full-history -p -- '*.json' | grep -l '"private_key"'

# If found, rotate immediately and clean git history

Verification

  • gpc auth status shows the expected service account email
  • gpc doctor passes all checks
  • .gpcrc.json contains no secrets or key paths
  • Audit log at ~/.config/gpc/audit.log is being written
  • CI secrets are encrypted and not visible in logs
  • Old keys are deleted after rotation

Failure modes / debugging

SymptomLikely CauseFix
Key file committed to gitNot in .gitignoreAdd to .gitignore; rotate key immediately
Token cache stale after rotationOld cached tokensDelete ~/.cache/gpc/tokens/
Audit log not writingConfig dir not writableCheck permissions on ~/.config/gpc/
Service account email unknownKey not inspectedgpc auth status --json | jq '.email'
CI shows credential in logsKey passed as argumentUse environment variables, never CLI args
Keychain prompt every commandmacOS keychain access not grantedClick "Always Allow" on the prompt
8. Supply chain protection (v0.9.77+)

GPC uses 15 layers of defense against dependency and publish supply chain attacks:

LayerWhat it does
Trusted Publisher (OIDC)npm publish authenticates via GitHub OIDC -- no long-lived NPM_TOKEN stored anywhere
Staged PublishingCI stages packages; maintainer approves with 2FA before they go live on npm
NPM_TOKEN deletedNo stored npm token in GitHub secrets -- OIDC is the only auth path
min-release-age=7 in .npmrcBlocks packages published less than 7 days ago
pnpm-lock.yamlExact version pinning, no unexpected upgrades
Socket.dev CI scansocket ci on every PR, blocks on critical alerts
Socket.dev GitHub AppInline PR comments on risky dependency changes
pnpm audit in CIGates PRs on high-severity CVEs (production deps)
GitHub Actions SHA pinsAll 14 action refs pinned to commit hashes, not mutable tags
SBOM (CycloneDX)Bill of materials generated and archived on every npm release
CODEOWNERSSecurity-sensitive paths require explicit review
DependabotWeekly update PRs (direct dependencies only, actions grouped)
Socket CLI wrapperScans every local npm install and npx
CodeQLStatic analysis on every push
GitHub secret scanningBlocks pushes containing 200+ secret patterns

GPC only has 4 runtime dependencies: google-auth-library, commander, protobufjs, yauzl. All API calls use Node.js built-in fetch.

Configuration: socket.yml at repo root controls Socket.dev alert rules. .npmrc controls min-release-age. .github/CODEOWNERS controls review requirements. Release workflow uses pnpm release-staged with OIDC authentication.

9. Security audit posture (v0.9.80 + v0.9.82)

v0.9.80 deepsec re-scan: A full-codebase deepsec audit was run after the v0.9.80 security fixes. Result: 0 new findings. All previously tracked findings from the v0.9.74 audit were resolved.

Webhook redaction (v0.9.80): Webhook payloads dispatched to Slack, Discord, and custom endpoints via --webhook-url are now redacted before dispatch. Sensitive fields are stripped from the payload before it leaves the process. This applies to all gpc watch breach events and any other webhook dispatch paths.

google-auth-library bump (v0.9.82): google-auth-library was upgraded to 10.7.0. This clears the only remaining tracked production audit finding: a brace-expansion transitive vulnerability. GPC now has zero production audit findings.

9a. GPC GitHub Action security

The GPC GitHub Action (yasserstudio/gpc-action) is a TypeScript action running on Node 24 with these security properties:

  • Verified binary: it downloads the standalone gpc binary from the GPC GitHub release and checks its SHA-256 on every run (cache hits included) against checksums committed in the action itself, not only the release's mutable checksums.txt. The default GPC version is pinned: 1.0.0-rc.2 as of action v1.1.4. gpc-version: latest or an unpinned version falls back to the release checksums with a warning.
  • Credentials: authentication uses the service account JSON key passed in service-account-json. Store it as an encrypted secret; there is no OIDC or keyless option yet.
  • Built-in preflight gate: the action runs gpc preflight before upload, and a failing scan blocks the publish (preflight: false skips it).
  • Node 24 runtime: matches the current GPC CLI CI matrix.

Usage:

- uses: yasserstudio/gpc-action@v1
  with:
    service-account-json: ${{ secrets.GPC_SERVICE_ACCOUNT }}
    package-name: com.example.app
    release-files: app/build/outputs/bundle/release/app-release.aab
    track: beta
10. Developer verification

Google's Android developer verification enforcement begins September 2026 (BR, ID, SG, TH):

gpc verify              # Status, deadlines, resources
gpc verify --open       # Open verification page in browser
gpc verify --json       # Machine-readable output

gpc verify checklist walks the full readiness list (11 items as of v0.9.96, every one of them promptable) -- see gpc-setup. gpc doctor includes a verification check. gpc status shows a footer reminder. gpc preflight shows a post-scan reminder.

11. Signing key verification (v0.9.75+)

Verify your local signing key matches the Play signing certificate:

gpc doctor --verify                                              # Show Play cert fingerprint
gpc doctor --verify --keystore release.keystore --store-pass $PW  # Compare local vs Play

If fingerprints don't match, you're distributing with a different key than Play uses. Register it in Play Console to avoid installation blocks after September 30, 2026.

12. Play App Signing with a self-hosted Cloud KMS key (v0.9.96+)

gpc app-signing enroll and gpc app-signing rotate cover the only Play App Signing path Google exposes through the API: enrollment with a self-hosted Google Cloud KMS key, and rotation of that key.

This is not the normal path. Standard Play App Signing with a Google-generated or Google-managed key cannot be set up through the API at all -- do it in Play Console. Reach for these commands only when a compliance, regulatory, or policy requirement forces you to keep key custody in your own Cloud KMS instance.

# Enroll an app already published to Open testing or Production
gpc app-signing enroll \
  --app com.example.app \
  --existing-app \
  --kms-key projects/acme/locations/global/keyRings/play/cryptoKeys/signing/cryptoKeyVersions/1

# Enroll an app not yet published (Google requires the certificate here)
gpc app-signing enroll \
  --app com.example.app \
  --new-app \
  --kms-key projects/acme/locations/global/keyRings/play/cryptoKeys/signing/cryptoKeyVersions/1 \
  --cert ./signing-cert.pem \
  --upload-cert ./upload-cert.pem

# Rotate to a new Cloud KMS key
gpc app-signing rotate \
  --app com.example.app \
  --kms-key projects/acme/locations/global/keyRings/play/cryptoKeys/signing/cryptoKeyVersions/2 \
  --cert ./new-signing-cert.pem \
  --lineage ./lineage.bin \
  --reason ROUTINE_KEY_UPGRADE

Security properties to know before running either:

  • --cert and --upload-cert take PEM certificates only. A private key handed to any file flag is a usage error (exit 2) and nothing is sent. Private keys never leave your machine, and the signing key itself stays in Cloud KMS -- it is never uploaded.
  • Grant Play the Decrypt and Sign IAM permissions on the key version before calling either subcommand. Google validates this server-side; without it the call fails with a Play API error (exit 4).
  • enroll takes exactly one of --new-app / --existing-app; neither or both is a usage error. --new-app also requires --cert.
  • rotate applies only to apps already enrolled with a self-hosted key (Google-managed rotation goes through Play Console) and needs a signing certificate lineage file as proof of rotation, produced with apksigner rotate.
  • --reason is required and rejects KEY_ROTATION_REASON_UNSPECIFIED locally. Accepted: COMPROMISED_KEY, USE_STRONGER_KEY, USE_SAME_KEY_FOR_MULTIPLE_APPS, ROUTINE_KEY_UPGRADE, OTHER.
  • There is no confirmation prompt in CI. Both commands print the warning and prompt only in an interactive terminal; under CI, a non-TTY, --no-interactive, GPC_NO_INTERACTIVE, or the global --yes they proceed. Gate them in the pipeline yourself if you want a human in the loop. --dry-run previews the request without sending it.

Verification

  • gpc auth status shows the expected service account email
  • gpc doctor passes all checks
  • .gpcrc.json contains no secrets or key paths
  • Audit log at ~/.config/gpc/audit.log is being written
  • CI secrets are encrypted and not visible in logs
  • Old keys are deleted after rotation

Related skills

  • gpc-setup — initial authentication and configuration
  • gpc-user-management — managing team access and permissions
  • gpc-ci-integration — secure CI/CD pipeline configuration
  • gpc-troubleshooting — debugging auth errors

Comments

Markdown supported