Safety & Security
Module 15 — Safety & Security
Goal: Keep your code, your credentials, and your AI workflow safe. Cover the security side of Git (secrets, signing, recovery safety), GitHub (2FA, branch protection, secret scanning, Dependabot, code scanning, Actions supply chain), and Copilot (insecure-code risk, prompt injection, and reviewing AI output safely).
Everything in the earlier modules makes you productive. This module makes you safe. Speed without safety is how secrets leak and vulnerabilities ship.
15.1 The golden rule: never commit secrets
A secret is anything that grants access — passwords, API keys, tokens, private keys, database connection strings, .env files. The single most common, most damaging Git mistake is committing one.
Why it’s so bad: from Module 04 you know commits are permanent objects in history. Deleting the file in a later commit does not remove it — the secret still lives in the earlier snapshot, reachable forever by anyone who clones the repo. On a public repo, automated bots scrape new commits for keys within seconds.
Prevent it
- Keep secrets out of code. Load them from environment variables or a secrets manager, never hard-code them.
.gitignoreyour secret files before the first commit (Module 9): add.env,*.pem,credentials.json, etc.- Review every
git diffbefore committing (Module 3).git add -plets you eyeball each change. - Use a pre-commit hook (Module 9) or a tool like
gitleaks/git-secretsto block commits containing key-shaped strings.
If a secret has already been committed
Treat it as compromised. Two steps, in this order:
- Rotate it immediately. Revoke the leaked key/token and issue a new one. Assume the old one is now public — scrubbing history does not un-leak something already pushed.
- Then remove it from history. Rewriting history is the only way to purge the object:
# Recommended modern tool (install separately):
git filter-repo --invert-paths --path config/secrets.yml
# Older built-in (slower, fiddlier):
# git filter-branch ... (avoid if you can use filter-repo)
This rewrites every affected commit (new hashes), so you must force-push and have collaborators re-clone — the same disruption as rebasing shared history (Module 5). That disruption is exactly why prevention beats cleanup. Rotation in step 1 is non-negotiable because you can’t guarantee no one already grabbed the secret.
15.2 Securing your Git identity
- Protect your SSH private key (Module 2). The
id_ed25519file (no.pub) is the one credential that must never leave your machine. Add a passphrase to it so a stolen laptop isn’t an instant compromise. - Scope Personal Access Tokens tightly. Give a PAT only the permissions and the shortest lifetime it needs; use fine-grained tokens over classic ones.
- Sign your commits (Module 2) so others can verify a commit really came from you. GitHub shows a green Verified badge. Without signing, the author email on a commit is just an unverified label anyone can fake. Enforce signing org-wide with rulesets (15.4).
git config --global commit.gpgsign true
15.3 Account-level security on GitHub
- Turn on two-factor authentication (2FA). It’s the highest-leverage protection for your account; GitHub now requires it for many contributors. Prefer an authenticator app or passkey over SMS.
- Use passkeys where available for phishing-resistant sign-in.
- Review authorized OAuth apps and SSH keys periodically and revoke anything you don’t recognize.
- Be careful with repo visibility. Double-check before making a repo public; it’s easy to expose more than intended.
15.4 Repository protection: rulesets & branch protection
By default, anyone with write access can push anything anywhere — including force-pushing over main. Branch protection rules and the newer, more flexible repository rulesets let you enforce standards. Common protections for main:
- Require pull requests before merging (no direct pushes to
main). - Require approvals — at least one reviewer must approve.
- Require status checks to pass — CI (Module 7) must be green before merge.
- Require signed commits (15.2).
- Block force pushes and branch deletion — protects history from being rewritten out from under the team.
- Bypass actors — rulesets let you grant narrow exceptions to specific users when truly needed.
These turn the collaboration workflow from Module 7 into an enforced policy rather than an honor system.
15.5 GitHub’s built-in security scanning
GitHub provides automated defenses you should switch on (many are free for public repos; some are part of GitHub Advanced Security for private/enterprise repos):
Secret scanning + push protection
Secret scanning detects known secret formats committed to a repo and alerts you. Push protection goes further — it blocks the push at the moment you try to commit a recognized secret, before it ever enters history, and logs an alert if someone bypasses the block. This is the safety net that catches the mistake in 15.1 before it becomes permanent. Enable both.
Dependabot (supply-chain safety)
Your project depends on other people’s code, and that code has vulnerabilities. Dependabot:
- raises alerts when a dependency has a known vulnerability (CVE), and
- opens automatic pull requests to bump vulnerable or outdated dependencies to safe versions.
Keeping dependencies patched closes one of the most exploited attack surfaces.
Code scanning (CodeQL)
Code scanning statically analyzes your code for vulnerability patterns (SQL injection, path traversal, etc.) and surfaces them as alerts on PRs, so flaws are caught in review rather than in production.
15.6 GitHub Actions supply-chain safety
Automation (Module 7) runs with access to your repo and secrets, so it’s a target:
- Pin third-party actions to a full commit SHA, not a moving tag:
uses: actions/checkout@<40-char-sha>. A tag like@v4can be repointed to malicious code; a SHA can’t. - Give workflows the least privilege they need with the
permissions:key; default to read-only. - Use scoped, short-lived secrets; never echo a secret into logs.
- Be cautious with workflows triggered by forks/PRs — untrusted code shouldn’t get access to your secrets.
15.7 Security and GitHub Copilot
AI assistance brings its own risks. The guiding principle:
Treat Copilot as an accelerator, not an authority. Every suggestion gets the same scrutiny as code from a brand-new teammate.
Risk 1 — Insecure generated code
LLMs learn from public code, which is full of insecure patterns. A meaningful share of AI-generated code contains vulnerabilities — missing input validation, SQL injection, weak crypto, hard-coded credentials copied from training patterns. Mitigations:
- Review AI output specifically for security, not just “does it run.”
- Run code scanning (15.5) and your linters/tests over AI-written code — the same controls, applied to AI diffs.
- Encode your security standards into custom instructions / skills (Modules 11–12), e.g. “always parameterize SQL queries; never build queries by string concatenation; never hard-code secrets.” The community awesome-copilot repo has ready-made secure-coding instruction sets.
- Limit fully-autonomous agent runs for inexperienced developers on sensitive code.
Risk 2 — Copilot leaking or generating secrets
Copilot can suggest a plausible-looking hard-coded key, or you might accidentally paste a real secret into a prompt. Keep push protection on (15.5) so a generated/pasted secret can’t be committed, and never paste live credentials into chat.
Risk 3 — Prompt injection
Prompt injection is when malicious instructions hidden in content Copilot reads (a file, an issue, a web page, a dependency’s README) hijack its behavior — tricking it into exfiltrating data or running unintended actions. Real 2025–2026 cases include the “Rules File Backdoor” (hiding malicious directives inside AI config files like instructions) and the RoguePilot class of attacks (instructions hidden in HTML comments), which led to fixes such as stripping hidden/comment content before processing. Mitigations:
- Review the customization files themselves. Because
copilot-instructions.md,*.instructions.md, andSKILL.mdsteer the agent, treat changes to them as security-sensitive code in PR review — a poisoned instruction file is a real attack vector (CVE-2025-53773 showed config manipulation enabling code execution). - Be wary of pointing the agent at untrusted content (random URLs, unvetted repos, attacker-controlled issues) and granting it powerful tools at the same time.
- Keep humans in the loop for consequential actions; review agent diffs before they’re committed or pushed (Module 14).
- Use least-privilege tools. A skill’s
allowed-tools(Module 12) should grant only what’s needed without confirmation.
Risk 4 — Licensing / provenance
Generated code may resemble licensed code. For compliance-sensitive work, enable Copilot’s duplication/match filtering where available and keep your own review process.
15.8 The layered model: why all this works together
No single control is enough; security is layers (defense in depth):
Prevent → .gitignore secrets, secure-coding instructions, least-privilege tokens
Detect → secret scanning, code scanning, Dependabot, signed-commit verification
Block → push protection, branch protection / rulesets, required checks
Review → human PR review of code AND of AI config files
Recover → reflog (Mod 8), rotate-then-scrub for leaks, restore from history
Notice how this reuses everything you already learned: Git’s recoverability (Module 8), the PR workflow (Module 7), and Copilot customization (Modules 11–12) are all also security tools when used deliberately.
Try it yourself
- Add
.envto.gitignore, create a fake.envwithAPI_KEY=test, and confirmgit statusignores it. - Turn on 2FA for your GitHub account if you haven’t.
- On a test repo, enable secret scanning + push protection, then try to commit a string that looks like a token and watch the push get blocked.
- Add a branch ruleset on
mainrequiring a PR and a passing status check; try to push directly and see it refused. - Add a security rule to your
copilot-instructions.md(“never hard-code secrets; always parameterize SQL”), then ask Copilot to write a DB query and check it complied.
Key takeaways
- Never commit secrets — they’re permanent in history. If one leaks, rotate first, then scrub with
git filter-repoand force-push. - Secure your identity: protect the SSH private key, scope PATs, sign commits, and enable 2FA/passkeys.
- Enforce standards with branch protection / rulesets; let GitHub secret scanning + push protection, Dependabot, and code scanning (CodeQL) detect and block problems automatically.
- Harden Actions by pinning to SHAs and using least-privilege permissions.
- Treat Copilot as an accelerator, not an authority: review AI code for security, guard against prompt injection (including poisoned instruction/skill files), keep humans in the loop, and encode security rules into instructions/skills.
- Security is layered: prevent, detect, block, review, recover.
Sources
- About push protection — GitHub Docs
- Working with secret scanning and push protection — GitHub Docs
- About GitHub Advanced Security (secret scanning, CodeQL, Dependabot) — GitHub Docs
- Top GitHub Copilot security risks and how to mitigate them — Checkmarx
- GitHub Copilot prompt injection: RoguePilot & Rules File Backdoor — Medium
- Secure-coding Copilot instructions — github/awesome-copilot