diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..700d8ac --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + labels: + - dependencies + - security + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + labels: + - ci diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ea2efd5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,94 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + test: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18, 20, 22] + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + security-audit: + name: Security Audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run npm audit + run: npm audit --audit-level=high + + zero-network-check: + name: Zero Network Verification + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Verify no network calls in source + run: | + echo "Checking for network API usage in source files..." + if grep -rn "fetch\|XMLHttpRequest\|WebSocket\|\.ajax\|sendBeacon\|new Request" \ + --include="*.js" --include="*.html" \ + --exclude-dir=node_modules --exclude-dir=.git \ + --exclude="build.js" .; then + echo "::error::Network API calls detected! FirePath must remain zero-network." + exit 1 + fi + echo "✓ No network calls found — privacy guarantee intact." + + build: + name: Build + runs-on: ubuntu-latest + needs: [test, security-audit, zero-network-check] + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build standalone HTML + run: node build.js + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: fire_calculator + path: fire_calculator.html + retention-days: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..aa470d2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,42 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + release: + name: Create Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Build standalone HTML + run: node build.js + + - name: Generate checksum + run: sha256sum fire_calculator.html > fire_calculator.html.sha256 + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + files: | + fire_calculator.html + fire_calculator.html.sha256 diff --git a/.gitignore b/.gitignore index c2658d7..e375bd2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ node_modules/ +.env +.env.* +*.log +.DS_Store diff --git a/README.md b/README.md index b8ebdd6..31135ab 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,13 @@ FirePath-Core is the open-source foundation. **FirePath-Pro** is a Tauri desktop Built for and with the FIRE community. +* [r/financialindependence](https://reddit.com/r/financialindependence) — where FirePath was born from real feedback +* [r/leanfire](https://reddit.com/r/leanfire) — conservative withdrawal model users +* [r/FIRE](https://reddit.com/r/FIRE) — general FIRE planning discussion +* [r/privacy](https://reddit.com/r/privacy) — local-first, no-account tool users +* [r/selfhosted](https://reddit.com/r/selfhosted) — self-host on your own machine or Raspberry Pi + + Found a bug? [Open an issue](https://github.com/sandseb123/FirePath-Core/issues). Want to add a withdrawal model? See CONTRIBUTING.md. Have a Garmin .fit file parser? We'd love a PR. --- diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d53997f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,82 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 1.0.x | :white_check_mark: | + +## Architecture & Threat Model + +FirePath-Core is designed as a **local-first, zero-network** financial calculator. This architecture eliminates entire categories of security threats by design. + +### What FirePath Does NOT Do + +- **No network requests** — No `fetch`, `XMLHttpRequest`, `WebSocket`, or any outbound calls. Verify yourself: + ```bash + grep -rn "fetch\|XMLHttpRequest\|WebSocket\|\.ajax\|sendBeacon" fire_math.js fire_calculator.html + ``` +- **No server-side processing** — All calculations run in the browser +- **No data storage on remote servers** — Your financial data never leaves your machine +- **No authentication or accounts** — Nothing to breach +- **No third-party analytics or tracking** — No cookies, no telemetry +- **No CDN dependencies at runtime** — Chart.js is bundled inline via `build.js` + +### Attack Surface + +| Vector | Risk | Mitigation | +|--------|------|------------| +| XSS via CSV import | Low | Input sanitization applied to all CSV-parsed values (fixed in `2bd6dc8`) | +| XSS via scenario rendering | Low | Template outputs are escaped before DOM insertion | +| Malicious CSV file | Low | Parser validates structure; no `eval()` or dynamic code execution | +| Supply chain (npm) | Low | Only 2 dependencies (`chart.js`, `jest`); Dependabot enabled | +| Local file tampering | Medium | Users should verify file integrity via git checksums | +| Browser extension interference | Medium | Outside project scope; users should audit extensions | + +### Data Handling + +- **Inputs**: All financial inputs are entered by the user and stored only in browser memory during the session +- **Assumption files** (`.fire-assumptions.json`): Contain only modeling parameters (rates, ages, allocations) — no account numbers, balances, or PII +- **CSV import**: Parsed entirely in JavaScript; raw file content is never persisted + +## Reporting a Vulnerability + +If you discover a security vulnerability in FirePath-Core, please report it responsibly: + +1. **Email**: Open a [GitHub Security Advisory](https://github.com/sandseb123/FirePath-Core/security/advisories/new) (preferred) +2. **Alternatively**: Open a private issue on the repository + +### What to Include + +- Description of the vulnerability +- Steps to reproduce +- Potential impact +- Suggested fix (if any) + +### Response Timeline + +- **Acknowledgment**: Within 48 hours +- **Assessment**: Within 7 days +- **Fix release**: Within 30 days for confirmed vulnerabilities + +### What Qualifies + +- XSS or injection vulnerabilities in CSV parsing or UI rendering +- Data leakage (any code path that transmits user data externally) +- Dependency vulnerabilities with a viable exploit path +- Logic errors in financial calculations that could mislead users + +### What Does NOT Qualify + +- Issues requiring physical access to the user's machine +- Browser-specific bugs outside the project's control +- Social engineering attacks +- Vulnerabilities in dependencies without a demonstrated exploit in this project + +## Security Best Practices for Users + +1. **Download from the official repo** — Only use releases from [github.com/sandseb123/FirePath-Core](https://github.com/sandseb123/FirePath-Core) +2. **Verify file integrity** — Compare checksums after download +3. **Keep dependencies updated** — Run `npm audit` periodically if using the dev setup +4. **Use a modern browser** — Ensures latest security patches and CSP support +5. **Don't modify and re-share** — If you fork, audit your changes for security diff --git a/build.js b/build.js index a946420..ebc82b6 100644 --- a/build.js +++ b/build.js @@ -14,13 +14,14 @@ let html = fs.readFileSync(htmlPath, 'utf-8'); const chartJs = fs.readFileSync(chartPath, 'utf-8'); const placeholder = '/* Chart.js 4.x — bundled inline for offline-first guarantee. No CDN. */'; -if (!html.includes(placeholder)) { - console.error('ERROR: Placeholder not found in HTML file.'); +if (html.includes(placeholder)) { + html = html.replace(placeholder, chartJs); + fs.writeFileSync(htmlPath, html); + const sizeKB = Math.round(Buffer.byteLength(html) / 1024); + console.log(`Done. fire_calculator.html is now ${sizeKB}KB with Chart.js inlined.`); +} else if (html.includes('Chart')) { + console.log('Chart.js is already inlined. Skipping.'); +} else { + console.error('ERROR: Placeholder not found and Chart.js not detected in HTML file.'); process.exit(1); } - -html = html.replace(placeholder, chartJs); -fs.writeFileSync(htmlPath, html); - -const sizeKB = Math.round(Buffer.byteLength(html) / 1024); -console.log(`Done. fire_calculator.html is now ${sizeKB}KB with Chart.js inlined.`); diff --git a/fire_calculator.html b/fire_calculator.html index e323218..a7fe87b 100644 --- a/fire_calculator.html +++ b/fire_calculator.html @@ -627,6 +627,10 @@
| Symbol | Description | Value |
|---|---|---|
| ${h.symbol} | ${h.description} | $${h.value.toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2})} |
| ${escapeHTML(h.symbol)} | ${escapeHTML(h.description)} | $${h.value.toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2})} |