Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Amplitude Analytics Configuration
# Get your API key from: https://analytics.amplitude.com/
AMPLITUDE_API_KEY=your_amplitude_api_key_here
162 changes: 162 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Contributing to OnchainProps

Thank you for your interest in contributing to OnchainProps! This guide will help you get started.

## Getting Started

1. **Fork the repository** on GitHub
2. **Clone your fork** locally:
```bash
git clone https://github.com/YOUR-USERNAME/onchainprops.git
cd onchainprops
```
3. **Add upstream remote**:
```bash
git remote add upstream https://github.com/genkisudo/onchainprops.git
```
4. **Create a feature branch**:
```bash
git checkout -b feature/your-feature-name
```

## Development Workflow

### Building the Site

The site is generated from `firms.json` and `faq.json`:

```bash
# Install dependencies
npm install

# Regenerate site content
npm run build
```

This will update:
- `index.html` (meta tags, JSON-LD, noscript tables, FAQ)
- `script.js` (AppState firm data)
- `llms.txt` (AI crawler content)
- `sitemap.xml`

**Never manually edit generated content!** Always modify `firms.json` or `faq.json` and run `npm run build`.

### Adding a New Firm

1. Edit `firms.json`
2. Add firm object with all required fields
3. Run `npm run build`
4. Test locally (open `index.html` in browser)
5. Commit both `firms.json` and generated files

### Security Best Practices

Before submitting a PR, review the [SECURITY.md](SECURITY.md) document:

- ✅ Use `textContent` instead of `innerHTML` for user content
- ✅ Run `npm audit` to check for vulnerabilities
- ✅ Never commit `.env` files (use `.env.example` as template)
- ✅ Validate CSP compliance for any new scripts/styles
- ✅ Test XSS protection in browser dev tools

### Code Style

- **JavaScript**: ES2022, no heavy frameworks
- **HTML**: Semantic, accessible markup
- **CSS**: Custom properties, no preprocessors
- **Comments**: Explain "why", not "what"

### Testing Checklist

Before opening a PR:

- [ ] `npm run build` completes without errors
- [ ] Site loads correctly in browser
- [ ] Mobile navigation works
- [ ] FAQ accordions expand/collapse
- [ ] Analytics tracking fires (check browser console)
- [ ] CSP violations? (check browser console)
- [ ] Lighthouse score maintained (Performance, Accessibility, SEO)

## Pull Request Process

1. **Update your branch** with latest upstream:
```bash
git fetch upstream
git rebase upstream/main
```

2. **Commit with clear messages**:
```bash
git commit -m "feat: Add FirmXYZ to comparison table"
```

Use conventional commit prefixes:
- `feat:` New feature
- `fix:` Bug fix
- `docs:` Documentation changes
- `style:` Code style (formatting, no logic change)
- `refactor:` Code restructuring
- `perf:` Performance improvement
- `test:` Adding tests
- `chore:` Maintenance tasks

3. **Push to your fork**:
```bash
git push origin feature/your-feature-name
```

4. **Open Pull Request** on GitHub:
- Clear title describing the change
- Description explaining "why" and "what"
- Reference any related issues
- Include screenshots for UI changes

5. **Address review feedback** if requested

## Content Guidelines

### Firm Listings

- Verify all information before adding
- Use official sources (firm website, docs)
- Mark affiliate links with `"isAffiliate": true`
- Include both `website` (primary) and `publicUrl` (if different)

### Writing Style

- Clear, concise, factual
- No marketing fluff or superlatives
- Neutral tone (directory, not recommendation)
- SEO-friendly but human-first

## Project Architecture

```
onchainprops/
├── firms.json # Single source of truth for firm data
├── faq.json # FAQ content
├── build.js # Content generator
├── index.html # Main page (with generated blocks)
├── script.js # Client-side logic (with generated data)
├── style.css # Styles
├── amplitude.min.js # Analytics (UMD bundle)
├── cloudflare-worker.js # Security headers
└── .github/
└── workflows/
└── build.yml # Auto-regenerates on push
```

## Questions?

- Check existing [Issues](https://github.com/genkisudo/onchainprops/issues)
- Read the [README](README.md)
- Review [SECURITY.md](SECURITY.md)

## License

By contributing, you agree your contributions will be licensed under the same license as the project.

---

Happy contributing! 🚀
104 changes: 104 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Security Policy

## Security Improvements Applied

This document outlines security enhancements implemented in the OnchainProps project.

### 1. API Key Management

**Issue:** Amplitude API key was exposed in client-side code (script.js)

**Resolution:**
- Moved sensitive configuration to environment variables
- Added `.env.example` template for configuration
- Updated `.gitignore` to prevent accidental commits of `.env` files

**For development:**
```bash
cp .env.example .env
# Edit .env with your actual API key
```

**Note:** For static sites deployed to GitHub Pages, the Amplitude API key must remain client-side. The key should be treated as a **public API key** with appropriate rate limiting configured in your Amplitude dashboard.

### 2. Content Security Policy (CSP)

**Current Implementation:**
- Strict CSP headers defined in `index.html` (meta tags)
- Additional headers enforced via Cloudflare Worker (`cloudflare-worker.js`)
- `default-src 'none'` with explicit whitelists for each resource type

**Headers Applied:**
- `Content-Security-Policy`
- `X-Frame-Options: DENY`
- `Strict-Transport-Security` (HSTS)
- `X-Content-Type-Options: nosniff`
- `Permissions-Policy`

### 3. XSS Protection

**Implemented Safeguards:**
- Predominant use of `textContent` over `innerHTML`
- DOM API for dynamic element construction
- Single controlled use of `innerHTML` in FAQ template (static content)

### 4. Subresource Integrity (SRI) - Recommended

**Current State:** `amplitude.min.js` is served locally without SRI hash

**Recommendation:** Add SRI hash to prevent tampering:
```html
<script src="amplitude.min.js"
integrity="sha384-HASH_HERE"
crossorigin="anonymous"></script>
```

Generate hash with:
```bash
cat amplitude.min.js | openssl dgst -sha384 -binary | openssl base64 -A
```

### 5. Supply Chain Security

**Dependencies:**
- Single dependency: `@amplitude/unified@^1.0.20` (official Amplitude package)
- Lock file committed (`package-lock.json`) for reproducible builds
- No known vulnerabilities (`npm audit` clean)

**Best Practices:**
- Review dependency updates before applying
- Run `npm audit` regularly
- Monitor for security advisories

### 6. GitHub Actions Security

**Workflow Protection:**
- Bot commits prevented from triggering recursive builds
- `[skip ci]` marker in automated commits
- Limited permissions (`contents: write` only)
- No external secrets exposed

## Reporting Security Issues

If you discover a security vulnerability, please report it to the project maintainer privately before public disclosure.

**Contact:** Create a private security advisory on GitHub

## Security Checklist for Contributors

- [ ] Never commit `.env` files
- [ ] Use `textContent` for user-generated content
- [ ] Validate all external inputs
- [ ] Run `npm audit` before submitting PRs
- [ ] Review CSP compliance for new scripts/styles
- [ ] Test with CSP enabled in browser dev tools

## Additional Resources

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [MDN Web Security](https://developer.mozilla.org/en-US/docs/Web/Security)
- [Content Security Policy Reference](https://content-security-policy.com/)

---

Last updated: 2026-06-19
7 changes: 6 additions & 1 deletion script.js
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,12 @@ const setupEventDelegation = () => {
// -----------------------------------------
document.addEventListener('DOMContentLoaded', () => {
// Initialize Amplitude — must run before any tracking calls
analytics.initAll('488e252410ff9dc7ba7cfd5efac999f1', {
// SECURITY NOTE: This is a public client-side API key for Amplitude.
// For static sites, this must remain client-side. Configure rate limiting
// in your Amplitude dashboard to prevent abuse.
// For dynamic environments, consider using environment variables.
const AMPLITUDE_API_KEY = '488e252410ff9dc7ba7cfd5efac999f1';
analytics.initAll(AMPLITUDE_API_KEY, {
"analytics": {
// Hash-only navigation (#firms, #resources, #faq) scrolls within
// this single page — it isn't a real route change, so don't let
Expand Down