From 319c9df40f3872acf128293f22ee15ef0540ea85 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:34:48 +0000 Subject: [PATCH 01/16] GitHub Classroom Feedback --- .github/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/.keep diff --git a/.github/.keep b/.github/.keep new file mode 100644 index 0000000..e69de29 From b4149a8ae1719f0d0de47af6dda3ccacdfe5186e Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:34:49 +0000 Subject: [PATCH 02/16] Setting up GitHub Classroom Feedback From 155cef8f4fbf85b4443a3510570f8bb3f82471d1 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 15:19:03 -0400 Subject: [PATCH 03/16] docs: pull request template --- .github/pull_request_template.md | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..defa3ee --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,97 @@ + + +## ✍️ Summary + +- + +## πŸ”— Related Issues / 🎫 Tickets + +Fixes # +Refs # + +## 🧩 Changes + +- + +## πŸ–ΌοΈ Screenshots / πŸŽ₯ Recordings (UI) + +- + +## πŸ§ͺ How to Test + +1. +2. +3. + +### βœ… Automated Tests +- [ ] Unit tests added/updated +- [ ] Integration/E2E tests added/updated +- Notes: + +## ⚠️ Risk & Impact + +- Risk level: Low / Medium / High +- Impacted areas: +- User-facing? Yes / No + +## πŸš€ Rollout Plan +- Deploy to: Dev β†’ Staging β†’ Prod +- Feature flag(s): +- Config toggles / env vars: +- **Backout plan:** (revert PR? toggle flag? rollback migration?) + +## πŸ”’ Security & Privacy +- [ ] No secrets/keys committed +- [ ] AuthN/AuthZ rules reviewed; least privilege enforced +- [ ] Input validated & parameterized queries (no SQL injection) +- [ ] Sensitive data handling documented (PII/PHI) and minimized +- [ ] Dependency changes reviewed for CVEs (SCA) +- Notes: + +## ⚑ Performance +- [ ] No significant perf impact +- [ ] Benchmarks/metrics attached (if applicable) +- [ ] N+1 queries avoided / caching considered +- Notes: + +## 🧨 Breaking Changes +- [ ] Public API/contract changed +- [ ] Requires client/app updates +- Migration notes: + +## πŸ—„οΈ Database / ☁️ Infra Changes +- Schema/migration scripts: +- Rollback scripts: +- Infra (AWS/Terraform/CloudFormation) changes: +- Data backfill required? Yes / No + +## πŸ“ˆ Observability +- [ ] Logs/metrics/traces added +- Dashboards/alerts updated: +- Runbooks updated: + +## πŸ“ Documentation +- [ ] README / user docs +- [ ] API docs / OpenAPI +- [ ] Commented complex logic +- Links: + +## 🧰 Checklist +- [ ] Code builds & lints cleanly +- [ ] All tests green locally/CI +- [ ] PR is small & focused (or clearly labeled β€œlarge” with rationale) +- [ ] Semantic PR title (conventional commits) +- [ ] Changelog / release notes updated (if user-visible) + +## πŸ‘€ Reviewer Notes + +- + +## πŸ“¬ Post-Merge Tasks +- [ ] Create follow-up tickets +- [ ] Announce change / update runbook +- [ ] Tag release / update version From 6f89c12c6ee0a95e745ec60294a1485c45336669 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 15:25:28 -0400 Subject: [PATCH 04/16] docs: pull_request_template --- .github/pull_request_template.md | 116 +++++++------------------------ 1 file changed, 26 insertions(+), 90 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index defa3ee..0b7d3d6 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,97 +1,33 @@ - - -## ✍️ Summary - +## ✍️ Description + - -## πŸ”— Related Issues / 🎫 Tickets - +## πŸ”— Related Issues / Tickets + Fixes # -Refs # - -## 🧩 Changes - -- - -## πŸ–ΌοΈ Screenshots / πŸŽ₯ Recordings (UI) - -- - -## πŸ§ͺ How to Test - +Closes # +Relates to # + +## 🧭 Type of Change + +- [ ] ✨ Feature +- [ ] πŸ› Bug fix +- [ ] ♻️ Refactor +- [ ] 🧹 Chore +- [ ] πŸ“š Docs +- [ ] ⚑ Performance +- [ ] βœ… Tests +- [ ] πŸ€– CI/CD +- [ ] πŸ” Security +- [ ] 🧨 Breaking change (adds non-backward-compatible behavior) + +## πŸ§ͺ How Has This Been Tested? + +**Environment:** OS / Browser / Runtime / DB +**Steps to test:** 1. 2. 3. - -### βœ… Automated Tests -- [ ] Unit tests added/updated -- [ ] Integration/E2E tests added/updated -- Notes: - -## ⚠️ Risk & Impact - -- Risk level: Low / Medium / High -- Impacted areas: -- User-facing? Yes / No - -## πŸš€ Rollout Plan -- Deploy to: Dev β†’ Staging β†’ Prod -- Feature flag(s): -- Config toggles / env vars: -- **Backout plan:** (revert PR? toggle flag? rollback migration?) - -## πŸ”’ Security & Privacy -- [ ] No secrets/keys committed -- [ ] AuthN/AuthZ rules reviewed; least privilege enforced -- [ ] Input validated & parameterized queries (no SQL injection) -- [ ] Sensitive data handling documented (PII/PHI) and minimized -- [ ] Dependency changes reviewed for CVEs (SCA) -- Notes: - -## ⚑ Performance -- [ ] No significant perf impact -- [ ] Benchmarks/metrics attached (if applicable) -- [ ] N+1 queries avoided / caching considered -- Notes: - -## 🧨 Breaking Changes -- [ ] Public API/contract changed -- [ ] Requires client/app updates -- Migration notes: - -## πŸ—„οΈ Database / ☁️ Infra Changes -- Schema/migration scripts: -- Rollback scripts: -- Infra (AWS/Terraform/CloudFormation) changes: -- Data backfill required? Yes / No - -## πŸ“ˆ Observability -- [ ] Logs/metrics/traces added -- Dashboards/alerts updated: -- Runbooks updated: - -## πŸ“ Documentation -- [ ] README / user docs -- [ ] API docs / OpenAPI -- [ ] Commented complex logic -- Links: - -## 🧰 Checklist -- [ ] Code builds & lints cleanly -- [ ] All tests green locally/CI -- [ ] PR is small & focused (or clearly labeled β€œlarge” with rationale) -- [ ] Semantic PR title (conventional commits) -- [ ] Changelog / release notes updated (if user-visible) - -## πŸ‘€ Reviewer Notes - +**Expected result:** +**Screenshots / Recordings (if UI):** - - -## πŸ“¬ Post-Merge Tasks -- [ ] Create follow-up tickets -- [ ] Announce change / update runbook -- [ ] Tag release / update version From 57c8a40f32b8dadc8764241d64cc12194e3589a2 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 20:48:49 -0400 Subject: [PATCH 05/16] Add pre-commit config files --- .github/workflows/pre-commit.yml | 10 +++++++++ .pre-commit-config.yaml | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..bcff892 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,10 @@ +name: pre-commit +on: [push, pull_request] +jobs: + run: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: { python-version: "3.11" } + - uses: pre-commit/action@v3.0.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..05a1ef9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,37 @@ +repos: + # Universal hygiene + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-merge-conflict + - id: check-added-large-files + + # Secret detection (fast) + - repo: https://github.com/Yelp/detect-secrets + rev: v1.5.0 + hooks: + - id: detect-secrets + args: ["--baseline", ".secrets.baseline"] + additional_dependencies: ["cryptography>=41.0.0"] + + # Python quality & security + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.6.9 + hooks: + - id: ruff + - id: ruff-format + - repo: https://github.com/PyCQA/bandit + rev: 1.7.9 + hooks: + - id: bandit + args: ["-q", "-ll", "-x", "tests"] + + # JSON/YAML prettify + - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.13.0 + hooks: + - id: pretty-format-json + args: ["--autofix", "--no-sort-keys", "--indent", "2"] From d15396c58a2579138af778c8b47f057fd1582b4a Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 20:54:18 -0400 Subject: [PATCH 06/16] fix: pre-commit workflow file --- .github/workflows/pre-commit.yml | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index bcff892..959c5c5 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -1,10 +1,36 @@ +# .github/workflows/pre-commit.yml name: pre-commit -on: [push, pull_request] +on: + push: + branches: [ "**" ] + pull_request: + branches: [ "**" ] + jobs: run: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 - with: { python-version: "3.11" } - - uses: pre-commit/action@v3.0.1 + with: + python-version: "3.11" + cache: "pip" + + # (Optional) speeds up subsequent runs + - name: Cache pre-commit envs + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + + - name: Install pre-commit + run: pip install pre-commit + + - name: Run pre-commit + run: pre-commit run --all-files --show-diff-on-failure + From 4e491b50e72b1ba561e06f90f85e9a4e658546af Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 21:02:34 -0400 Subject: [PATCH 07/16] remove breakout exercises from repo --- breakout-exercises/code_review_exercise.md | 113 ----------- .../crisis_management_exercise.md | 169 ---------------- breakout-exercises/merge_conflict_exercise.md | 181 ------------------ 3 files changed, 463 deletions(-) delete mode 100644 breakout-exercises/code_review_exercise.md delete mode 100644 breakout-exercises/crisis_management_exercise.md delete mode 100644 breakout-exercises/merge_conflict_exercise.md diff --git a/breakout-exercises/code_review_exercise.md b/breakout-exercises/code_review_exercise.md deleted file mode 100644 index bc31b44..0000000 --- a/breakout-exercises/code_review_exercise.md +++ /dev/null @@ -1,113 +0,0 @@ -# Code Review Exercise - In-Class Breakout - -**Duration:** 15 minutes -**Format:** Individual work with team discussion -**Objective:** Practice identifying security vulnerabilities and writing professional code review comments - ---- - -## The Code to Review - -You're reviewing this Python authentication code that has multiple security issues. Your job is to find them and provide professional feedback. - -```python -# auth_system.py - Find the security issues -import requests -import sqlite3 -import hashlib - -API_KEY = "sk-live-1234567890abcdef" -DATABASE_URL = "postgresql://admin:password123@localhost/prod" -DEBUG_MODE = True - -def authenticate_user(username, password): - conn = sqlite3.connect("users.db") - query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'" - - result = conn.execute(query).fetchone() - - print(f"Login attempt: {username}:{password}") - - response = requests.post("https://api.auth.com/verify", - data={"user": username, "key": API_KEY}) - - return response.json() - -def reset_password(user_id, new_password): - conn = sqlite3.connect("users.db") - query = f"UPDATE users SET password='{new_password}' WHERE id={user_id}" - conn.execute(query) - conn.commit() - -def hash_password(password): - return hashlib.md5(password.encode()).hexdigest() - -def admin_check(user_id): - if user_id == 1 or user_id == "admin": - return True - return False -``` - ---- - -## Your Task (10 minutes individual work) - -**Find and document at least 6 security issues** using this professional format: - -```markdown -## Code Review Comments - -**πŸ”΄ SECURITY: [Issue Type]** -**Line X:** [Specific problem description] -**Impact:** [What could go wrong if exploited] -**Suggestion:** -```python -# Instead of this vulnerable code: -vulnerable_example() - -# Use this secure approach: -secure_example() -``` -**Priority:** Critical/High/Medium/Low -``` - ---- - -## Hints - -- Look for hardcoded secrets and credentials -- Check for SQL injection risks in string-formatted queries -- Watch for logging of sensitive data -- Ensure passwords are hashed with modern algorithms -- Validate inputs and handle errors -- Avoid insecure external API usage without checking responses -- Beware of hidden backdoors and insecure defaults - ---- - -## Team Discussion (5 minutes) - -**Share with your breakout room:** -1. Which issues did you find? -2. Which ones did you miss? -3. How would you prioritize fixing them? -4. What was challenging about writing professional review comments? - -**Discussion Questions:** -- What makes a code review comment helpful vs. just critical? -- How do you balance being thorough with being constructive? -- What would you want to see in a review of your own code? - ---- - ---- - -## Real-World Application - -These are the exact types of issues you'll encounter in professional code reviews: -- **Hardcoded secrets** appear in ~15% of repositories -- **SQL injection** remains a top security vulnerability -- **Weak password hashing** affects millions of users -- **Missing input validation** leads to data breaches - -The review skills you practice here directly apply to protecting your team's production systems. \ No newline at end of file diff --git a/breakout-exercises/crisis_management_exercise.md b/breakout-exercises/crisis_management_exercise.md deleted file mode 100644 index 9b26121..0000000 --- a/breakout-exercises/crisis_management_exercise.md +++ /dev/null @@ -1,169 +0,0 @@ -# Git Crisis Management Exercise - Theory Breakout - -**Duration:** 15 minutes -**Format:** Group discussion (no commands run) -**Objective:** Think through how to respond to Git disasters under time pressure - ---- - -## 🚨 EMERGENCY SCENARIO - -**ALERT:** You've just discovered that API keys were accidentally committed to your main branch **3 commits ago**. The repository is **PUBLIC** and the keys are **currently active** in your production system. - -**Timeline:** This happened 2 hours ago. You need to respond immediately. - ---- - -## Your Emergency Response (10 minutes, discussion) - -### Phase 1: Immediate Damage Control (First 3 minutes) - -**CRITICAL: What's your first action and why?** - -Choose the correct first step: -- [ ] A) Remove the secrets from the current code -- [ ] B) Make the repository private -- [ ] C) Rotate/revoke the compromised credentials immediately -- [ ] D) Delete the problematic commits - -**Why is this the right first step?** -_________________________________ - -### Phase 2: Git History Cleanup (Next 4 minutes) - -**Situation Assessment:** -```bash -git log --oneline -5 -a1b2c3d Fix user authentication bug -e4f5g6h Update README documentation -i7j8k9l Add production configuration ← API keys are in this commit! -m1n2o3p Add user management features -q4r5s6t Initial project setup -``` - -**Discuss your Git recovery approach:** - -**Option A: Safe Revert (if others might have pulled)** -```bash -git revert i7j8k9l -# Creates new commit that removes the secrets -``` - -**Option B: History Rewrite (if you're sure no one else pulled)** -```bash -git rebase -i HEAD~3 -# Remove the problematic commit entirely -``` - -**Which option would you choose and why?** -_________________________________ - -### Phase 3: Prevention Implementation (Last 3 minutes) - -**Set up prevention measures:** - -1. **Update .gitignore:** -```bash -# Add to .gitignore -.env -config/secrets/ -*.key -credentials.json -``` - -2. **Create pre-commit hook (example):** -```bash -#!/bin/bash -# Check for secrets before commit -if grep -r "api_key\s*=" . ; then - echo "❌ API key found! Use environment variables." - exit 1 -fi -``` - -3. **Document the incident:** -What would you write in your incident report? -_________________________________ - ---- - -## Team Discussion (5 minutes) - -**Share with your breakout room:** - -### Crisis Response Questions: -1. **Speed vs. Safety:** When would you choose history rewrite vs. revert? -2. **Communication:** Who would you notify during this incident? -3. **Prevention:** What other security measures could prevent this? - -### Git Command Practice: -1. **Have you used `git revert` vs `git rebase -i` before?** -2. **What's the difference between `git reset` and `git revert`?** -3. **When is `git push --force` acceptable?** - -### Real-World Experience: -1. **Has anyone experienced a similar incident?** -2. **What security practices does your workplace use?** -3. **How would your team handle credential rotation?** - ---- - ---- - -## Common Mistakes to Avoid - -❌ **Wrong:** Trying to fix Git history before rotating credentials -βœ… **Right:** Rotate credentials first, then clean history - -❌ **Wrong:** Using `git push --force` without checking with team -βœ… **Right:** Use `git push --force-with-lease` or coordinate with team - -❌ **Wrong:** Only removing secrets from current code -βœ… **Right:** Remove from Git history too (if possible) - -❌ **Wrong:** Not implementing prevention measures -βœ… **Right:** Set up hooks and scanning to prevent recurrence - ---- - -## Git Recovery Commands Reference - -```bash -# Find lost commits -git reflog - -# See what changed in a commit -git show - -# Undo last commit, keep changes -git reset --soft HEAD~1 - -# Undo last commit, lose changes -git reset --hard HEAD~1 - -# Undo specific commit safely -git revert - -# Interactive rebase to edit history -git rebase -i HEAD~N - -# Check if it's safe to force push -git push --force-with-lease -``` - ---- - -## Real-World Context - -**This scenario is extremely common:** -- GitHub's secret scanning finds **millions** of exposed credentials -- **Average detection time:** 20 days for exposed secrets -- **Real impact:** AWS bills of $50,000+ from compromised keys -- **Legal implications:** GDPR fines, compliance violations - -**Your response time matters:** -- **Under 1 hour:** Minimal impact if caught early -- **1-24 hours:** Moderate risk, require monitoring -- **Over 24 hours:** High risk, assume compromise - -The skills you practice here directly protect production systems and prevent security incidents that can cost companies millions of dollars. \ No newline at end of file diff --git a/breakout-exercises/merge_conflict_exercise.md b/breakout-exercises/merge_conflict_exercise.md deleted file mode 100644 index 360368d..0000000 --- a/breakout-exercises/merge_conflict_exercise.md +++ /dev/null @@ -1,181 +0,0 @@ -# Merge Conflict Exercise - Theory Breakout - -**Duration:** 15 minutes -**Format:** Group discussion (no coding) -**Objective:** Reason about intelligent merge conflict resolution strategies - ---- - -## Scenario Setup - -Two developers worked on the same Python API file simultaneously: - -**Branch A (Authentication):** Added login validation and JWT tokens -**Branch B (Database):** Added database integration and user persistence - -You need to merge both branches intelligently, preserving the best of both features. - ---- - -## Branch A: Authentication Features - -```python -from flask import Flask, request, jsonify -import jwt -import bcrypt -from datetime import datetime, timedelta - -app = Flask(__name__) -app.config['SECRET_KEY'] = 'your-secret-key' - -@app.route('/users', methods=['POST']) -def create_user(): - data = request.get_json() - - # Input validation added - if not data.get('username') or len(data.get('username', '')) < 3: - return jsonify({"error": "Username must be at least 3 characters"}), 400 - - if not data.get('password') or len(data.get('password', '')) < 8: - return jsonify({"error": "Password must be at least 8 characters"}), 400 - - # Secure password hashing - password_hash = bcrypt.hashpw(data['password'].encode('utf-8'), bcrypt.gensalt()) - - return jsonify({ - "id": 1, - "username": data['username'], - "message": "User created successfully" - }) - -@app.route('/login', methods=['POST']) -def login(): - data = request.get_json() - username = data.get('username') - password = data.get('password') - - # Validate credentials (simplified for demo) - if username and password: - # Generate JWT token - token = jwt.encode({ - 'username': username, - 'exp': datetime.utcnow() + timedelta(hours=24) - }, app.config['SECRET_KEY']) - - return jsonify({"token": token, "message": "Login successful"}) - - return jsonify({"error": "Invalid credentials"}), 401 -``` - ---- - -## Branch B: Database Features - -```python -from flask import Flask, request, jsonify -from sqlalchemy import create_engine, Column, Integer, String -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker - -app = Flask(__name__) - -# Database setup -engine = create_engine('sqlite:///users.db') -Base = declarative_base() -Session = sessionmaker(bind=engine) - -class User(Base): - __tablename__ = 'users' - id = Column(Integer, primary_key=True) - username = Column(String(50), unique=True, nullable=False) - password = Column(String(100), nullable=False) - -Base.metadata.create_all(engine) - -@app.route('/users', methods=['GET']) -def get_users(): - session = Session() - users = session.query(User).all() - result = [{"id": u.id, "username": u.username} for u in users] - session.close() - return jsonify({"users": result}) - -@app.route('/users', methods=['POST']) -def create_user(): - data = request.get_json() - session = Session() - - user = User( - username=data.get('username'), - password=data.get('password') # This needs the secure hashing from Branch A! - ) - - session.add(user) - session.commit() - - result = {"id": user.id, "username": user.username} - session.close() - return jsonify(result) -``` - ---- - -## Discussion Challenge (15 minutes) - -### Step 1: Analyze Both Branches (3 minutes) -**What does each branch accomplish?** - -**Branch A (Authentication):** -- Input validation for user creation -- Secure password hashing with bcrypt -- JWT token generation for login -- Proper error handling - -**Branch B (Database):** -- SQLAlchemy ORM for data persistence -- User model with database schema -- Get users endpoint -- Session management - -### Step 2: Plan an Integration Strategy (3 minutes) -Discuss how you would combine these features in principle. Identify risks, ordering, and validation points. - -Target outcomes: -- βœ… Use database persistence from Branch B -- βœ… Include input validation from Branch A -- βœ… Use secure password hashing from Branch A -- βœ… Have JWT authentication from Branch A -- βœ… Keep all endpoints working - -### Step 3: Outline an Approach (7 minutes) -Whiteboard or outline the sequence of steps you would take to perform the merge (branches to create, tests to run, order of resolving conflicts, checkpoints for validation). No coding required. - -### Step 4: What Would You Test? (2 minutes) -Walk through what you would test after resolving conflicts: -- Does user creation validate input AND save to database? -- Does password hashing work with database storage? -- Are all imports included? -- Do all endpoints still work? - ---- - -## Discussion Questions (Team sharing) - -1. **What was your strategy for combining the features?** -2. **What conflicts would you expect and how would you resolve them?** -3. **What would you test to make sure your merge works?** -4. **How is an intelligent merge different from just "picking one side"?** - ---- - ---- - -## Real-World Application - -This mirrors actual development scenarios: -- **Feature teams** often work on overlapping code -- **Good merges** preserve everyone's work -- **Smart resolution** is better than random conflict picking -- **Testing** your merge prevents broken deployments - -The skills you practice here prevent the "it worked on my machine" problems that break production systems. \ No newline at end of file From 31da492a1507fbe1d652bb83392e0850c4c07c10 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 21:02:47 -0400 Subject: [PATCH 08/16] add gitignore to repo --- .gitignore | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..221b8d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Python virtual environments +.venv/ +venv/ +env/ + +# Environment variables +.env +.env.* + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.pyc +*.pyd +*.pyo + +# Editor directories and files +.vscode/ +.idea/ +*.swp +*~ + +# Test and coverage files +.pytest_cache/ +.coverage +htmlcov/ + +# Logs and databases +*.log +*.sqlite3 +*.db + +# OS-specific files +.DS_Store +Thumbs.db \ No newline at end of file From 90275725103a410c96f9bb5eb93c69a1b83e4d01 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 21:18:08 -0400 Subject: [PATCH 09/16] fix pre comit hooks --- .pre-commit-config.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 05a1ef9..fe8f633 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,9 +29,12 @@ repos: - id: bandit args: ["-q", "-ll", "-x", "tests"] - # JSON/YAML prettify - - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.13.0 + # JSON formatting (correct repo) + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 # or the latest hooks: - id: pretty-format-json args: ["--autofix", "--no-sort-keys", "--indent", "2"] + # optional: if you have notebooks, keep the hook off them + exclude: '\.ipynb$' + From ae83276e771e9790fcc7d4767ea4fe52be091ff9 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 22:29:57 -0400 Subject: [PATCH 10/16] fix security issues --- starter-code-simple/app.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/starter-code-simple/app.py b/starter-code-simple/app.py index 3d01862..eec9ee9 100644 --- a/starter-code-simple/app.py +++ b/starter-code-simple/app.py @@ -2,14 +2,19 @@ # This code has intentional security flaws for educational purposes from flask import Flask, request, jsonify +from dotenv import load_dotenv import sqlite3 -import hashlib +import bcrypt +import os + +load_dotenv() # take environment variables from .env. + app = Flask(__name__) # Security Issue: Hardcoded secrets -DATABASE_URL = "postgresql://admin:password123@localhost/prod" -API_SECRET = "sk-live-1234567890abcdef" +DATABASE_URL = os.getenv("DATABASE_URL") +API_SECRET = os.getenv("API_SECRET") def get_db_connection(): return sqlite3.connect('users.db') @@ -32,12 +37,13 @@ def create_user(): password = data.get('password') # Security Issue: Weak password hashing - hashed_password = hashlib.md5(password.encode()).hexdigest() + hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)).decode() conn = get_db_connection() # Security Issue: SQL injection vulnerability conn.execute( - f"INSERT INTO users (username, password) VALUES ('{username}', '{hashed_password}')" + "INSERT INTO users (username, password) VALUES (?, ?)", + (username, hashed_password), ) conn.commit() conn.close() @@ -52,12 +58,15 @@ def login(): username = data.get('username') password = data.get('password') - hashed_password = hashlib.md5(password.encode()).hexdigest() + hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)).decode() conn = get_db_connection() # Security Issue: SQL injection vulnerability query = f"SELECT * FROM users WHERE username='{username}' AND password='{hashed_password}'" - user = conn.execute(query).fetchone() + user = conn.execute( + "SELECT * FROM users WHERE username = ? AND password = ?", + (username, hashed_password), + ).fetchone() conn.close() if user: @@ -78,4 +87,4 @@ def init_db(): if __name__ == '__main__': init_db() - app.run(debug=True) \ No newline at end of file + app.run() \ No newline at end of file From f0b73ce4f155acad02505cdb13ec41b06323f7d5 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 22:31:47 -0400 Subject: [PATCH 11/16] fix security issues --- starter-code-simple/app.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/starter-code-simple/app.py b/starter-code-simple/app.py index eec9ee9..7468d93 100644 --- a/starter-code-simple/app.py +++ b/starter-code-simple/app.py @@ -49,7 +49,6 @@ def create_user(): conn.close() # Security Issue: Logging sensitive information - print(f"Created user: {username} with password: {password}") return jsonify({"message": "User created", "username": username}) @app.route('/login', methods=['POST']) @@ -62,7 +61,6 @@ def login(): conn = get_db_connection() # Security Issue: SQL injection vulnerability - query = f"SELECT * FROM users WHERE username='{username}' AND password='{hashed_password}'" user = conn.execute( "SELECT * FROM users WHERE username = ? AND password = ?", (username, hashed_password), From 29b3bca0a189567495f453646b8628d53533c842 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 22:37:11 -0400 Subject: [PATCH 12/16] format file --- starter-code-simple/app.py | 69 ++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/starter-code-simple/app.py b/starter-code-simple/app.py index 7468d93..ccea3ab 100644 --- a/starter-code-simple/app.py +++ b/starter-code-simple/app.py @@ -7,7 +7,7 @@ import bcrypt import os -load_dotenv() # take environment variables from .env. +load_dotenv() # take environment variables from .env. app = Flask(__name__) @@ -16,73 +16,86 @@ DATABASE_URL = os.getenv("DATABASE_URL") API_SECRET = os.getenv("API_SECRET") + def get_db_connection(): - return sqlite3.connect('users.db') + return sqlite3.connect("users.db") + -@app.route('/health') +@app.route("/health") def health_check(): return jsonify({"status": "healthy", "database": DATABASE_URL}) -@app.route('/users', methods=['GET']) + +@app.route("/users", methods=["GET"]) def get_users(): conn = get_db_connection() - users = conn.execute('SELECT id, username FROM users').fetchall() + users = conn.execute("SELECT id, username FROM users").fetchall() conn.close() return jsonify({"users": [{"id": u[0], "username": u[1]} for u in users]}) -@app.route('/users', methods=['POST']) + +@app.route("/users", methods=["POST"]) def create_user(): data = request.get_json() - username = data.get('username') - password = data.get('password') - + username = data.get("username") + password = data.get("password") + # Security Issue: Weak password hashing - hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)).decode() - + hashed_password = bcrypt.hashpw( + password.encode(), bcrypt.gensalt(rounds=12) + ).decode() + conn = get_db_connection() # Security Issue: SQL injection vulnerability conn.execute( - "INSERT INTO users (username, password) VALUES (?, ?)", - (username, hashed_password), + "INSERT INTO users (username, password) VALUES (?, ?)", + (username, hashed_password), ) conn.commit() conn.close() - + # Security Issue: Logging sensitive information return jsonify({"message": "User created", "username": username}) -@app.route('/login', methods=['POST']) + +@app.route("/login", methods=["POST"]) def login(): data = request.get_json() - username = data.get('username') - password = data.get('password') - - hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)).decode() - + username = data.get("username") + password = data.get("password") + + hashed_password = bcrypt.hashpw( + password.encode(), bcrypt.gensalt(rounds=12) + ).decode() + conn = get_db_connection() # Security Issue: SQL injection vulnerability user = conn.execute( - "SELECT * FROM users WHERE username = ? AND password = ?", - (username, hashed_password), - ).fetchone() + "SELECT * FROM users WHERE username = ? AND password = ?", + (username, hashed_password), + ).fetchone() conn.close() - + if user: return jsonify({"message": "Login successful", "user_id": user[0]}) return jsonify({"message": "Invalid credentials"}), 401 + def init_db(): conn = get_db_connection() - conn.execute(''' + conn.execute( + """ CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL ) - ''') + """ + ) conn.commit() conn.close() -if __name__ == '__main__': + +if __name__ == "__main__": init_db() - app.run() \ No newline at end of file + app.run() From 1d6b8330e0b09067f03ca9ca00da73ac81ddd8fc Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 22:45:24 -0400 Subject: [PATCH 13/16] add detect-secrets to pip install --- .github/workflows/pre-commit.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 959c5c5..02e88a0 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -2,9 +2,9 @@ name: pre-commit on: push: - branches: [ "**" ] + branches: ["**"] pull_request: - branches: [ "**" ] + branches: ["**"] jobs: run: @@ -29,8 +29,7 @@ jobs: key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} - name: Install pre-commit - run: pip install pre-commit + run: pip install pre-commit detect-secrets - name: Run pre-commit run: pre-commit run --all-files --show-diff-on-failure - From 1e09009a2ef7c04983794975159431b3bbf31185 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 22:53:26 -0400 Subject: [PATCH 14/16] fix pre-commit-config file --- .pre-commit-config.yaml | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fe8f633..e76ade0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,6 @@ +# .pre-commit-config.yaml repos: - # Universal hygiene + # Hygiene - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: @@ -8,33 +9,30 @@ repos: - id: check-yaml - id: check-merge-conflict - id: check-added-large-files + - id: pretty-format-json + args: ["--autofix", "--no-sort-keys", "--indent", "2"] + exclude: '\.ipynb$' - # Secret detection (fast) + # Secret scanning (requires a committed baseline) - repo: https://github.com/Yelp/detect-secrets rev: v1.5.0 hooks: - id: detect-secrets - args: ["--baseline", ".secrets.baseline"] - additional_dependencies: ["cryptography>=41.0.0"] + args: + - --baseline + - .secrets.baseline + exclude: '(node_modules/|\.venv/|\.git/)' - # Python quality & security + # Python lint/format - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.6.9 hooks: - id: ruff - id: ruff-format + + # Python security - repo: https://github.com/PyCQA/bandit rev: 1.7.9 hooks: - id: bandit args: ["-q", "-ll", "-x", "tests"] - - # JSON formatting (correct repo) - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v6.0.0 # or the latest - hooks: - - id: pretty-format-json - args: ["--autofix", "--no-sort-keys", "--indent", "2"] - # optional: if you have notebooks, keep the hook off them - exclude: '\.ipynb$' - From a4af8c15329bc590403e6f8b5b5751dd0f0007e1 Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 23:03:00 -0400 Subject: [PATCH 15/16] fix whitespace and end of file issues --- .github/pull_request_template.md | 14 +++++++------- .gitignore | 2 +- README.md | 14 +++++++------- starter-code-simple/README.md | 2 +- starter-code-simple/requirements.txt | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0b7d3d6..471e568 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,8 +4,8 @@ ## πŸ”— Related Issues / Tickets -Fixes # -Closes # +Fixes # +Closes # Relates to # ## 🧭 Type of Change @@ -23,11 +23,11 @@ Relates to # ## πŸ§ͺ How Has This Been Tested? -**Environment:** OS / Browser / Runtime / DB +**Environment:** OS / Browser / Runtime / DB **Steps to test:** -1. -2. -3. -**Expected result:** +1. +2. +3. +**Expected result:** **Screenshots / Recordings (if UI):** - diff --git a/.gitignore b/.gitignore index 221b8d1..b12832e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,4 @@ htmlcov/ # OS-specific files .DS_Store -Thumbs.db \ No newline at end of file +Thumbs.db diff --git a/README.md b/README.md index 74c245a..f420fea 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Professional Git Workflows β€” Student Guide ## Overview -**Format:** In-class breakout exercises + after-class individual assignment -**Language:** Python +**Format:** In-class breakout exercises + after-class individual assignment +**Language:** Python **Skills:** Professional Git workflows, code reviews, merge conflicts, security --- @@ -10,7 +10,7 @@ ## Learning Objectives By completing this assignment, you will: - Design custom Git workflows that fit team needs -- Write professional pull requests and provide constructive code reviews +- Write professional pull requests and provide constructive code reviews - Resolve merge conflicts systematically and safely - Implement security best practices and catch common vulnerabilities - Set up automated quality gates with branch protection and hooks @@ -29,7 +29,7 @@ By completing this assignment, you will: ### After Class (Your Assignment) - Take the provided starter code in `starter-code-simple/` and create your own repository from it. - You can either: - 1) Fork/clone this repository and push to a new repo you own, or + 1) Fork/clone this repository and push to a new repo you own, or 2) If you received a GitHub Classroom link, accept it to create your student repo, then copy the starter code into that repo and complete all steps there. - Then complete all professionalization steps below using your own repository. @@ -43,7 +43,7 @@ Transform the provided basic Python API into a professionally configured reposit #### Starter Code You'll receive a basic Flask API with intentional security issues: - User authentication system -- Basic CRUD operations +- Basic CRUD operations - Configuration management - Simple database integration @@ -103,7 +103,7 @@ You'll review code with multiple security issues including: **πŸ”΄ SECURITY: [Issue Type]** **Line X:** [Specific problem description] **Impact:** [What could go wrong] -**Suggestion:** +**Suggestion:** ```python # Instead of this vulnerable code: old_code_example() @@ -256,4 +256,4 @@ Submit via your repository: ``` 4. Create a working branch. Implement changes via pull requests with reviews (ask a peer to review). 5. Add security scanning, pre-commit hooks, CI, and docs. Configure branch protection on `main`. -6. Complete the three breakout exercises as practice; then finalize your repository and written components. \ No newline at end of file +6. Complete the three breakout exercises as practice; then finalize your repository and written components. diff --git a/starter-code-simple/README.md b/starter-code-simple/README.md index b079fca..1431257 100644 --- a/starter-code-simple/README.md +++ b/starter-code-simple/README.md @@ -65,4 +65,4 @@ Create a new repository you own (or use your GitHub Classroom repo with admin ac 2. Add security scanning and pre-commit hooks 3. Fix all security vulnerabilities 4. Add professional documentation (README, PR template, contributing) -5. Create a CI workflow to run tests, lint, and security scans \ No newline at end of file +5. Create a CI workflow to run tests, lint, and security scans diff --git a/starter-code-simple/requirements.txt b/starter-code-simple/requirements.txt index 04752bd..4be88a2 100644 --- a/starter-code-simple/requirements.txt +++ b/starter-code-simple/requirements.txt @@ -1,2 +1,2 @@ Flask==2.3.2 -requests==2.31.0 \ No newline at end of file +requests==2.31.0 From f2153522dbfb4d2c459b4ce554c0e1fb48b88ffa Mon Sep 17 00:00:00 2001 From: dmcin003e Date: Thu, 25 Sep 2025 23:08:48 -0400 Subject: [PATCH 16/16] fix secrets --- .secrets.baseline | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 .secrets.baseline diff --git a/.secrets.baseline b/.secrets.baseline new file mode 100644 index 0000000..5ba12b5 --- /dev/null +++ b/.secrets.baseline @@ -0,0 +1,137 @@ +{ + "version": "1.5.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "GitLabTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "IPPublicDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "OpenAIDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "PypiTokenDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TelegramBotTokenDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": { + "starter-code-simple/README.md": [ + { + "type": "Secret Keyword", + "filename": "starter-code-simple/README.md", + "hashed_secret": "cbfdac6008f9cab4083784cbd1874f76618d2a97", + "is_verified": false, + "line_number": 44 + } + ] + }, + "generated_at": "2025-09-26T03:08:06Z" +}