Skip to content

24.1 Security Practices for Project Maintainers

Open source maintainers occupy a unique position in the software supply chain. Your project—whether downloaded a hundred times or a hundred million times—becomes part of other people's software, potentially running in environments you'll never know about. This creates both responsibility and opportunity: responsibility to your users who depend on your code's security, and opportunity to demonstrate security practices that build trust and protect the broader ecosystem.

The good news is that implementing solid security practices doesn't require a security team or extensive resources. Modern platforms like GitHub and GitLab provide free security features that automate much of the work. Frameworks like the OpenSSF Best Practices Badge provide clear guidance on what to implement and how. This section provides practical, actionable guidance for maintainers who want to secure their projects without becoming security experts.

Security Policy (SECURITY.md)

A security policy tells users and security researchers how to report vulnerabilities in your project. Without one, well-intentioned researchers may publicly disclose vulnerabilities before you can fix them—or may not report them at all.

SECURITY.md template:

# Security Policy

# Supported Versions

| Version | Supported          |
| ------- | ------------------ |
| 2.x.x   | :white_check_mark: |
| 1.x.x   | :white_check_mark: (security fixes only) |
| < 1.0   | :x:                |

## Reporting a Vulnerability

We take security seriously. If you discover a security vulnerability, 
please report it responsibly.

### How to Report

**Please do not report security vulnerabilities through public GitHub issues.**

Instead, please report them via one of these methods:

1. **GitHub Security Advisories** (Preferred): 
   Use our [private vulnerability reporting](link-to-your-repo/security/advisories/new)

2. **Email**: security@yourproject.org
   - Use our PGP key (fingerprint: XXXX XXXX XXXX XXXX) for sensitive reports
   - Key available at: [link to key]

### What to Include

- Description of the vulnerability
- Steps to reproduce
- Affected versions
- Any potential mitigations you've identified

### What to Expect

- **Acknowledgment**: Within 48 hours
- **Initial Assessment**: Within 1 week
- **Resolution Timeline**: Depends on severity, typically:
  - Critical: 7-14 days
  - High: 30 days
  - Medium/Low: 90 days

### Disclosure Policy

- We follow coordinated disclosure
- We will work with you to understand and resolve the issue
- We will credit you in our security advisory (unless you prefer anonymity)
- We ask that you give us reasonable time to address issues before public disclosure

## Security Updates

Security updates are announced via:
- GitHub Security Advisories
- Our mailing list: [link]
- Release notes

## Security Best Practices for Users

[Include any security-relevant usage guidance for your project]

SECURITY.md content guide:

Section Purpose Tips
Supported versions Set expectations on what gets fixes Be honest; don't claim support you can't provide
Reporting method Enable private vulnerability reports GitHub Security Advisories is easiest to manage
Response timeline Set expectations for reporters Commit only to what you can realistically achieve
Disclosure policy Clarify coordination expectations Be reasonable; 90 days is standard maximum
Contact information Ensure reports reach you Monitor the email/channel you list

Place SECURITY.md in your repository root, .github/ directory, or docs/ folder. GitHub will automatically link to it from your Security tab.

Communicating Your Support Model

Beyond telling users how to report vulnerabilities, help them understand what level of maintenance and support they can expect from your project. Users need to know: Is this an actively maintained critical project, or a weekend hobby you might abandon next month? Both are valid—but setting clear expectations prevents users from depending on maintenance commitments you can't or won't provide.

The challenge is that most maintainers don't explicitly communicate their support model, leaving users to guess. This leads to unrealistic expectations, frustration when vulnerabilities aren't immediately patched, and pressure on maintainers who never promised ongoing support in the first place.

Why This Matters:

When users depend on your project, they're making assumptions about future maintenance. Without clear communication, they might assume:

  • Critical security vulnerabilities will be patched within days
  • Feature requests will be considered
  • Breaking changes in dependencies will be addressed
  • Compatibility with new platform versions will be maintained

If your actual commitment is "I'll fix things when I have time," the gap between expectation and reality creates risk—for users who depend on your code and for you when you face pressure to provide support you can't sustain.

The Solution: SECURITY-INSIGHTS.yml

The OpenSSF Security Insights specification provides a standardized, machine-readable way to communicate your support model, security practices, and project status. Tools and platforms can parse this file to help users make informed decisions about depending on your project.

Creating SECURITY-INSIGHTS.yml:

Place this file in your repository root or .github/ directory:

# SECURITY-INSIGHTS.yml
header:
  schema-version: "1.0.0"
  project-url: "https://github.com/yourusername/yourproject"
  project-release: "v2.1.0"
  changelog: "https://github.com/yourusername/yourproject/releases"
  expiration-date: "2026-01-01T00:00:00.000Z"  # When to re-review this file

project-lifecycle:
  status: active  # Options: active, inactive, deprecated, archived
  roadmap-url: "https://github.com/yourusername/yourproject/roadmap"
  bug-fixes-only: false  # Set true if you're in maintenance mode
  core-maintainers: 3  # Number of active maintainers with commit access

contribution-policy:
  accepts-pull-requests: true
  accepts-automated-pull-requests: true
  automated-tools-list:
    - tool: "Dependabot"
      action: enabled
  code-of-conduct: "https://github.com/yourusername/yourproject/CODE_OF_CONDUCT.md"
  contributing-policy: "https://github.com/yourusername/yourproject/CONTRIBUTING.md"

documentation:
  - "https://yourproject.readthedocs.io"

distribution-points:
  - "https://www.npmjs.com/package/yourproject"
  - "https://pypi.org/project/yourproject/"

security-artifacts:
  threat-model:
    - threat-model-created: true
      evidence-url: "https://github.com/yourusername/yourproject/docs/threat-model.md"
  self-assessment:
    - name: "OpenSSF Best Practices Badge"
      url: "https://bestpractices.coreinfrastructure.org/projects/XXXX"
      achieved: true

security-contacts:
  - type: email
    value: security@yourproject.org
    primary: true

vulnerability-reporting:
  accepts-vulnerability-reports: true
  security-policy: "https://github.com/yourusername/yourproject/security/policy"
  email-contact: security@yourproject.org
  bug-bounty-available: false
  bug-bounty-url: null
  pgp-key: "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xYOURKEYID"

dependencies:
  third-party-packages: true
  dependencies-lists:
    - "https://github.com/yourusername/yourproject/network/dependencies"
  sbom:
    - sbom-file: "https://github.com/yourusername/yourproject/releases/download/v2.1.0/sbom.spdx.json"
      sbom-format: SPDX
      sbom-url: "https://github.com/yourusername/yourproject/releases"

Key Fields Explained:

Project Lifecycle Status:

Be honest about your project's status:

  • active: Ongoing development, regular maintenance, responsive to issues
  • inactive: Minimal activity, updates only when maintainer has time (be explicit about this)
  • deprecated: Actively discouraging new adoption, recommend alternatives
  • archived: No further changes expected, read-only

Bug Fixes Only:

project-lifecycle:
  status: active
  bug-fixes-only: true  # Maintenance mode: security/critical bugs only, no new features

This is honest communication: "I'll keep this working and secure, but I'm not adding features."

Core Maintainers Count:

project-lifecycle:
  core-maintainers: 1  # Solo maintainer

Users see single maintainer = higher key-person risk. That's valuable information.

Contribution Policy:

Be explicit about whether you accept contributions:

contribution-policy:
  accepts-pull-requests: true  # You review and merge PRs
  accepts-automated-pull-requests: true  # You accept Dependabot PRs

Or if you don't:

contribution-policy:
  accepts-pull-requests: false  # Personal project, not accepting PRs

Security Contacts:

Make it easy to reach you for security issues:

security-contacts:
  - type: email
    value: security@yourproject.org
    primary: true

Or use GitHub's private reporting:

vulnerability-reporting:
  accepts-vulnerability-reports: true
  security-policy: "https://github.com/yourusername/yourproject/security/policy"

Templates for Common Scenarios:

Scenario 1: Solo Hobby Project

Be honest—this is a side project:

header:
  schema-version: "1.0.0"
  project-url: "https://github.com/username/hobby-tool"

project-lifecycle:
  status: active
  bug-fixes-only: false
  core-maintainers: 1
  # No roadmap—respond to issues as I have time

contribution-policy:
  accepts-pull-requests: true  # Happy to accept good PRs

security-contacts:
  - type: email
    value: username@example.com
    primary: true

vulnerability-reporting:
  accepts-vulnerability-reports: true
  # I'll respond when I can—no SLA promised

README.md addition:

Maintenance Status: This is a personal project maintained in my spare time. I respond to issues and PRs when I'm able, but can't commit to specific timelines. For production use, please evaluate whether this support level meets your needs. See SECURITY-INSIGHTS.yml for details.

Scenario 2: Team-Maintained Project

Multiple maintainers, regular activity:

project-lifecycle:
  status: active
  roadmap-url: "https://github.com/org/project/roadmap"
  bug-fixes-only: false
  core-maintainers: 5

contribution-policy:
  accepts-pull-requests: true
  automated-tools-list:
    - tool: "Dependabot"
    - tool: "CodeQL"

security-artifacts:
  self-assessment:
    - name: "OpenSSF Best Practices Badge"
      achieved: true

Scenario 3: Maintenance Mode

Feature-complete, only accepting critical fixes:

project-lifecycle:
  status: active
  bug-fixes-only: true  # Key field
  core-maintainers: 2

README.md addition:

Maintenance Mode: This project is feature-complete. We will address critical bugs and security vulnerabilities but are not adding new features. For feature requests, consider forking or using alternative projects.

Scenario 4: Seeking New Maintainer

Ready to hand off:

project-lifecycle:
  status: inactive  # Be honest
  core-maintainers: 1

README.md addition:

Seeking Maintainer: I no longer have time to actively maintain this project. If you're interested in taking over maintenance, please contact me at [email]. In the meantime, I will address critical security issues on a best-effort basis but cannot commit to regular updates.

Scenario 5: Foundation/Enterprise-Backed

Funded, committed support:

project-lifecycle:
  status: active
  roadmap-url: "https://github.com/foundation/project/roadmap"
  core-maintainers: 12

security-artifacts:
  self-assessment:
    - name: "OpenSSF Best Practices Badge - Gold"
      achieved: true
    - name: "Security Audit by Trail of Bits"
      url: "https://github.com/foundation/project/docs/audit-2024.pdf"

vulnerability-reporting:
  bug-bounty-available: true
  bug-bounty-url: "https://hackerone.com/foundation-project"

Benefits of Adopting Security Insights:

For users:

  • Clear expectations about maintenance commitment
  • Machine-readable format tools can parse
  • Standardized structure across projects
  • Evidence of security practices

For you as maintainer:

  • Reduces pressure from unrealistic expectations
  • Documents your actual commitment level
  • Shows professionalism and transparency
  • Helps tools like CLOMonitor showcase your security practices

Tools That Parse Security Insights:

Several platforms provide visibility into Security Insights:

As adoption grows, more tools will integrate Security Insights to help users evaluate dependencies.

Updating Your SECURITY-INSIGHTS.yml:

This isn't a "set and forget" file. Update it when:

  • Your maintenance status changes (active → maintenance mode)
  • Maintainers join or leave
  • You achieve security milestones (Best Practices badge, audit completion)
  • Your support model changes (hobby → foundation-backed)
  • The expiration-date approaches (recommended: annual review)

Be Honest About Your Commitment:

The worst outcome is promising support you can't deliver. Users will plan around your stated commitment. If you say "active maintenance" but don't respond to issues for months, you've created risk for everyone depending on your code.

Better outcomes from honest communication:

  • "This is a hobby project": Users who need guaranteed support will look elsewhere (which is good—they shouldn't depend on you if they need SLAs). Users who are fine with best-effort support can proceed with informed expectations.

  • "Maintenance mode only": Users know not to expect new features, but can rely on you for security fixes. This manages expectations and reduces pressure.

  • "Seeking maintainer": Users can decide to fork, contribute to maintainer transition, or migrate to alternatives. Everyone has clear information.

Combining with Other Documentation:

Security Insights complements but doesn't replace:

  • SECURITY.md: How to report vulnerabilities (required)
  • README.md: Brief maintenance status summary for human readers
  • GOVERNANCE.md: Decision-making process for larger projects
  • MAINTAINERS.md: List of maintainers and their roles

Minimal Viable Security Insights:

Don't let perfect be the enemy of good. Start simple:

header:
  schema-version: "1.0.0"
  project-url: "https://github.com/yourusername/yourproject"

project-lifecycle:
  status: active  # or inactive, deprecated, archived
  core-maintainers: 1

security-contacts:
  - type: email
    value: youremail@example.com
    primary: true

vulnerability-reporting:
  accepts-vulnerability-reports: true

This minimal file communicates the essentials. Expand it over time as you implement more security practices.

Resources:

By clearly communicating your support model through Security Insights, you help users make informed decisions about depending on your project while protecting yourself from unrealistic expectations. Honest communication about what you can and can't commit to is a security practice—it helps the ecosystem make better risk decisions and reduces the chance users will be surprised when they need support you can't provide.

Enabling Platform Security Features

Modern code hosting platforms provide security features that require only enablement, not ongoing effort. These features form your first line of defense.

GitHub security feature enablement:

Navigate to Settings → Security in your repository.

1. Dependabot Alerts:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"  # or pip, maven, etc.
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

    # Group minor/patch updates to reduce PR noise
    groups:
      minor-and-patch:
        patterns:
          - "*"
        update-types:
          - "minor"
          - "patch"

Enable via: Settings → Code security and analysis → Dependabot alerts (Enable)

2. Dependabot Security Updates:

Automatically creates PRs for vulnerable dependencies.

Enable via: Settings → Code security and analysis → Dependabot security updates (Enable)

3. Secret Scanning:

Detects accidentally committed secrets (API keys, tokens, passwords).

Enable via: Settings → Code security and analysis → Secret scanning (Enable)

4. Branch Protection:

Prevent direct pushes to main branch; require reviews.

Navigate to: Settings → Branches → Add rule

Recommended settings: - Require pull request reviews before merging - Require status checks to pass before merging - Require signed commits (if using commit signing) - Include administrators

5. GitHub Security Advisories:

Enable private vulnerability reporting.

Navigate to: Settings → Code security and analysis → Private vulnerability reporting (Enable)

GitLab security feature enablement:

For GitLab projects:

  • Dependency Scanning: Add to .gitlab-ci.yml:

    include:
      - template: Security/Dependency-Scanning.gitlab-ci.yml
    

  • Secret Detection: Add to .gitlab-ci.yml:

    include:
      - template: Security/Secret-Detection.gitlab-ci.yml
    

  • Protected Branches: Settings → Repository → Protected branches

Security feature checklist:

Feature GitHub GitLab Priority
Dependency alerts ✅ Free ✅ Free (scan artifacts)* Essential
Automated dependency PRs ✅ Free ✅ Free Essential
Secret scanning ✅ Free (public repos) ✅ Free (scan execution)* Essential
Branch protection ✅ Free ✅ Free Essential
Private vulnerability reporting ✅ Free ✅ Free High
Code scanning (SAST) ✅ Free (public repos) ✅ Free Recommended

*GitLab: Scanning available on free tier; full vulnerability management features require Ultimate tier

Vulnerability Disclosure Process

Beyond having a SECURITY.md, you need a process for handling reports when they arrive.

Vulnerability disclosure process template:

1. RECEIVE REPORT
   ├── Acknowledge within 48 hours
   ├── Thank the reporter
   ├── Assign tracking ID
   └── Do NOT discuss publicly

2. ASSESS VULNERABILITY
   ├── Reproduce the issue
   ├── Determine affected versions
   ├── Assess severity (CVSS if applicable)
   └── Estimate fix timeline

3. COMMUNICATE WITH REPORTER
   ├── Share your assessment
   ├── Agree on disclosure timeline
   ├── Discuss credit preferences
   └── Keep them updated on progress

4. DEVELOP FIX
   ├── Create fix in private (GitHub Security Advisories support private forks)
   ├── Test thoroughly
   ├── Prepare release notes
   └── Prepare advisory draft

5. RELEASE AND DISCLOSE
   ├── Publish fixed version
   ├── Publish security advisory
   ├── Request CVE (if applicable)
   ├── Notify users via established channels
   └── Credit reporter

6. POST-DISCLOSURE
   ├── Monitor for questions/issues
   ├── Update documentation if needed
   └── Consider what could prevent similar issues

Using GitHub Security Advisories:

GitHub's Security Advisory feature streamlines disclosure:

  1. Create draft advisory: Security tab → Advisories → New draft advisory
  2. Fill in details: Description, severity, affected versions, patches
  3. Create temporary private fork: Develop fix without public visibility
  4. Request CVE: GitHub is a CVE Numbering Authority (CNA) and can assign CVEs
  5. Publish advisory: Releases advisory and notifies users

Severity assessment guidance:

Severity CVSS Score Characteristics
Critical 9.0-10.0 Remote code execution, no authentication required
High 7.0-8.9 Significant impact, moderate prerequisites
Medium 4.0-6.9 Limited impact or significant prerequisites
Low 0.1-3.9 Minimal impact, edge cases

When in doubt, consult the CVSS calculator or ask the security research community.

Signing Releases and Commits

Cryptographic signing provides assurance that releases genuinely come from you. As supply chain attacks increase, signed releases become increasingly important.

Signing options:

Method Complexity Key Management Verification
GPG signing Medium Manual key management gpg --verify
Sigstore/Cosign Low Keyless (identity-based) cosign verify
GitHub verified commits Low SSH or GPG Shown in GitHub UI

GPG commit signing setup:

# Generate key (if you don't have one)
gpg --full-generate-key
# Choose RSA and RSA, 4096 bits, reasonable expiration

# Get your key ID
gpg --list-secret-keys --keyid-format=long

# Configure Git
git config --global user.signingkey YOUR_KEY_ID
git config --global commit.gpgsign true

# Export public key for GitHub
gpg --armor --export YOUR_KEY_ID
# Add to GitHub: Settings → SSH and GPG keys → New GPG key

Sigstore signing for releases (keyless):

# Install cosign
brew install cosign  # or see https://docs.sigstore.dev/cosign/system_config/installation/

# Sign a release artifact
cosign sign-blob --output-signature release.sig release.tar.gz
# You'll authenticate via OIDC (GitHub, Google, Microsoft)

# Sign a container image
cosign sign your-registry/your-image:tag

# Verify
cosign verify-blob --signature release.sig release.tar.gz

Release signing workflow:

# .github/workflows/release.yml
name: Release
on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      id-token: write  # For Sigstore
    steps:
      - uses: actions/checkout@v4

      - name: Build release artifacts
        run: |
          # Your build commands
          tar -czf release.tar.gz dist/

      - name: Install Cosign
        uses: sigstore/cosign-installer@v3

      - name: Sign artifacts
        run: |
          cosign sign-blob --yes --output-signature release.tar.gz.sig release.tar.gz

      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            release.tar.gz
            release.tar.gz.sig

Maintaining a Security Changelog

A security changelog provides a dedicated record of security-relevant changes, separate from general release notes.

Security changelog format:

# Security Changelog

All security-related changes to this project will be documented in this file.

## [2.1.0] - 2024-03-15

### Fixed
- **CVE-2024-12345** (High): Fixed SQL injection in query builder
  - Affected versions: 2.0.0 - 2.0.5
  - Thanks to @researcher for responsible disclosure

### Security Improvements
- Added input validation for user-supplied file paths
- Updated `lodash` to 4.17.21 (addresses prototype pollution)

## [2.0.0] - 2024-01-10

### Breaking Changes (Security)
- Removed deprecated `unsafeExecute()` function
- Default authentication now required for API endpoints

### Dependencies
- Updated all dependencies to latest secure versions
- Removed unused dependency `vulnerable-package`

## [1.5.3] - 2023-11-20

### Fixed
- **CVE-2023-98765** (Medium): Fixed path traversal in file upload
  - Affected versions: 1.5.0 - 1.5.2

What to include:

  • CVE identifiers (when assigned)
  • Severity rating
  • Affected version range
  • Brief description (enough to understand, not enough to exploit)
  • Credit to reporters (with permission)
  • Security-relevant dependency updates
  • Security improvements (new features, hardening)
  • Breaking changes made for security reasons

Regular Dependency Updates and Audits

Dependencies require ongoing attention. Outdated dependencies accumulate vulnerabilities; abandoned dependencies may never receive fixes.

Dependency update cadence recommendations:

Update Type Recommended Cadence Approach
Security patches Immediately (within days) Automated PRs, expedited review
Patch versions Weekly Automated PRs, batch merge
Minor versions Weekly/Monthly Automated PRs, standard review
Major versions Quarterly review Manual evaluation, breaking change assessment

Automated update workflow:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
    commit-message:
      prefix: "deps"
    labels:
      - "dependencies"
    groups:
      # Group non-security updates to reduce noise
      production-dependencies:
        dependency-type: "production"
        update-types:
          - "minor"
          - "patch"
      development-dependencies:
        dependency-type: "development"
        update-types:
          - "minor"
          - "patch"

Manual dependency audits:

Beyond automated updates, periodic manual audits catch issues automation misses:

# npm
npm audit
npm outdated

# Python
pip-audit
pip list --outdated

# Go
go list -m -u all
govulncheck ./...

# Rust
cargo audit
cargo outdated

Dependency audit checklist (quarterly):

  • Run ecosystem-specific audit tools
  • Check for abandoned dependencies (no commits in 2+ years)
  • Review dependency health (maintenance, security responsiveness)
  • Evaluate if all dependencies are still needed
  • Check for lighter alternatives to heavy dependencies
  • Verify dependency licenses remain compatible

OpenSSF Best Practices Badge

The OpenSSF Best Practices Badge provides a framework for evaluating and improving project security practices. Pursuing the badge systematically improves your security posture.

Badge levels:

Level Requirements Effort
Passing Basic security practices Hours-days
Silver Enhanced practices, more automation Days-weeks
Gold Comprehensive practices, external review Weeks-months

Key criteria for supply chain security:

Criterion Description How to Satisfy
Vulnerability reporting Documented process for reporting SECURITY.md with clear instructions
Vulnerability response Process for handling reports Defined timeline, responsible disclosure
Signed releases Cryptographic signatures on releases GPG or Sigstore signing
Working build system Reproducible builds possible Documented build process
Automated test suite Tests run automatically CI/CD integration
Static analysis Code scanned for issues SAST in CI pipeline
Dependency analysis Dependencies checked for vulnerabilities Dependabot or equivalent

Start with the "passing" level—it addresses the most important practices and establishes a foundation for further improvement.

Recommendations

We recommend the following security practices for all maintainers:

  1. Create a SECURITY.md immediately: Even a minimal security policy is better than none. Use the template above as a starting point and refine over time.

  2. Enable all free platform security features: Dependabot alerts, secret scanning, and branch protection require only clicks to enable. There's no reason not to use them.

  3. Establish a vulnerability handling process: Know what you'll do before you receive a report. Use GitHub Security Advisories for streamlined handling.

  4. Sign your releases: Sigstore makes signing easy with no key management. Start with release signing; add commit signing if your workflow supports it.

  5. Maintain a security changelog: Keep users informed about security-relevant changes. This transparency builds trust.

  6. Automate dependency updates: Configure Dependabot or Renovate to create update PRs automatically. Review and merge security updates promptly.

  7. Pursue the OpenSSF Best Practices Badge: The badge criteria provide a roadmap for security improvement. Start with "passing" level and progress from there.

  8. Be honest about your capacity: Don't promise response times you can't meet. Setting realistic expectations serves everyone better than over-promising.

These practices require modest investment but significantly improve your project's security posture and your users' trust. Most importantly, they establish habits that make security a natural part of your maintenance workflow rather than an afterthought.