|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to the Claude Code (claude.ai/code) AI assistant when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +This is the **OpenSSF Best Practices Badge** project (formerly CII Best Practices Badge) - a Rails web application that helps FLOSS projects self-certify that they meet security and quality best practices. The application provides a badging system with three levels: passing, silver, and gold. |
| 8 | + |
| 9 | +**Key URLs:** |
| 10 | + |
| 11 | +- Production: https://www.bestpractices.dev/ |
| 12 | +- GitHub: https://github.com/coreinfrastructure/best-practices-badge |
| 13 | + |
| 14 | +## Common Development Commands |
| 15 | + |
| 16 | +### Testing |
| 17 | + |
| 18 | +- `rails test` - Run unit/integration tests (excludes system tests by default) |
| 19 | +- `rails test:system` - Run system tests (Capybara-based browser tests) |
| 20 | +- `rails test:all` - Run ALL tests including system tests |
| 21 | +- `rails test test/integration/project_list_test.rb` - Run the specified test file |
| 22 | +- `rails test test/features/can_access_home_test.rb:4` - Run a test at line 4 of the specified test file. |
| 23 | +- `rails test rails test TESTFILE -n NAME_OR_PATTERN` - Run the specific test or pattern `NAME_OR_PATTERN` in file TESTFILE |
| 24 | + |
| 25 | +### Code Quality & Linting |
| 26 | + |
| 27 | +- `rake default` - Run local CI/CD pipeline (linting, tests, etc.). |
| 28 | + |
| 29 | + Don't just use `rake` without arguments, Claude's user permission |
| 30 | + system can't permit that without permitting all rake commands. |
| 31 | + Some additional checks (e.g., Brakeman) run on GitHub's CI/CD pipeline. |
| 32 | + |
| 33 | +- `rake rubocop` - Ruby style checker |
| 34 | +- `rake rails_best_practices` - Rails-specific best practices source checker |
| 35 | +- `rake markdownlint` - Markdown linting |
| 36 | +- `rake eslint` - JavaScript linting |
| 37 | +- `rake whitespace_check` - Check for trailing whitespace |
| 38 | +- `rake yaml_syntax_check` - YAML syntax validation |
| 39 | +- `rake license_okay` - License compliance check |
| 40 | +- `rake bundle_audit` - Security audit of gems |
| 41 | + |
| 42 | +For specific files: |
| 43 | + |
| 44 | +- `mdl FILENAME.md` - Markdownlint only the file `FILENAME.md` |
| 45 | +- `rubocop FILENAME.rb` - Run rubocop on just `FILENAME.rb` |
| 46 | + |
| 47 | +**Markdown Helper Script**: |
| 48 | + |
| 49 | +After creating or editing markdown files, use the markdown fixer script to automatically fix common markdownlint errors: |
| 50 | + |
| 51 | +```bash |
| 52 | + |
| 53 | +# Fix markdown file in-place (most common usage): |
| 54 | +script/fix_markdown.rb docs/myfile.md |
| 55 | + |
| 56 | +# See what would be fixed without changing the file: |
| 57 | +script/fix_markdown.rb --dry-run docs/myfile.md |
| 58 | + |
| 59 | +# After fixing, verify with markdownlint: |
| 60 | +mdl docs/myfile.md |
| 61 | + |
| 62 | +``` |
| 63 | + |
| 64 | +The script automatically fixes: |
| 65 | + |
| 66 | +- MD031: Fenced code blocks surrounded by blank lines |
| 67 | +- MD022: Headers surrounded by blank lines |
| 68 | +- MD032: Lists surrounded by blank lines |
| 69 | +- MD023: Headers at beginning of line (no leading spaces) |
| 70 | +- MD012: Multiple consecutive blank lines |
| 71 | + |
| 72 | +After running the script, manually fix any remaining errors that require human judgment (MD001, MD025, etc.). |
| 73 | + |
| 74 | +Don't run `rails_best_practices` to analyze individual files. |
| 75 | +The `rails_best_practices` program needs the full context of all files |
| 76 | +to do its best. |
| 77 | + |
| 78 | +**IMPORTANT**: You may ONLY use `rubocop -a` (safe autocorrect) |
| 79 | +for RuboCop corrections. |
| 80 | + |
| 81 | +**NEVER use `rubocop -A` (unsafe autocorrect)** - |
| 82 | +it frequently introduces subtle bugs by making assumptions |
| 83 | +about code context that are incorrect. Examples include: |
| 84 | + |
| 85 | +- Changing array methods to string methods (`.drop()` vs `[1..]`) |
| 86 | +- Changing hash syntax that breaks in complex ActiveRecord queries |
| 87 | + |
| 88 | +Always use `-a` (safe) and manually review any remaining suggestions from |
| 89 | +`rubocop` without flags to determine if they're actually appropriate |
| 90 | +for the specific context. |
| 91 | + |
| 92 | +### Workflow |
| 93 | + |
| 94 | +After making significant changes to source code, ALWAYS run linters: |
| 95 | + |
| 96 | +**For Ruby files**: |
| 97 | + |
| 98 | +1. Run `rubocop FILENAME.rb` on changed files |
| 99 | +2. Run `rake rails_best_practices` for Rails-specific checks |
| 100 | +3. Fix all reported problems |
| 101 | + |
| 102 | +**For Markdown files**: |
| 103 | + |
| 104 | +1. Run `script/fix_markdown.rb FILENAME.md` to auto-fix common issues |
| 105 | +2. Run `mdl FILENAME.md` to check for remaining errors |
| 106 | +3. Manually fix any errors that require human judgment (MD001, MD025, etc.) |
| 107 | + |
| 108 | +**For all changed files**: |
| 109 | + |
| 110 | +1. Run `rake whitespace_check` to catch trailing whitespace |
| 111 | +2. Fix any whitespace issues |
| 112 | + |
| 113 | +**Finally**: |
| 114 | + |
| 115 | +- Run `rails test:all` to ensure all tests pass |
| 116 | + |
| 117 | +### Development Environment Shortcut |
| 118 | + |
| 119 | +As a convenience, in the development environment you don't need to use |
| 120 | +`bundle exec` prefixes for ruby commands (though you may use them). |
| 121 | +They *are* necessary in cases, such as rake tasks, that might be |
| 122 | +used in the CI or production environments. |
| 123 | + |
| 124 | +### Security Analysis |
| 125 | + |
| 126 | +GitHub runs Brakeman and CodeQL remotely for static security analysis, it's |
| 127 | +not done on the local system. |
| 128 | + |
| 129 | +### Development Server |
| 130 | + |
| 131 | +- `rails s` - Start development server (http://localhost:3000) |
| 132 | +- `rails console` - Start Rails console for debugging |
| 133 | + |
| 134 | +## Architecture & Key Concepts |
| 135 | + |
| 136 | +### Core Models |
| 137 | + |
| 138 | +- **Project** - Central model representing a FLOSS project seeking certification |
| 139 | + - Has three badge levels: passing, silver, gold (plus `in_progress`) |
| 140 | + - Uses PostgreSQL full-text search via `pg_search` gem |
| 141 | + - Stores criteria answers and justifications |
| 142 | + - Heavy security validation and input sanitization |
| 143 | + |
| 144 | +- **User** - Project owners and contributors |
| 145 | + - Email addresses are encrypted in database using `attr_encrypted` |
| 146 | + - Uses OAuth (GitHub) and local authentication |
| 147 | + - Session management with automatic timeouts |
| 148 | + |
| 149 | +- **Criteria** - The best practices criteria that projects must meet |
| 150 | + - Dynamic criteria system supporting multiple languages |
| 151 | + - Complex validation rules and automated checking |
| 152 | + |
| 153 | +### Security Architecture |
| 154 | + |
| 155 | +Security is *extremely* important to this project. Some features: |
| 156 | + |
| 157 | +- **Encrypted Data**: User emails encrypted with AES-256-GCM |
| 158 | +- **Blind Indexing**: Email searches use blind indices for privacy |
| 159 | +- **CSRF Protection**: All forms protected with Rails CSRF tokens |
| 160 | +- **Rate Limiting**: Uses `rack-attack` for DoS protection |
| 161 | +- **Content Security Policy**: Strict CSP headers via `secure_headers` gem |
| 162 | +- **HTTPS Enforcement**: Force SSL in production |
| 163 | +- **IP Validation**: Optional Fastly IP allowlisting (cloud piercing protection) |
| 164 | + |
| 165 | +The file `docs/assurance-case.md` explains why we *believe* this is secure. |
| 166 | + |
| 167 | +### Authentication & Authorization |
| 168 | + |
| 169 | +- **Multi-provider**: GitHub OAuth + local accounts |
| 170 | +- **Session Security**: Automatic timeouts, secure cookies |
| 171 | +- **Additional Rights**: Project collaborator system via `additional_rights` table |
| 172 | +- **Admin System**: Special admin user capabilities |
| 173 | + |
| 174 | +### Internationalization |
| 175 | + |
| 176 | +- **Multi-language Support**: Currently supports 6+ languages |
| 177 | +- **URL Structure**: Routes include locale (e.g., `/en/projects`, `/fr/projets`) |
| 178 | +- **Locale Handling**: Automatic locale detection and redirection |
| 179 | +- **Translation Management**: Uses `translation.io` service |
| 180 | + |
| 181 | +### Performance & Caching |
| 182 | + |
| 183 | +- **CDN Integration**: Fastly CDN for badge images and static assets |
| 184 | +- **Database Optimization**: Careful indexing, connection pooling |
| 185 | +- **Pagination**: Uses `pagy` gem (currently v9.4.0) |
| 186 | +- **Asset Pipeline**: Rails asset pipeline with Sass compilation |
| 187 | + |
| 188 | +### Background Jobs |
| 189 | + |
| 190 | +- **solid_queue**: ActiveJob backend for async processing |
| 191 | +- **Email System**: Reminder emails, user notifications |
| 192 | +- **Data Tasks**: Daily/monthly maintenance tasks |
| 193 | + |
| 194 | +## Key Configuration Files |
| 195 | + |
| 196 | +- `config/application.rb` - Core Rails app configuration |
| 197 | +- `config/routes.rb` - Complex routing with locale support |
| 198 | +- `lib/tasks/default.rake` - Custom rake tasks including full CI pipeline |
| 199 | + |
| 200 | +The directory `config`, especially `config/initializers`, |
| 201 | +has many configuration files. |
| 202 | + |
| 203 | +## Development Practices |
| 204 | + |
| 205 | +### Code Style |
| 206 | + |
| 207 | +- **Frozen String Literals**: All files must include `# frozen_string_literal: true` |
| 208 | +- **Security Headers**: Each file has copyright and SPDX-License-Identifier |
| 209 | +- **Refinements**: Uses custom `StringRefinements` and `SymbolRefinements` |
| 210 | +- **Documentation**: Comprehensive inline documentation expected |
| 211 | + |
| 212 | +### Testing Strategy |
| 213 | + |
| 214 | +- **Minitest**: Uses Rails' default Minitest framework |
| 215 | +- **System Tests**: Capybara-based browser automation tests |
| 216 | +- **Parallel Safe**: Tests designed for process parallelism (not thread safe) |
| 217 | +- **Coverage**: Maintains high test coverage via codecov integration |
| 218 | + |
| 219 | +### Security Requirements |
| 220 | + |
| 221 | +Security is *VERY* important in this application. |
| 222 | + |
| 223 | +- **DCO Required**: All commits must be signed off per Developer Certificate of Origin |
| 224 | +- **No Sensitive Data**: Never commit API keys, passwords, or encryption keys |
| 225 | +- **Input Validation**: All user inputs heavily validated and sanitized |
| 226 | +- **SQL Injection Prevention**: Always uses Rails parameterized queries (including its ORM) with untrusted input |
| 227 | +- **XSS Prevention**: Uses templates to prevent XSS |
| 228 | +- User inputs should be checked for expected format and length. |
| 229 | +- When applicable, generate unit tests for security-critical functions (including negative tests to ensure the code fails safely). |
| 230 | +- Do not add dependencies that may be malicious or hallucinated. |
| 231 | +- Never log, expose, or commit passwords, API keys, encryption keys, or other secrets. |
| 232 | + |
| 233 | +### Version Control |
| 234 | + |
| 235 | +- **Git Workflow**: Feature branches with pull request review |
| 236 | +- **Commit Signing**: DCO sign-off required (`git commit --signoff`) |
| 237 | +- **Branch Protection**: Main branch requires review and CI passage |
| 238 | + |
| 239 | +## Environment Variables |
| 240 | + |
| 241 | +Key environment variables for development: |
| 242 | + |
| 243 | +- `RAILS_ENV` - Rails environment (development/test/production) |
| 244 | +- `EMAIL_ENCRYPTION_KEY` - 64 hex digits for email encryption |
| 245 | +- `EMAIL_BLIND_INDEX_KEY` - 64 hex digits for email search indices |
| 246 | +- `BADGEAPP_REAL_PRODUCTION` - Set to "true" only on true production site |
| 247 | +- `PUBLIC_HOSTNAME` - Hostname for the application |
| 248 | +- `DB_POOL` - Database connection pool size |
| 249 | + |
| 250 | +## Common Issues & Solutions |
| 251 | + |
| 252 | +### Database Issues |
| 253 | + |
| 254 | +- Email encryption keys must be exactly 64 hex digits (256 bits) |
| 255 | +- Use `rails db:reset` carefully - will destroy all local data |
| 256 | +- PostgreSQL full-text search requires specific indices |
| 257 | + |
| 258 | +### Testing Issues |
| 259 | + |
| 260 | +- System tests require JavaScript driver setup |
| 261 | +- Tests are process-parallel safe but NOT thread-safe |
| 262 | +- Use `RAILS_ENV=test` for test database operations |
| 263 | + |
| 264 | +### Security Considerations |
| 265 | + |
| 266 | +- Badge image URLs must be canonical for CDN caching |
| 267 | +- All user input requires validation and sanitization |
| 268 | +- Session timeouts are enforced - don't extend arbitrarily |
| 269 | +- Rate limiting is aggressive - be aware when testing |
| 270 | + |
| 271 | +## Important File Locations |
| 272 | + |
| 273 | +- `app/models/project.rb` - Core project model with badge logic |
| 274 | +- `app/controllers/application_controller.rb` - Base controller with security |
| 275 | +- `config/routes.rb` - Complex internationalized routing |
| 276 | +- `docs/` - Extensive documentation including security assurance case |
| 277 | +- `lib/tasks/default.rake` - CI pipeline and custom tasks |
| 278 | +- `test/` - Comprehensive test suite |
| 279 | + |
| 280 | +## Temporary File Convention |
| 281 | + |
| 282 | +- ALL temporary files' filenames must be prefixed with a comma |
| 283 | + |
| 284 | + (e.g., `,temp-notes.md`, `,migration-plan.txt`) |
| 285 | + |
| 286 | +- This applies to documentation, scratch files, migration plans, etc. |
| 287 | +- The linting tools are configured to ignore comma-prefixed files |
| 288 | + |
| 289 | +## Rails Conventions |
| 290 | + |
| 291 | +This application mostly follows Rails 5+ baseline conventions with |
| 292 | +selective upgrades: |
| 293 | + |
| 294 | +* It most follows Rails 5 conventions in terms of |
| 295 | + |
| 296 | + directory structure, extensive use of `Rails.application.config.*`, |
| 297 | + an asset pipeline with a Rails 5+ Sprockets setup, and initializer structure. |
| 298 | + |
| 299 | +* It has some pre-Rails 5 remnants: |
| 300 | + - No `config.load_defaults` in application.rb (would be Rails 5.1+) |
| 301 | + - Manual framework defaults instead of version-specific defaults |
| 302 | + - Some older asset organization (app/assets/javascripts vs modern |
| 303 | + |
| 304 | + app/javascript) |
| 305 | + |
| 306 | +* Some Rails 6+ features in use: |
| 307 | + - Modern gems: E.g., Rails, `solid_queue`, `secure_headers` |
| 308 | + - Security configurations: Advanced CSRF, CSP headers |
| 309 | + - Database setup: Modern PostgreSQL configuration |
| 310 | + |
| 311 | +We want to slowly move to more recent Rails conventions. However, rapid |
| 312 | +moves like the direct use of `rails app:update` or adding `load_defaults |
| 313 | +8.0` is likely to cause a cascade of many changes, leading to |
| 314 | +many hard-to-fix failures with little obvious external benefit. |
| 315 | + |
| 316 | +## Miscellaneous |
| 317 | + |
| 318 | +IMPORTANT: Never have trailing whitespace in text-like files including |
| 319 | +source code and markdown files. |
0 commit comments