From 37a60d7174e54f3778f527bb3584fac3b055fa6a Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Tue, 30 Dec 2025 00:08:22 -0500 Subject: [PATCH] Add CI/CD workflows and security policy --- .github/CI_CD_PRIMER.md | 228 ++++++++++++++++++++++++++++++++ .github/workflows/lint.yml | 63 +++++++++ .github/workflows/release.yml | 95 ++++++++++++++ .github/workflows/test.yml | 53 ++++++++ .pre-commit-config.yaml | 40 ++++++ SECURITY.md | 237 ++++++++++++++++++++++++++++++++++ 6 files changed, 716 insertions(+) create mode 100644 .github/CI_CD_PRIMER.md create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 .pre-commit-config.yaml create mode 100644 SECURITY.md diff --git a/.github/CI_CD_PRIMER.md b/.github/CI_CD_PRIMER.md new file mode 100644 index 0000000..f7112ab --- /dev/null +++ b/.github/CI_CD_PRIMER.md @@ -0,0 +1,228 @@ +# CI/CD Primer for Stegasoo + +## What is CI/CD? + +**CI** = Continuous Integration +**CD** = Continuous Deployment + +Think of it as a robot assistant that automatically: +1. **Tests your code** every time you push +2. **Checks formatting** so code stays consistent +3. **Publishes releases** when you tag a version + +``` +You push code → GitHub runs workflows → You get ✓ or ✗ +``` + +--- + +## How It Works + +### The Trigger + +When you `git push` or create a Pull Request, GitHub looks for workflow files in: +``` +.github/workflows/*.yml +``` + +Each `.yml` file defines a workflow - a series of steps to run. + +### The Runners + +GitHub provides free Linux/Mac/Windows VMs that: +1. Clone your repo +2. Set up Python +3. Run your commands +4. Report success/failure + +You don't manage servers - GitHub does. + +--- + +## Your Workflows + +### 1. `test.yml` - Run Tests on Every Push + +**When it runs:** Every push, every PR + +**What it does:** +``` +1. Spins up Ubuntu VM +2. Installs Python 3.10, 3.11, 3.12 +3. Installs your package + dependencies +4. Runs pytest +5. Reports pass/fail +``` + +**You'll see:** Green ✓ or red ✗ on your commits + +### 2. `lint.yml` - Check Code Style + +**When it runs:** Every push, every PR + +**What it does:** +``` +1. Runs ruff (fast Python linter) +2. Checks black formatting +3. Fails if code isn't formatted +``` + +**Why:** Keeps code consistent, catches common bugs + +### 3. `release.yml` - Publish to PyPI + +**When it runs:** Only when you create a version tag + +**What it does:** +``` +1. Builds the package (wheel + sdist) +2. Uploads to PyPI +``` + +**You trigger it by:** +```bash +git tag v2.2.0 +git push origin v2.2.0 +``` + +--- + +## Day-to-Day Usage + +### Normal Development + +```bash +# Make changes +git add . +git commit -m "Add new feature" +git push +``` + +Then check GitHub → Actions tab → See if tests pass. + +### If Tests Fail + +1. Click the failed workflow +2. Click the failed job +3. Read the error log +4. Fix locally, push again + +### Making a Release + +```bash +# 1. Update version in pyproject.toml and constants.py +# 2. Commit the version bump +git add . +git commit -m "Bump version to 2.2.1" +git push + +# 3. Create and push a tag +git tag v2.2.1 +git push origin v2.2.1 + +# 4. GitHub automatically publishes to PyPI +``` + +--- + +## Reading the GitHub UI + +### Actions Tab + +``` +Repository → Actions → [List of workflow runs] +``` + +Each run shows: +- ✓ Green checkmark = passed +- ✗ Red X = failed +- 🟡 Yellow dot = running + +### Pull Request Checks + +When you open a PR, you'll see: +``` +┌─────────────────────────────────────────┐ +│ All checks have passed │ +│ ✓ test (3.10) — 45s │ +│ ✓ test (3.11) — 42s │ +│ ✓ test (3.12) — 44s │ +│ ✓ lint — 12s │ +└─────────────────────────────────────────┘ +``` + +--- + +## Setting Up PyPI Publishing + +For `release.yml` to work, you need to add a PyPI API token: + +### One-Time Setup + +1. **Create PyPI account** at https://pypi.org/account/register/ + +2. **Generate API token:** + - PyPI → Account Settings → API tokens + - Create token (scope: entire account or just stegasoo) + - Copy the token (starts with `pypi-`) + +3. **Add to GitHub:** + - GitHub repo → Settings → Secrets and variables → Actions + - New repository secret + - Name: `PYPI_API_TOKEN` + - Value: paste the token + +Now `release.yml` can publish automatically. + +--- + +## Common Scenarios + +### "Tests pass locally but fail in CI" + +Usually means: +- Missing dependency in `pyproject.toml` +- Hardcoded path that doesn't exist in CI +- Test relies on local file + +### "Lint is failing" + +Run locally to see/fix: +```bash +# Check issues +ruff check src/ + +# Auto-fix what's possible +ruff check --fix src/ + +# Format code +black src/ +``` + +### "I want to skip CI for a commit" + +Add `[skip ci]` to commit message: +```bash +git commit -m "Update README [skip ci]" +``` + +--- + +## Costs + +GitHub Actions is **free** for public repos. + +For private repos: 2,000 minutes/month free, then paid. + +--- + +## Summary + +| Action | What Happens | +|--------|--------------| +| `git push` | Tests + lint run automatically | +| Open PR | Tests must pass before merge | +| `git tag v*` | Publishes to PyPI | +| Check results | GitHub → Actions tab | + +That's it! Push code, check for green checkmarks. diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..5d01ba0 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,63 @@ +# Check code style and formatting +name: Lint + +on: + push: + branches: [main, master, develop] + pull_request: + branches: [main, master, develop] + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + # 1. Get the code + - name: Checkout code + uses: actions/checkout@v4 + + # 2. Set up Python + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + # 3. Install linting tools + - name: Install linters + run: | + python -m pip install --upgrade pip + pip install ruff black + + # 4. Run ruff (fast linter - catches bugs and style issues) + - name: Run ruff + run: | + ruff check src/ tests/ frontends/ + + # 5. Check black formatting (doesn't modify, just checks) + - name: Check black formatting + run: | + black --check src/ tests/ frontends/ + + # Type checking (optional but helpful) + typecheck: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + pip install mypy + + - name: Run mypy + run: | + mypy src/stegasoo --ignore-missing-imports + continue-on-error: true # Don't fail build on type errors (yet) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0a60b13 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,95 @@ +# Publish to PyPI when a version tag is pushed +name: Release + +on: + push: + tags: + - 'v*' # Triggers on v1.0.0, v2.1.0, etc. + +jobs: + # First, run tests to make sure everything works + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libzbar0 + + - name: Install package + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + - name: Run tests + run: pytest + + # Then build and publish + publish: + needs: test # Only run if tests pass + runs-on: ubuntu-latest + + # Required for PyPI trusted publishing (recommended) + permissions: + id-token: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install build tools + run: | + python -m pip install --upgrade pip + pip install build twine + + - name: Build package + run: python -m build + + - name: Check package + run: twine check dist/* + + # Option 1: Trusted Publishing (recommended, no token needed) + # Set this up at: https://pypi.org/manage/project/stegasoo/settings/publishing/ + - name: Publish to PyPI (Trusted Publishing) + uses: pypa/gh-action-pypi-publish@release/v1 + # No token needed if you configure trusted publishing on PyPI + + # Option 2: API Token (uncomment if not using trusted publishing) + # - name: Publish to PyPI (API Token) + # env: + # TWINE_USERNAME: __token__ + # TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + # run: twine upload dist/* + + # Create GitHub Release with changelog + github-release: + needs: publish + runs-on: ubuntu-latest + + permissions: + contents: write # Needed to create releases + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + generate_release_notes: true + files: | + dist/* diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..9fb325c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,53 @@ +# Run tests on every push and pull request +name: Tests + +on: + push: + branches: [main, master, develop] + pull_request: + branches: [main, master, develop] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + fail-fast: false # Don't cancel other jobs if one fails + matrix: + python-version: ["3.10", "3.11", "3.12"] + + steps: + # 1. Get the code + - name: Checkout code + uses: actions/checkout@v4 + + # 2. Set up Python + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + # 3. Install system dependencies (for pyzbar QR reading) + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libzbar0 + + # 4. Install the package with all dependencies + - name: Install package + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + # 5. Run tests with coverage + - name: Run tests + run: | + pytest --cov=stegasoo --cov-report=xml --cov-report=term-missing + + # 6. Upload coverage report (optional - integrates with codecov.io) + - name: Upload coverage + uses: codecov/codecov-action@v4 + if: matrix.python-version == '3.11' # Only upload once + with: + files: ./coverage.xml + fail_ci_if_error: false # Don't fail if codecov is down diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b5d4b30 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +# Pre-commit hooks - run formatting/linting before each commit +# Install: pip install pre-commit && pre-commit install +# Manual run: pre-commit run --all-files + +repos: + # Ruff - fast Python linter (replaces flake8, isort, etc.) + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.6 + hooks: + - id: ruff + args: [--fix] # Auto-fix what's possible + - id: ruff-format # Ruff's formatter (alternative to black) + + # Black - code formatter (comment out if using ruff-format above) + # - repo: https://github.com/psf/black + # rev: 23.11.0 + # hooks: + # - id: black + + # Basic file hygiene + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace # Remove trailing spaces + - id: end-of-file-fixer # Ensure newline at EOF + - id: check-yaml # Validate YAML + - id: check-toml # Validate TOML + - id: check-added-large-files # Prevent giant files + args: ['--maxkb=1000'] + - id: check-merge-conflict # No merge conflict markers + - id: debug-statements # No print() or pdb left behind + + # Security checks + - repo: https://github.com/PyCQA/bandit + rev: 1.7.6 + hooks: + - id: bandit + args: ["-c", "pyproject.toml"] + additional_dependencies: ["bandit[toml]"] + exclude: tests/ diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5f3321b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,237 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 2.x.x | :white_check_mark: | +| 1.x.x | :x: | + +## Reporting a Vulnerability + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please email: **security@example.com** (replace with your email) + +Include: +- Description of the vulnerability +- Steps to reproduce +- Potential impact +- Any suggested fixes + +You should receive a response within 48 hours. We'll work with you to understand and address the issue. + +--- + +# Threat Model + +## What Stegasoo Protects + +Stegasoo is designed to hide the **existence** of a secret message within an ordinary-looking image, protected by multi-factor authentication. + +### Protection Goals + +| Goal | How It's Achieved | +|------|-------------------| +| **Confidentiality** | AES-256-GCM encryption with Argon2id key derivation | +| **Steganography** | LSB embedding with pseudo-random pixel selection | +| **Authentication** | Multi-factor: reference photo + passphrase + PIN (or RSA key) | +| **Integrity** | GCM authentication tag detects tampering | + +### Security Factors + +Stegasoo combines multiple authentication factors: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Key Derivation │ +│ │ +│ Reference Photo ─────┐ │ +│ (something you have) │ │ +│ ├──► Argon2id ──► AES-256 Key │ +│ Day Passphrase ──────┤ (256MB RAM) │ +│ (something you know) │ │ +│ │ │ +│ PIN or RSA Key ──────┘ │ +│ (second factor) │ +└─────────────────────────────────────────────────────────────┘ +``` + +## What Stegasoo Does NOT Protect Against + +### 1. Statistical Steganalysis + +**Risk:** Advanced analysis can detect that an image contains hidden data. + +**Reality:** LSB steganography is detectable by: +- Chi-square analysis +- RS analysis +- Machine learning classifiers + +**Mitigation:** Stegasoo uses pseudo-random pixel selection (not sequential), which helps but doesn't eliminate detectability. + +**Recommendation:** Don't rely on Stegasoo if your adversary has: +- Access to the original carrier image +- Sophisticated forensic tools +- Motivation to analyze your specific images + +### 2. Compromised Endpoints + +**Risk:** If your device is compromised, the attacker can capture credentials. + +**Not protected:** +- Keyloggers capturing your PIN/passphrase +- Screen capture of decoded messages +- Memory scraping during encode/decode +- Malware on sender or receiver device + +**Recommendation:** Use on trusted devices only. + +### 3. Reference Photo Exposure + +**Risk:** The reference photo is a critical secret. + +**If leaked:** Attacker only needs to guess/brute-force the passphrase + PIN. + +**Recommendation:** +- Never share the reference photo +- Use a unique photo (not posted online) +- Store securely (encrypted drive, password manager) + +### 4. Weak Credentials + +**Risk:** Short PINs or common passphrases can be brute-forced. + +| PIN Length | Combinations | Time to Brute Force* | +|------------|--------------|----------------------| +| 4 digits | 10,000 | Seconds | +| 6 digits | 1,000,000 | Minutes | +| 8 digits | 100,000,000 | Hours | +| 9 digits | 1,000,000,000| Days | + +*With Argon2 (256MB RAM, 4 iterations), each attempt takes ~1 second, making brute force slow but not impossible for short PINs. + +**Recommendation:** +- Use 8+ digit PINs +- Use 4+ word passphrases +- Consider RSA keys for high-security use cases + +### 5. Image Modification + +**Risk:** Lossy compression destroys hidden data. + +**Data is destroyed by:** +- JPEG compression +- Resizing +- Filters/effects +- Screenshots +- Social media upload (Instagram, Twitter, etc.) + +**Recommendation:** +- Always use lossless formats (PNG, BMP) +- Transfer files directly (email, Signal, USB) +- Never upload stego images to social media + +### 6. Metadata Leakage + +**Risk:** The stego image itself may reveal information. + +**Potential leaks:** +- File creation timestamp +- Camera EXIF data (if carrier has it) +- File size changes + +**Mitigation:** Stegasoo strips EXIF on output, but timestamps remain. + +### 7. Traffic Analysis + +**Risk:** The act of sending an image may be suspicious. + +**Not protected:** +- Network observers seeing you send image files +- Email metadata showing sender/receiver +- Frequency analysis of communications + +**Recommendation:** Use alongside normal image-sharing behavior. + +## Cryptographic Details + +### Encryption + +| Component | Algorithm | Parameters | +|-----------|-----------|------------| +| Key Derivation | Argon2id | 256MB RAM, 4 iterations, 4 parallelism | +| Fallback KDF | PBKDF2-SHA256 | 600,000 iterations | +| Encryption | AES-256-GCM | 12-byte IV, 16-byte tag | +| Photo Hash | SHA-256 | Full image bytes | + +### Pixel Selection + +Pixels are selected pseudo-randomly using a key derived from: +``` +pixel_key = SHA256(photo_hash || passphrase || date || pin/rsa_signature) +``` + +This prevents: +- Sequential embedding patterns +- Statistical detection of modified regions + +### Format + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Magic (4B) │ Version (1B) │ Date (10B) │ Salt (32B) │ IV (12B) │ +├──────────────────────────────────────────────────────────────┤ +│ Encrypted Payload (AES-256-GCM) │ +│ ├── Type (1B): 0x01=text, 0x02=file │ +│ ├── Length (4B) │ +│ ├── Data (variable) │ +│ └── [Filename if file] (variable) │ +├──────────────────────────────────────────────────────────────┤ +│ GCM Auth Tag (16B) │ +└──────────────────────────────────────────────────────────────┘ +``` + +## Best Practices + +### For Maximum Security + +1. **Use RSA keys** instead of PINs for authentication +2. **Use unique reference photos** not available online +3. **Use long passphrases** (4+ random words) +4. **Transfer via secure channels** (Signal, encrypted email) +5. **Delete stego images** after message is read +6. **Keep software updated** for security fixes + +### For Casual Privacy + +1. **6-digit PIN** is sufficient for non-adversarial use +2. **3-word passphrase** provides reasonable security +3. **PNG format** always for output +4. **Direct file transfer** (email attachment, AirDrop) + +## Known Limitations + +| Limitation | Impact | Status | +|------------|--------|--------| +| LSB is detectable | Statistical analysis can detect hidden data | By design (tradeoff for capacity) | +| No forward secrecy | Compromised key decrypts all messages | Use different keys per message for high security | +| Date in header | Reveals when message was encoded | By design (enables day-specific passphrases) | +| No deniability | Single password = single message | Future: plausible deniability layers | + +## Security Audit Status + +This software has **not** been professionally audited. Use at your own risk for sensitive applications. + +If you're a security researcher interested in auditing Stegasoo, please reach out. + +--- + +## Version History (Security Relevant) + +| Version | Security Changes | +|---------|------------------| +| 2.2.0 | Added compression (no security impact) | +| 2.1.0 | Upgraded to Argon2id, increased iterations | +| 2.0.0 | Added RSA key support | +| 1.0.0 | Initial release |