Skip to content

Security Guide for OpenPets

Last Updated: 2025-11-25 Status: Production Ready

This guide outlines security best practices and tools for developing and publishing OpenPets packages.


🔐 Security Scanner

OpenPets includes a comprehensive security scanner that runs automatically before every publish to prevent accidental exposure of secrets and sensitive files.

Quick Start

bash
# Scan entire project
npm run security:scan

# Scan with verbose output
npm run security:scan:verbose

# Scan specific directory
bun scripts/security-scan.ts --dir pets/maps

# Get JSON output (for CI/CD)
npm run security:scan:json

# Fail on any issue (strictest)
npm run security:check:all

What It Detects

🔴 Critical Issues

Sensitive Files:

  • .env, .env.local, .env.production
  • private.key, *.pem, *.key
  • credentials.json, auth.json
  • secrets.*, *.pfx, *.p12
  • .git-credentials, .npmrc

API Keys & Tokens:

  • Google API Keys (AIza...)
  • OpenAI API Keys (sk-..., sk-proj-...)
  • GitHub Personal Access Tokens (ghp_...)
  • NPM Access Tokens (npm_...)
  • AWS Access Keys (AKIA...)
  • Stripe Live Keys (sk_live_...)
  • Bearer Tokens
  • Private Keys (PEM format)
  • Database connection strings with credentials

🟠 High Severity

  • Generic API key patterns
  • Configuration files with embedded secrets
  • MongoDB/PostgreSQL connection strings

🟡 Medium Severity

  • Hardcoded passwords
  • NPM configuration files (may contain tokens)

Automatic Protection

The security scanner runs automatically in these scenarios:

  1. Before npm publish - via prepublish hooks
  2. During CI/CD - in GitHub Actions
  3. In publish scripts - integrated into publish-pet.ts

The publish will fail if critical issues are detected, preventing accidental leaks.


🧱 Secure Subprocess Patterns (CLI)

When adding or updating CLI commands that call external tools (git, opencode, pets, etc.), use these rules:

  • Prefer spawn/spawnSync or execFile argument arrays over shell strings.
  • Never interpolate user-controlled values into shell commands.
  • Pass dynamic values (branch names, queries, paths) as separate argv items.
  • Normalize or validate command-specific inputs before invocation (for example branch names).
  • Treat non-zero subprocess exit codes as failures and surface a clear message.

Example:

ts
const result = spawnSync("git", ["checkout", "-b", branchName], { stdio: "inherit" })

Avoid:

ts
execSync(`git checkout -b "${branchName}"`, { stdio: "inherit" })

📋 Publishing Security Checklist

Before publishing any package, ensure:

Pre-Publish

  • [ ] Run security scan: npm run security:check
  • [ ] Review package.json files array
  • [ ] Check .gitignore includes sensitive files
  • [ ] No .env files in tracked directories
  • [ ] No hardcoded API keys in code
  • [ ] Test installation with --dry-run

NPM Account Security

  • [ ] 2FA enabled on npm account
  • [ ] Using granular access tokens (not classic tokens)
  • [ ] Tokens restricted to @openpets scope only
  • [ ] Token permissions: Publish only (no admin)
  • [ ] Tokens have expiration dates set

Package Configuration

  • [ ] publishConfig.access: "public" set
  • [ ] files array explicitly whitelist files
  • [ ] Publish via bun scripts/publish-pet.ts (handles security checks)
  • [ ] No sensitive files in files array
  • [ ] .npmignore or .gitignore properly configured

🛠️ Security Scanner Configuration

Command Line Options

bash
# Scan specific directory
bun scripts/security-scan.ts --dir pets/maps

# Verbose output (shows all severity levels)
bun scripts/security-scan.ts --verbose

# JSON output (for programmatic use)
bun scripts/security-scan.ts --json

# Fail on different severity levels
bun scripts/security-scan.ts --fail-on critical   # Default
bun scripts/security-scan.ts --fail-on high
bun scripts/security-scan.ts --fail-on medium
bun scripts/security-scan.ts --fail-on low        # Strictest

Ignored Patterns

The scanner automatically ignores:

  • node_modules/
  • dist/, build/, coverage/
  • *.test.ts, *.spec.ts
  • Files containing mock, fixture, example
  • .env.example, .env.template
  • Binary files (images, fonts, videos, archives)

Exit Codes

  • 0 - Security scan passed
  • 1 - Security issues found at or above fail threshold

🔧 Integration with Publish Workflow

Publish Scripts

The security scanner is integrated into scripts/publish-pet.ts:

typescript
// Enhanced security check runs before publish
try {
    const srcPath = join(petPath, 'src')
    if (await Bun.file(join(petPath, 'tsconfig.json')).exists()) {
         const srcStat = await Bun.file(srcPath).exists()
         if (srcStat) {
             await checkSecurity(srcPath)
         }
    }
} catch (e) {
    console.error("Security check failed:", e)
    process.exit(1)
}

What happens:

  1. Scanner checks the src/ directory
  2. Scans all .ts, .js, .json files
  3. Checks for sensitive filenames
  4. Scans content for API key patterns
  5. Fails the publish if issues found

NPM Hooks

Package.json includes pre-publish hooks:

json
{
  "scripts": {
    "prepublish:core": "npm run security:check",
    "prepublish:pet": "npm run security:check"
  }
}

🔐 NPM Provenance

Note: Provenance (--provenance) is not currently used because the source repository is private. npm provenance requires a public source repository and GitHub-hosted runners. If the repository becomes public in the future, provenance can be re-enabled by adding --provenance to the publish command in scripts/publish-pet.ts.


🔑 Token Management

Creating Granular Access Tokens

Recommended approach:

bash
# 1. Login to npm
npm login

# 2. Create scoped token
npm token create \
  --read-only=false \
  --cidr-whitelist=0.0.0.0/0 \
  --scope=@openpets \
  --description="GitHub Actions Publisher" \
  --expires=90d

Token Permissions:

  • ✅ Read and Publish
  • ✅ Scoped to @openpets/* only
  • ✅ No admin permissions
  • ✅ Expires in 1 year

Environment Variables

For CI/CD:

bash
# GitHub Actions Secrets
NPM_TOKEN=npm_xxxxxxxxxx...

Never:

  • ❌ Commit tokens to git
  • ❌ Use classic tokens (legacy)
  • ❌ Give tokens admin permissions
  • ❌ Create tokens without expiration

🚨 Responding to Security Issues

If Scanner Detects Issues

  1. Review the findings - Check what was detected
  2. Remove sensitive files - Delete or move to gitignored location
  3. Replace hardcoded secrets - Use environment variables
  4. Run scanner again - Verify issues are resolved
  5. Commit changes - Update repository

If Secret Was Published

Immediate Actions:

  1. Rotate the secret immediately

    • Generate new API keys
    • Update environment variables
    • Revoke compromised keys
  2. Unpublish if within 72 hours:

    bash
    npm unpublish openpets@1.0.x
  3. Deprecate if past 72 hours:

    bash
    npm deprecate openpets@1.0.x "Security issue - please upgrade"
  4. Publish fixed version:

    bash
    npm version patch
    npm publish --access public
  5. Notify users via:

    • GitHub Security Advisory
    • npm deprecation message
    • README update

📊 Security Audit Logs

Manual Audits

Run comprehensive security checks:

bash
# Full project scan
npm run security:scan:verbose

# Check dependencies for vulnerabilities
npm audit

# Check for outdated packages
npm outdated

# Verify published packages
npm view openpets
npm view @openpets/maps

Automated Audits

Set up GitHub Actions for automatic scanning:

yaml
name: Security Scan

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v1
      - run: npm run security:check:all
      - run: npm audit --audit-level=moderate

🎯 Security Best Practices

Code

  1. Use environment variables for all secrets
  2. Never hardcode API keys or passwords
  3. Use .env.example for documentation (without real values)
  4. Validate input from external sources
  5. Sanitize user input before processing

Configuration

  1. Keep .gitignore up to date

    .env
    .env.local
    .env.*.local
    *.pem
    *.key
    credentials.json
  2. Whitelist files explicitly in package.json

    json
    {
      "files": [
        "*.ts",
        "*.js",
        "src/**/*",
        "!**/*.test.*"
      ]
    }
  3. Use .npmignore for additional exclusions

Publishing

  1. Use bun scripts/publish-pet.ts --preview to test first
  2. Test with --dry-run before manual publishes
  3. Review package contents with npm pack
  4. Enable 2FA on npm account
  5. Use granular tokens for automation

📚 Additional Resources


🆘 Support

Found a security issue?

Public issues: Open a GitHub issue Security vulnerabilities: Email security@raggle.co (private disclosure)

Response time: 24-48 hours for critical issues