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 issuesinactive: Minimal activity, updates only when maintainer has time (be explicit about this)deprecated: Actively discouraging new adoption, recommend alternativesarchived: 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:
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:
Security Contacts:
Make it easy to reach you for security issues:
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.ymlfor 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:
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:
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:
- CLOMonitor: CNCF project monitoring dashboard
- LFX Insights: Linux Foundation metrics
- OSPS Baseline Scanner: OpenSSF security baseline checks
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-dateapproaches (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: -
Secret Detection: Add to
.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:
- Create draft advisory: Security tab → Advisories → New draft advisory
- Fill in details: Description, severity, affected versions, patches
- Create temporary private fork: Develop fix without public visibility
- Request CVE: GitHub is a CVE Numbering Authority (CNA) and can assign CVEs
- 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:
-
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.
-
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.
-
Establish a vulnerability handling process: Know what you'll do before you receive a report. Use GitHub Security Advisories for streamlined handling.
-
Sign your releases: Sigstore makes signing easy with no key management. Start with release signing; add commit signing if your workflow supports it.
-
Maintain a security changelog: Keep users informed about security-relevant changes. This transparency builds trust.
-
Automate dependency updates: Configure Dependabot or Renovate to create update PRs automatically. Review and merge security updates promptly.
-
Pursue the OpenSSF Best Practices Badge: The badge criteria provide a roadmap for security improvement. Start with "passing" level and progress from there.
-
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.