diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..15ba33bca --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,28 @@ +## Goal +Describe in one sentence what this PR delivers. + +## Changes +- Added/updated: +- Added/updated: +- Added/updated: + +## Testing +List the commands you ran and the observed results. + +```bash +# paste commands here +``` + +Observed output/results: +- +- +- + +## Artifacts & Screenshots +- Submission file: `submissions/labN.md` +- Other artifacts: +- Screenshots: + +- [ ] Title is clear (`feat(labN): ` style) +- [ ] No secrets/large temp files committed +- [ ] Submission file at `submissions/labN.md` exists diff --git a/.github/workflows/lab1-smoke.yml b/.github/workflows/lab1-smoke.yml new file mode 100644 index 000000000..ec2f567db --- /dev/null +++ b/.github/workflows/lab1-smoke.yml @@ -0,0 +1,40 @@ +name: Lab 1 Smoke Test + +on: + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + smoke-test: + runs-on: ubuntu-latest + + steps: + - name: Pull Juice Shop image + run: docker pull bkimminich/juice-shop:v20.0.0 + + - name: Start Juice Shop + run: | + docker run -d \ + --name juice-shop \ + -p 3000:3000 \ + bkimminich/juice-shop:v20.0.0 + + - name: Wait for application + run: | + for i in $(seq 1 30); do + curl --silent --fail http://localhost:3000/rest/admin/application-version >/dev/null && exit 0 + sleep 2 + done + exit 1 + + - name: Verify homepage returns HTTP 200 + run: | + test "$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/)" = "200" + + - name: Show HTTP status code + run: | + curl -is http://localhost:3000/ | head -n1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..e3ab45683 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: + - repo: https://github.com/gitleaks/gitleaks + rev: v8.30.1 + hooks: + - id: gitleaks + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: detect-private-key + - id: check-added-large-files diff --git a/submissions/lab3.md b/submissions/lab3.md new file mode 100644 index 000000000..4b0b9ac9d --- /dev/null +++ b/submissions/lab3.md @@ -0,0 +1,110 @@ +# Lab 3 - Submission + +## Task 1: SSH Commit Signing + +### Local configuration +- `git config --global gpg.format` -> `ssh` +- `git config --global user.signingkey` -> `/Users/rom.m.ivanov/.ssh/id_ed25519.pub` +- `git config --global commit.gpgsign` -> `true` + +### Local verification +Output of `git log --show-signature -1`: +``` +commit 61b2853e6f42d1b92483dd10cccaabb5834545e6 +Good "git" signature for ro.ivanov@innopolis.university with ED25519 key SHA256:[REDACTED] +Author: eraegar +Date: Wed Jun 17 23:45:54 2026 +0300 + + feat(lab3): SSH signing, gitleaks hooks, and history rewrite notes +``` + +### GitHub verification +- Direct link to your most recent commit on GitHub: [add after push] +- Screenshot of the Verified badge: [attach in PR or link after push] + +### One-paragraph reflection (2-3 sentences) +In a real team, an unsigned or forged-author commit could let someone deny responsibility for a change or impersonate another developer when introducing malicious code. A visible Verified badge does not prove the code is safe, but it makes authorship tampering much easier to detect and strengthens the audit trail for reviews and incident response. + +## Task 2: Pre-commit + gitleaks + +### `.pre-commit-config.yaml` (paste the full content) +```yaml +repos: + - repo: https://github.com/gitleaks/gitleaks + rev: v8.30.1 + hooks: + - id: gitleaks + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: detect-private-key + - id: check-added-large-files +``` + +### `pre-commit install` output +``` +pre-commit installed at .git/hooks/pre-commit +``` + +### The blocked commit +Output of the `git commit` that gitleaks blocked: +``` +Detect hardcoded secrets.................................................Failed +- hook id: gitleaks +- exit code: 1 + +○ + │╲ + │ ○ + ○ ░ + ░ gitleaks + +Finding: GH_PAT=REDACTED +Secret: REDACTED +RuleID: github-pat +Entropy: 4.143943 +File: submissions/leak-attempt.txt +Line: 2 +Fingerprint: submissions/leak-attempt.txt:github-pat:2 + +11:17PM INF 0 commits scanned. +11:17PM INF scanned ~101 bytes (101 bytes) in 22.4ms +11:17PM WRN leaks found: 1 + +detect private key.......................................................Passed +check for added large files..............................................Passed +``` + +### Tune-out exercise +1. **Inline allowlist** - An inline allowlist in `.gitleaks.toml` is acceptable when a specific example value is known, intentional, and tightly scoped, such as a canonical documentation token that is not real and cannot be abused. The advantage is precision: the rule still protects the rest of the repository without suppressing unrelated findings. +2. **Path exclusion** - Excluding a whole path such as `docs/` is riskier because it creates a blind spot where real secrets can later be committed unnoticed. It may be tempting for noisy documentation folders, but it trades convenience for a broader loss of coverage and should be used very carefully. + +## Bonus: History Rewrite + +### Before +``` +ba9c76e (HEAD -> main) docs: add usage notes +0afb204 feat: empty log +0cde15a feat: add config +8a8d948 init +``` +Output of `git log -p | grep -c 'ghp_'`: **2** + +### After +``` +5d18498 (HEAD -> main) docs: add usage notes +4d0f9ed feat: empty log +0373b55 feat: add config +eb63517 init +``` +Output of `git log -p | grep -c 'ghp_'`: **0** +Output of `git log -p | grep -c 'REDACTED'`: **2** + +### The two-step pattern in real life +1. `git filter-repo --replace-text replacements.txt` - rewrite locally +2. Rotate or revoke the exposed secret and then force-push the rewritten history to the remote. Rewriting history removes the value from Git, but remediation still requires treating the leaked credential as compromised. + +### Two real-world gotchas you discovered (2 sentences each) +1. `git filter-repo` changes commit IDs, so every rewritten commit becomes a different object and any collaborators must resync carefully afterward. In a real shared repository, this makes communication and force-push coordination part of the incident response, not an optional cleanup detail. +2. Removing the secret from current files is not enough if it still exists in older commits. The exercise shows why prevention with hooks matters: once a secret enters history, cleanup is slower, riskier, and easier to get wrong.