From 5844208fbe930456fa26a34190a2916245c29e2f Mon Sep 17 00:00:00 2001
From: Sean Sinclair <146738689+sean-sinclair@users.noreply.github.com>
Date: Mon, 23 Mar 2026 12:04:52 +0000
Subject: [PATCH 1/2] feat: MDFormat added and executed to enforce 4 spaces
instead of 2 to properly show indents. Also ran /init to create the github
instructions.
---
.devcontainer/post-create.sh | 8 ++++
.github/copilot-instructions.md | 49 ++++++++++++++++++++++
.gitignore | 3 +-
.mdformat.toml | 3 ++
.pre-commit-config.yaml | 8 ++++
docs/toolbox/guidelines/secret-scanning.md | 40 +++++++++---------
pyproject.toml | 7 ++++
7 files changed, 97 insertions(+), 21 deletions(-)
create mode 100644 .github/copilot-instructions.md
create mode 100644 .mdformat.toml
create mode 100644 .pre-commit-config.yaml
diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh
index cbec3b80..9ca7efea 100755
--- a/.devcontainer/post-create.sh
+++ b/.devcontainer/post-create.sh
@@ -47,4 +47,12 @@ else
exit 1
fi
+# Install project with dev dependencies (pre-commit, mdformat)
+echo "[post-create] Installing project dev dependencies..."
+pip install -e ".[dev]"
+
+# Install pre-commit git hooks
+echo "[post-create] Installing pre-commit hooks..."
+pre-commit install
+
echo "[post-create] Setup complete."
\ No newline at end of file
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 00000000..4767c82a
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,49 @@
+# Copilot Instructions — Equinor AppSec
+
+## Project Overview
+
+MkDocs documentation site for Equinor Application Security guidelines. Pure documentation — no application code.
+
+- **Stack:** MkDocs Material theme, Python 3.12+, uv package manager
+- **Site URL:**
+- **Repo:**
+
+## Commands
+
+```bash
+uv sync # Install dependencies
+uv run mkdocs serve # Local dev server on http://localhost:8000
+uv run mkdocs build # Build static site to site/
+pip install -e ".[dev]" # Install dev tools (pre-commit, mdformat)
+pre-commit install # Set up git hooks
+pre-commit run --all-files # Run formatting on all files
+```
+
+## Content & Markdown Conventions
+
+- All documentation lives in `docs/`. Do not create files outside this directory for content.
+- MkDocs uses **Python-Markdown**, which requires **4-space indentation** for nested lists (not 2-space like GitHub Flavored Markdown).
+- `mdformat` with `mdformat-mkdocs` enforces this automatically via pre-commit hooks.
+- Use MkDocs Material features: `admonition` blocks, `pymdownx.details` for collapsible sections, `pymdownx.emoji` for icons.
+- Navigation structure is managed by `mkdocs-awesome-pages-plugin` — use `.pages` files or directory-level `index.md` for ordering.
+- The `git-revision-date-localized` plugin shows last-edit dates, so CI checkouts use `fetch-depth: 0`.
+- Theme overrides go in `overrides/` (custom `main.html`).
+
+## Deployments
+
+All three deploy automatically on push to `main`:
+
+1. **GitHub Pages** — `uv run mkdocs gh-deploy --force` ([gh-pages.yml](.github/workflows/gh-pages.yml))
+2. **Azure Static Web Apps** — builds `site/`, deploys via Azure token ([azure-static-webapp.yml](.github/workflows/azure-static-webapp.yml))
+3. **Backstage TechDocs** — generates and publishes to Azure Blob Storage ([backstage-techdocs.yml](.github/workflows/backstage-techdocs.yml))
+
+## Dev Container
+
+- Python 3 image with Node.js LTS, port 8000 forwarded
+- `post-create.sh` installs uv, dev dependencies, and pre-commit hooks
+- `postStartCommand` auto-launches `uv run mkdocs serve`
+
+## Contribution Workflow
+
+Fork → branch → add/edit docs → commit (pre-commit auto-formats) → push → PR to `main`.
+See [CONTRIBUTING.md](CONTRIBUTING.md) for full guidelines. Test links, run markdown linter, use a spell checker. Follow conventional commit messages for clarity.
diff --git a/.gitignore b/.gitignore
index 98ff0d17..bd700dce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ venv/
.venv/
.dccache
.DS_STORE
-.idea/
\ No newline at end of file
+.idea/
+*.egg-info/
\ No newline at end of file
diff --git a/.mdformat.toml b/.mdformat.toml
new file mode 100644
index 00000000..7aee18a5
--- /dev/null
+++ b/.mdformat.toml
@@ -0,0 +1,3 @@
+# mdformat configuration
+# Use 4-space indentation for nested lists (required by MkDocs / Python-Markdown)
+[plugin.mkdocs]
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..17ce3492
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,8 @@
+repos:
+ - repo: https://github.com/executablebooks/mdformat
+ rev: 0.7.21
+ hooks:
+ - id: mdformat
+ additional_dependencies:
+ - mdformat-mkdocs
+ files: ^docs/
diff --git a/docs/toolbox/guidelines/secret-scanning.md b/docs/toolbox/guidelines/secret-scanning.md
index 66626779..a633217e 100644
--- a/docs/toolbox/guidelines/secret-scanning.md
+++ b/docs/toolbox/guidelines/secret-scanning.md
@@ -5,20 +5,20 @@
This guideline aims to present relevant tools, some good practices for managing this risk, and what to do when we have messed up.
- [Secret Scanning](#secret-scanning)
- - [Recommended tools](#recommended-tools)
- - [GitHub Advanced Security: Secret Scanning](#github-advanced-security-secret-scanning)
- - [How to turn it on for your repository](#how-to-turn-it-on-for-your-repository)
- - [Global push protection](#global-push-protection)
- - [Where to scan for secrets in our SDLC](#where-to-scan-for-secrets-in-our-sdlc)
- - [🤯 What to do when we have messed up 🤯](#-what-to-do-when-we-have-messed-up-)
- - [Steps to mitigate a leak](#steps-to-mitigate-a-leak)
- - [Cleaning the git history](#cleaning-the-git-history)
- - [You are working locally, the secret is in the last commit, not pushed](#you-are-working-locally-the-secret-is-in-the-last-commit-not-pushed)
- - [You are working locally, the secret is beyond the last commit](#you-are-working-locally-the-secret-is-beyond-the-last-commit)
- - [How do we manage secrets in our dev environments?](#how-do-we-manage-secrets-in-our-dev-environments)
- - [A few *known* ways of managing secrets is](#a-few-known-ways-of-managing-secrets-is)
- - [A few *known* controls could be](#a-few-known-controls-could-be)
- - [What we should NOT do](#what-we-should-not-do)
+ - [Recommended tools](#recommended-tools)
+ - [GitHub Advanced Security: Secret Scanning](#github-advanced-security-secret-scanning)
+ - [How to turn it on for your repository](#how-to-turn-it-on-for-your-repository)
+ - [Global push protection](#global-push-protection)
+ - [Where to scan for secrets in our SDLC](#where-to-scan-for-secrets-in-our-sdlc)
+ - [🤯 What to do when we have messed up 🤯](#-what-to-do-when-we-have-messed-up-)
+ - [Steps to mitigate a leak](#steps-to-mitigate-a-leak)
+ - [Cleaning the git history](#cleaning-the-git-history)
+ - [You are working locally, the secret is in the last commit, not pushed](#you-are-working-locally-the-secret-is-in-the-last-commit-not-pushed)
+ - [You are working locally, the secret is beyond the last commit](#you-are-working-locally-the-secret-is-beyond-the-last-commit)
+ - [How do we manage secrets in our dev environments?](#how-do-we-manage-secrets-in-our-dev-environments)
+ - [A few *known* ways of managing secrets is](#a-few-known-ways-of-managing-secrets-is)
+ - [A few *known* controls could be](#a-few-known-controls-could-be)
+ - [What we should NOT do](#what-we-should-not-do)
## Recommended tools
@@ -75,9 +75,9 @@ We will mess up sooner or later. So be prepared, both as an individual developer
- Revoke/rotate the exposed secret - `THIS IS PRIORITY #1`
- Depending on risk consequence - considering contacting IT-Security for assistance
- - Contact Help Desk at (+47) 51 999 222
+ - Contact Help Desk at (+47) 51 999 222
- Clean the git history
- - If the repo is public/internal, consider making it `private` until the secrets are removed
+ - If the repo is public/internal, consider making it `private` until the secrets are removed
- Potentially inspect logs
- Do a retrospective with your team
@@ -88,10 +88,10 @@ This part can be very easy to super hard, it all depends on what, where and when
#### You are working locally, the secret is in the last commit, not pushed
- The secret is the only file in the last commit
- - Consider dropping the last commit, doing a `git reset --hard HEAD~1`
+ - Consider dropping the last commit, doing a `git reset --hard HEAD~1`
- The secret is not the only file in the last commit
- - Consider altering files and then amend the last commit
- - `git add [files]; git commit --amend`
+ - Consider altering files and then amend the last commit
+ - `git add [files]; git commit --amend`
#### You are working locally, the secret is beyond the last commit
@@ -128,7 +128,7 @@ Context matters, a lot. There are many different ways of handling secrets in dev
- Using .gitignore
- Using .dockerignore
- Using secret scanners
- - Not all tools are equal
+ - Not all tools are equal
- Limit access to secrets
- Use different secrets for different environments (dev, test, prod)
- Rotate secrets often, automate!
diff --git a/pyproject.toml b/pyproject.toml
index 56ea7830..7835a467 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -18,6 +18,13 @@ dependencies = [
"requests==2.32.5",
]
+[project.optional-dependencies]
+dev = [
+ "pre-commit",
+ "mdformat",
+ "mdformat-mkdocs",
+]
+
[project.urls]
Homepage = "https://github.com/equinor/appsec"
Documentation = "https://github.com/equinor/appsec"
From e66334f71e249c2997424f41e529bc4800cac855 Mon Sep 17 00:00:00 2001
From: Sean Sinclair <146738689+sean-sinclair@users.noreply.github.com>
Date: Mon, 23 Mar 2026 12:08:54 +0000
Subject: [PATCH 2/2] chore: Ran mdformat on existing docs.
---
docs/about/index.md | 2 +-
docs/experimental/git-signed-commits-old.md | 39 ++--
docs/experimental/git-signed-commits-young.md | 71 ++++---
.../1-new_security_champion.md | 6 +-
.../2-security_champion_activities.md | 2 +-
docs/security-champion/4-learning-platform.md | 1 +
.../5-belts-and-activities.md | 42 ++--
docs/security-champion/6-merch.md | 31 +--
docs/security-champion/index.md | 2 +
docs/toolbox/github-advanced-security/faq.md | 6 +-
.../toolbox/github-advanced-security/index.md | 12 +-
.../multi-branch-setup-dependabot.md | 21 +-
.../toolbox/github-advanced-security/setup.md | 71 ++++---
docs/toolbox/guidelines/authn-authz.md | 8 +-
docs/toolbox/guidelines/dockerfile-linting.md | 49 ++---
docs/toolbox/guidelines/logging-monitoring.md | 11 +-
.../guidelines/security-requirements.md | 101 ++++-----
.../security-screening-os-packages.md | 7 +-
docs/toolbox/index.md | 1 +
docs/toolbox/threat-modeling/ai-assisted.md | 9 +-
.../threat-modeling/getting-started.md | 7 +-
docs/toolbox/threat-modeling/index.md | 3 +-
docs/toolbox/threat-modeling/support.md | 1 +
docs/toolbox/tools/pre-commit-faq.md | 4 +-
docs/toolbox/tools/secure-coding.md | 1 +
docs/toolbox/tools/sharing-secrets.md | 17 +-
.../version-control/gh-actions-runners.md | 5 +-
docs/toolbox/version-control/git-github.md | 55 +++--
.../version-control/git-signed-commits.md | 34 ++-
uv.lock | 193 ++++++++++++++++++
30 files changed, 512 insertions(+), 300 deletions(-)
diff --git a/docs/about/index.md b/docs/about/index.md
index 3e5f3550..dfdb2cc0 100644
--- a/docs/about/index.md
+++ b/docs/about/index.md
@@ -4,7 +4,7 @@
**Always safe** is one of three pillar in the Equinor strategy. Safety in the digital world includes cyber security. Equinor has many software development teams (internals and partners) and we expect a growth in DevOps teams in the future. Modern software development adopts all aspects of cloud capabilities and thus there is also an increased information security and privacy risk.
->The purpose of the AppSec team is to reduce cyber security risk in Equinor's SDLC - Software Development Life Cycle (DevOps teams).
+> The purpose of the AppSec team is to reduce cyber security risk in Equinor's SDLC - Software Development Life Cycle (DevOps teams).
The primary target audience for the team is Equinor's software development community - aka. DevOps teams.
diff --git a/docs/experimental/git-signed-commits-old.md b/docs/experimental/git-signed-commits-old.md
index 2a9cf0d8..c31f40a9 100644
--- a/docs/experimental/git-signed-commits-old.md
+++ b/docs/experimental/git-signed-commits-old.md
@@ -1,22 +1,22 @@
# 🔏 Git Signed Commits: A Guide for Proper Folks Who Ain’t Raised by Wolves 🧐
-Now gather ‘round, you young scallywags, and let an old hand set you straight. Back in my day, a man’s word was his bond, and a signature was as good as gold. Nowadays, any two-bit scoundrel with a typewriter hooked up to a lightning machine can go fiddlin’ with your code, impersonatin’ honest folk, and muckin’ things up faster than a hog in a henhouse. 💨
+Now gather ‘round, you young scallywags, and let an old hand set you straight. Back in my day, a man’s word was his bond, and a signature was as good as gold. Nowadays, any two-bit scoundrel with a typewriter hooked up to a lightning machine can go fiddlin’ with your code, impersonatin’ honest folk, and muckin’ things up faster than a hog in a henhouse. 💨
-**That’s why you gotta sign your Git commits, just like an honest citizen signs a check down at the bank.** 🏦
+**That’s why you gotta sign your Git commits, just like an honest citizen signs a check down at the bank.** 🏦
-If you don’t, well… don’t come cryin’ when some rascal tampers with your code and the whole shebang crumbles like a biscuit in hot coffee. ☕🍪
+If you don’t, well… don’t come cryin’ when some rascal tampers with your code and the whole shebang crumbles like a biscuit in hot coffee. ☕🍪
## 📜 The Rules (And You Better Follow ‘Em, Y’hear?)
-✅ **Sign every Git commit you make.** A commit without a signature is like a contract written in invisible ink—ain’t worth a plugged nickel. 💰
+✅ **Sign every Git commit you make.** A commit without a signature is like a contract written in invisible ink—ain’t worth a plugged nickel. 💰
-✅ **Use SSH keys with a passphrase.** That’s a password, son. And make it a good one. "123456" ain’t a passphrase, it’s an invitation for hooligans.
+✅ **Use SSH keys with a passphrase.** That’s a password, son. And make it a good one. "123456" ain’t a passphrase, it’s an invitation for hooligans.
-✅ **Keep separate keys for signin’ and loggin’ in.** You wouldn’t use the same key for your house and your barn, would ya?
+✅ **Keep separate keys for signin’ and loggin’ in.** You wouldn’t use the same key for your house and your barn, would ya?
✅ **Make sure your branch rules require signed commits.** Otherwise, you might as well leave your front door wide open and hope the raccoons don’t move in.
-✅ **GitHub Codespaces might sign commits for ya, but don’t trust it more than your own two hands.** Machines are tricky little devils, and you never know when one’s gonna go haywire. 🤖🔥
+✅ **GitHub Codespaces might sign commits for ya, but don’t trust it more than your own two hands.** Machines are tricky little devils, and you never know when one’s gonna go haywire. 🤖🔥
## 🔐 SSH Key Signing (The Best Way for Most Folks)
@@ -27,6 +27,7 @@ Open up your terminal and generate a dedicated SSH key just for signing your Git
```bash
ssh-keygen -t ed25519 -f ~/.ssh/git_ssh_signing_key_1 -C "Created on , for [your_username] on github.com"
```
+
Make sure to give it a good passphrase, not something like password123. A weak passphrase is like lockin’ your door with a shoelace.
### 2. 🧠 Add the Key to Your SSH Agent
@@ -47,7 +48,7 @@ git config --global user.signingkey ~/.ssh/git_ssh_signing_key_1.pub
git config --global commit.gpgsign true
```
-Now Git will sign your commits automatically, no fuss, no muss.
+Now Git will sign your commits automatically, no fuss, no muss.
## 4. 🔍 Check That Your Commits Are Signed
@@ -73,20 +74,20 @@ cat ~/.ssh/git_ssh_signing_key_1.pub
To keep your project clean and avoid unwanted shenanigans, enforce signed commits:
-1. Go to your **GitHub repository**
-2. Navigate to **Settings → Branches**
-3. Under **Branch protection rules**, click **Add rule**
-4. Select your main branch (usually `main` or `master`)
-5. Check **Require signed commits**
+1. Go to your **GitHub repository**
+1. Navigate to **Settings → Branches**
+1. Under **Branch protection rules**, click **Add rule**
+1. Select your main branch (usually `main` or `master`)
+1. Check **Require signed commits**
-Now only signed commits can get through, like a proper gated community. 🚧
+Now only signed commits can get through, like a proper gated community. 🚧
-## 🎤 Final Words from an Old-Timer Who’s Seen Some Things
+## 🎤 Final Words from an Old-Timer Who’s Seen Some Things
-Now listen, I ain’t sayin’ you gotta follow these rules. You’re a grown person, you can do what you want. **But if you don’t sign your commits,** don’t come bellyachin’ when some no-good scoundrel impersonates you and sneaks nonsense into your project. 🎭
+Now listen, I ain’t sayin’ you gotta follow these rules. You’re a grown person, you can do what you want. **But if you don’t sign your commits,** don’t come bellyachin’ when some no-good scoundrel impersonates you and sneaks nonsense into your project. 🎭
-Back in my day, we didn’t have none of this "Git" business. We wrote our code by hand, uphill both ways, in the snow, and we liked it! 📝 **But if we had Git, you better believe we’d sign our work.**
+Back in my day, we didn’t have none of this "Git" business. We wrote our code by hand, uphill both ways, in the snow, and we liked it! 📝 **But if we had Git, you better believe we’d sign our work.**
-So quit yer lollygaggin’, set up signed commits, and keep your code cleaner than a Sunday suit. 👔
+So quit yer lollygaggin’, set up signed commits, and keep your code cleaner than a Sunday suit. 👔
-Or don’t. But don’t come cryin’ when your project turns into a three-ring circus. 🎪
\ No newline at end of file
+Or don’t. But don’t come cryin’ when your project turns into a three-ring circus. 🎪
diff --git a/docs/experimental/git-signed-commits-young.md b/docs/experimental/git-signed-commits-young.md
index be42dd5a..8b70616d 100644
--- a/docs/experimental/git-signed-commits-young.md
+++ b/docs/experimental/git-signed-commits-young.md
@@ -1,32 +1,33 @@
# 🔥 Yo, Code Warriors! 🧑💻 Let's Get Sigma with Signed Commits! 🦸♂️
-Alright, fam, it's time to level up your coding game and flex those sigma vibes. 💪 You know, being that lone wolf 🐺 who's got everything under control without breaking a sweat. Let's dive into why signing your commits is the ultimate sigma move and how to do it. 🚀
+Alright, fam, it's time to level up your coding game and flex those sigma vibes. 💪 You know, being that lone wolf 🐺 who's got everything under control without breaking a sweat. Let's dive into why signing your commits is the ultimate sigma move and how to do it. 🚀
----
+______________________________________________________________________
-## Why Signing Your Commits is Pure Sigma Energy
+## Why Signing Your Commits is Pure Sigma Energy
-- **🕵️♂️ Identity Flex (No Impostors Allowed)**
- Signing your commits is like stamping your unique seal of approval. It confirms that *you* made the changes, blocking any wannabe impostors from sneaking in malicious code.
+- **🕵️♂️ Identity Flex (No Impostors Allowed)**\
+ Signing your commits is like stamping your unique seal of approval. It confirms that *you* made the changes, blocking any wannabe impostors from sneaking in malicious code.
-- **🔒 Code Integrity = Sigma Grindset**
- A signed commit ensures your code remains untouched from the moment you commit. If anyone tries to mess with it afterward, the signature breaks, throwing up red flags.
+- **🔒 Code Integrity = Sigma Grindset**\
+ A signed commit ensures your code remains untouched from the moment you commit. If anyone tries to mess with it afterward, the signature breaks, throwing up red flags.
-- **🚫🔁 No Replay Shenanigans (Stay Ahead of the Game)**
- Signed commits stop attackers from reusing your legit commits in shady contexts, maintaining the flow and context of your code history.
+- **🚫🔁 No Replay Shenanigans (Stay Ahead of the Game)**\
+ Signed commits stop attackers from reusing your legit commits in shady contexts, maintaining the flow and context of your code history.
-- **🕵️♂️ Audit Like a Boss (Keep It 100)**
- With signed commits, tracking changes becomes a breeze, making audits smoother than a fresh jar of Skippy.
+- **🕵️♂️ Audit Like a Boss (Keep It 100)**\
+ With signed commits, tracking changes becomes a breeze, making audits smoother than a fresh jar of Skippy.
-- **🌟 Boosting Street Cred (Trust & Compliance)**
- For projects with strict standards, signed commits show you're playing by the rules, building trust among your crew and users.
+- **🌟 Boosting Street Cred (Trust & Compliance)**\
+ For projects with strict standards, signed commits show you're playing by the rules, building trust among your crew and users.
----
+______________________________________________________________________
-## How to Sign Your Commits and Embrace the Sigma Lifestyle
+## How to Sign Your Commits and Embrace the Sigma Lifestyle
-### 🛠️ Step 1: Generate Your SSH Key
-Fire up your terminal and punch in:
+### 🛠️ Step 1: Generate Your SSH Key
+
+Fire up your terminal and punch in:
```bash
ssh-keygen -t ed25519 -f ~/.ssh/git_ssh_signing_key_1 -C "Created on $(date), for $(whoami) on github.com"
@@ -34,41 +35,43 @@ ssh-keygen -t ed25519 -f ~/.ssh/git_ssh_signing_key_1 -C "Created on $(date), fo
This crafts a fresh SSH key for signing.
-💡 **Pro tip**: Add a passphrase. Don’t be that person who skips security.
+💡 **Pro tip**: Add a passphrase. Don’t be that person who skips security.
-### 🛠️ Step 2: Add Your Key to the SSH Agent 🤖
+### 🛠️ Step 2: Add Your Key to the SSH Agent 🤖
-Alright, so you've got your **fancy new SSH key**, but let's be real—typing your passphrase every time? That's **LAME.** Ain't nobody got time for that.
+Alright, so you've got your **fancy new SSH key**, but let's be real—typing your passphrase every time? That's **LAME.** Ain't nobody got time for that.
So let’s make your system remember your key like a **goldfish with a 500 IQ** 🧠🐠:
```bash
ssh-add ~/.ssh/git_ssh_signing_key_1
```
+
Now your key is locked in 🔐, and you can commit like an absolute **Chad** without re-entering your passphrase every 2 seconds.
Big W. ✅
-### 🏴☠️ Step 3: Tell Git to Use Your New Key
+### 🏴☠️ Step 3: Tell Git to Use Your New Key
-Yo, **Git ain't psychic**—you gotta tell it to start using that **top-tier** SSH key.
+Yo, **Git ain't psychic**—you gotta tell it to start using that **top-tier** SSH key.
-Hit it with this:
+Hit it with this:
```bash
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/git_ssh_signing_key_1.pub
git config --global commit.gpgsign true
```
+
This is like **giving Git a treasure map** 🏴☠️ so it knows where to find your key.
Now every commit is gonna be signed like a **true security giga-brain.** 🔥
-### 🔑 Step 4: Drop That Key into GitHub (Gotta Let the Squad Know)
+### 🔑 Step 4: Drop That Key into GitHub (Gotta Let the Squad Know)
-Your key is ready, but **GitHub doesn’t know you like that yet.**
+Your key is ready, but **GitHub doesn’t know you like that yet.**
-So let’s make it official:
+So let’s make it official:
```bash
cat ~/.ssh/git_ssh_signing_key_1.pub
@@ -80,20 +83,20 @@ cat ~/.ssh/git_ssh_signing_key_1.pub
💾 Paste it in and save! **BOOM**. Now GitHub recognizes you like an old friend. 😎
-### 🚧 Step 5: Protect Your Branches (Gatekeeping, But in a Good Way)
+### 🚧 Step 5: Protect Your Branches (Gatekeeping, But in a Good Way)
-Alright, **now we set up branch protection** to make sure NO UNSIGNED COMMITS GET IN. ❌🔓
+Alright, **now we set up branch protection** to make sure NO UNSIGNED COMMITS GET IN. ❌🔓
-This is like **putting a bouncer** in front of your code, checking IDs before letting commits into the club. 🕶️💪
+This is like **putting a bouncer** in front of your code, checking IDs before letting commits into the club. 🕶️💪
Set up **branch protection rules** so **ONLY signed commits** get through.
-No signature? 🚫 **Access DENIED.**
+No signature? 🚫 **Access DENIED.**
-### 🏆 Final Boss Move: Always Sign, Always Secure
+### 🏆 Final Boss Move: Always Sign, Always Secure
-Using signed commits isn’t just *cool*, it’s **mandatory** if you wanna keep your code safe.
+Using signed commits isn’t just *cool*, it’s **mandatory** if you wanna keep your code safe.
-Don’t be that dev who gets caught slipping—sign those commits and keep your repo locked down.
+Don’t be that dev who gets caught slipping—sign those commits and keep your repo locked down.
-💯 **Secure devs are W devs. Now go sign those commits.** 🚀🔥
\ No newline at end of file
+💯 **Secure devs are W devs. Now go sign those commits.** 🚀🔥
diff --git a/docs/security-champion/1-new_security_champion.md b/docs/security-champion/1-new_security_champion.md
index 68250bf1..20227dd8 100644
--- a/docs/security-champion/1-new_security_champion.md
+++ b/docs/security-champion/1-new_security_champion.md
@@ -10,7 +10,8 @@ This is where the fun begins.
- 🚀 Explore our [learning platform](./4-learning-platform.md)
!!! question "🎮 Challenge: Hack your way to Merch"
- _Hack_ your way to victory and you might just earn some **socks**!
+
+ _Hack_ your way to victory and you might just earn some **socks**!\
👉 [Start the game and join the hall of champions](https://forms.microsoft.com/r/cLRPzRtPGQ)
## Add Security Champion events to your calendar 📅
@@ -29,4 +30,5 @@ You can also find a calendar of events on [SharePoint](https://statoilsrm.sharep
- [#AppSec](https://equinor.slack.com/archives/CMM6FSW5V): Information regarding AppSec
!!! info
- Most general information and questions should be posted in [#appsec](https://equinor.slack.com/archives/CMM6FSW5V) so everyone in Equinor has access to it and can participate! Dropping a ```@appsecteam``` in this channel will get our attention immediately.
+
+ Most general information and questions should be posted in [#appsec](https://equinor.slack.com/archives/CMM6FSW5V) so everyone in Equinor has access to it and can participate! Dropping a `@appsecteam` in this channel will get our attention immediately.
diff --git a/docs/security-champion/2-security_champion_activities.md b/docs/security-champion/2-security_champion_activities.md
index 90541f9f..9268dcaa 100644
--- a/docs/security-champion/2-security_champion_activities.md
+++ b/docs/security-champion/2-security_champion_activities.md
@@ -1,4 +1,3 @@
-
# Security Champion Activities 🦸♀️
## Introduce yourself
@@ -10,6 +9,7 @@ Say hello in the [Security Champion channel](https://equinor.slack.com/archives/
We have created a few guidelines. Please check them out in our [toolbox](../toolbox/index.md) and consider implementing them in your projects where it makes sense.
!!! info
+
Feedback is good, so if you have any, feel free to contact us, or even create a PR on our [github repo](https://github.com/equinor/appsec/)!
## Threat Modelling activities
diff --git a/docs/security-champion/4-learning-platform.md b/docs/security-champion/4-learning-platform.md
index 07ae9379..1ae75fb4 100644
--- a/docs/security-champion/4-learning-platform.md
+++ b/docs/security-champion/4-learning-platform.md
@@ -13,6 +13,7 @@ We have a secure coding learning platform. You as a champion are a perfect match
- Learn things that may help advance your career!
!!! info
+
You can gain unique merch based on your belt level!
There is a wide arrangement of subjects, and you can do them all if you wish! So there are tracks for:
diff --git a/docs/security-champion/5-belts-and-activities.md b/docs/security-champion/5-belts-and-activities.md
index 2d2f18bd..f8ea7fa5 100644
--- a/docs/security-champion/5-belts-and-activities.md
+++ b/docs/security-champion/5-belts-and-activities.md
@@ -7,16 +7,18 @@ We are launching a new activity system, previously only available to the [learni
### Secure Code learning platform
!!! Info "If you are using the Secure Code learning platform"
+
Nothing changes except where you register activities! It pays to learn, and you get to upskill your knowledge as well, Win Win!
-There are 5 belts you can achieve.```White```, ```Yellow```, and ```Green``` belt are achievable from only learning through the platform. More on the merch you can get from the different belts [here](./6-merch.md#merch-for-belts). The ```brown``` and ```black``` belts are something special. They require you to complete activities that give back to the Security Champion network.
+There are 5 belts you can achieve.`White`, `Yellow`, and `Green` belt are achievable from only learning through the platform. More on the merch you can get from the different belts [here](./6-merch.md#merch-for-belts). The `brown` and `black` belts are something special. They require you to complete activities that give back to the Security Champion network.
!!! Danger " "
- The ```brown``` and ```black``` belts are special and require you to complete activities normally in the Security Champion network. Check them out below!
-- To start earning the ```brown``` belt: Complete the three belts from the learning platform (```white```, ```yellow```, and ```green```)
-- To gain the ```brown``` belt: Complete ```3``` activities from the list [below](./5-belts-and-activities.md#activities)
-- To gain the ```black``` belt: Complete ```6``` new activities, for a total of ```9```
+ The `brown` and `black` belts are special and require you to complete activities normally in the Security Champion network. Check them out below!
+
+- To start earning the `brown` belt: Complete the three belts from the learning platform (`white`, `yellow`, and `green`)
+- To gain the `brown` belt: Complete `3` activities from the list [below](./5-belts-and-activities.md#activities)
+- To gain the `black` belt: Complete `6` new activities, for a total of `9`
### Others
@@ -25,11 +27,11 @@ Each belt requires X amount of approved activities.
#### Activities per belt
-|White|Yellow|Green|Brown|Black|**Total**|
-|:---:|:---:|:---:|:---:|:---:|:---:|
-|1|2|3|4|5|**15**|
+| White | Yellow | Green | Brown | Black | **Total** |
+| :---: | :----: | :---: | :---: | :---: | :-------: |
+| 1 | 2 | 3 | 4 | 5 | **15** |
-Ex: Yellow is ```3``` completed activities, ```1``` for ```white```, ```2``` for ```yellow```
+Ex: Yellow is `3` completed activities, `1` for `white`, `2` for `yellow`
### Registration
@@ -40,6 +42,7 @@ The process for registration has been simplified! All you have to do is:
- We will double-check, and if everything is A-OK, you get an e-mail!
!!! info
+
One activity is one submission.
## Activities
@@ -47,19 +50,20 @@ The process for registration has been simplified! All you have to do is:
Please help contribute with useful activities that make sense in Equinor context for our Security Champions.
- Talk briefly about your project/challenges in regards to security in the Morning Coffee
-- Facilitate a ```Threat Modeling``` session for your team/project
-- Share a write-up of a ```security activity``` you/your team did on the AppSec Slack
+- Facilitate a `Threat Modeling` session for your team/project
+- Share a write-up of a `security activity` you/your team did on the AppSec Slack
- Speak at a seminar/meetup
-- Have a Security Champion from another team join/review your ```threat model```
-- Join/review another team's ```threat model```
+- Have a Security Champion from another team join/review your `threat model`
+- Join/review another team's `threat model`
- Attend a meetup
- Hold a security moment at a seminar/meetup
-- Set up ```Secret scanning``` for your project [using our guidelines](../toolbox/guidelines/secret-scanning.md)
-- Attend a Security Journey tournament ```(learning platform)```
-- Gain three ```white``` belts ```(learning platform)```
-- Gain two ```yellow``` belts ```(learning platform)```
-- Gain two ```green``` belts ```(learning platform)```
-- Go through a Security Journey task in the Morning Coffee ```(learning platform)```
+- Set up `Secret scanning` for your project [using our guidelines](../toolbox/guidelines/secret-scanning.md)
+- Attend a Security Journey tournament `(learning platform)`
+- Gain three `white` belts `(learning platform)`
+- Gain two `yellow` belts `(learning platform)`
+- Gain two `green` belts `(learning platform)`
+- Go through a Security Journey task in the Morning Coffee `(learning platform)`
!!! note
+
The list of activities will change based on your feedback.
diff --git a/docs/security-champion/6-merch.md b/docs/security-champion/6-merch.md
index bcbd617b..3b8725c5 100644
--- a/docs/security-champion/6-merch.md
+++ b/docs/security-champion/6-merch.md
@@ -4,16 +4,16 @@ Merch is an important tool in building a security culture. We need to be visible
As SCN age, we will have different merch come and go. Below we have a record of some of the selections we have given out. Some of them are out of stock, some are in stock, you never know! Should we get keep inventory? Probably...
-- ```Stickers```: A large assortment of stickers to help show that you are the voice of security! With the number of stickers we have, you will get a proper workout while carrying your laptop
-- ```Black Hoodie```: Display your black belt to everyone in the vicinity. You are a true champion
-- ```Socks```: Socks decorated with The Security Champion shields! Would they have protected Achilles? Maybe not. Will they protect you from cyber criminals? Who knows! But you will certainly look stylish while being hacked!
-- ```Book (Alice and Bob Learn Application Security)```: Complete the challenge at [this form](https://forms.microsoft.com/r/cLRPzRtPGQ)
-- ```AbbSack```: You have to see it to believe it! Keep your items _secure_ while traveling
-- ```BallSec```: Those pesky attackers are giving you a lot of stress? Relieve it with your branded stress ball!
-- ```Lanyard```: Start your belt journey to black belt here with a stylish card holder
-- ```Pins```: Decorate your lanyard to display your belt level to the world
-- ```Christmas ornament```: No Christmas tree is secure without your own Security Champion Christmas tree ornament. Is it because the christmas tree is offline? Most likely
-- ```S.W.A.T. (Small Work Addictive Thing)```: the latest and freshest within fidget toy technology. Meetings will never be the same! This exists in multiple colors. Gotta cath'em all!
+- `Stickers`: A large assortment of stickers to help show that you are the voice of security! With the number of stickers we have, you will get a proper workout while carrying your laptop
+- `Black Hoodie`: Display your black belt to everyone in the vicinity. You are a true champion
+- `Socks`: Socks decorated with The Security Champion shields! Would they have protected Achilles? Maybe not. Will they protect you from cyber criminals? Who knows! But you will certainly look stylish while being hacked!
+- `Book (Alice and Bob Learn Application Security)`: Complete the challenge at [this form](https://forms.microsoft.com/r/cLRPzRtPGQ)
+- `AbbSack`: You have to see it to believe it! Keep your items _secure_ while traveling
+- `BallSec`: Those pesky attackers are giving you a lot of stress? Relieve it with your branded stress ball!
+- `Lanyard`: Start your belt journey to black belt here with a stylish card holder
+- `Pins`: Decorate your lanyard to display your belt level to the world
+- `Christmas ornament`: No Christmas tree is secure without your own Security Champion Christmas tree ornament. Is it because the christmas tree is offline? Most likely
+- `S.W.A.T. (Small Work Addictive Thing)`: the latest and freshest within fidget toy technology. Meetings will never be the same! This exists in multiple colors. Gotta cath'em all!
## How to get merch
@@ -27,19 +27,20 @@ As SCN age, we will have different merch come and go. Below we have a record of
Since we are launching a new belt system connected to the secure coding platform, we need fresh merch! Below is a list of what you can get at the different belt systems. The items will be shipped via mail unless you can pick it up in the building (Stavanger Forus Øst).
-- ```White``` belt:
+- `White` belt:
- Your very own white lanyard decorated with placeholders for those hard-earned pins.
-- ```Yellow``` belt:
+- `Yellow` belt:
- A yellow pin to hang on your newly acquired lanyard. Be proud!
- A yellow S.W.A.T. to showcase your advancement within security. It will be your best friend in meetings.
-- ```Green``` belt:
+- `Green` belt:
- A green pin to display your advanced knowledge of secure coding.
-- ```Brown``` belt:
+- `Brown` belt:
- A brown pin to show that you are no rookie in terms of being one of the top-notch champions in our network.
- A brown S.W.A.T. that will become your best friend when focusing.
-- ```Black``` belt:
+- `Black` belt:
- A black pin. Your final step to look like an overly decorated army general. You can retire happy as your life-long goal of contributing to Equinor's Security Champion Network is finally complete _for now_.
- Hoodie: Now that you are all medal'ed out, the last thing you need is that sweet warm hacker hoodie to put the . over the i.
!!! note
+
Merch will "build up" and be shipped in bulk, normally every 2/3 belt levels in order to avoid too much shipping work.
diff --git a/docs/security-champion/index.md b/docs/security-champion/index.md
index 341ed028..1ed3ba3e 100644
--- a/docs/security-champion/index.md
+++ b/docs/security-champion/index.md
@@ -5,6 +5,7 @@ You are probably wondering what a Security Champion is in Equinor context and wh
A Security Champion in our context is a person who has a interest in security and want to expand on this interest. The Security Champions Network (SCN) is a network where people and security is in the center.
!!! info
+
You do not need to have any security knowledge to join, but the eagerness to learn and share
## What do we expect from you?
@@ -17,6 +18,7 @@ A Security Champion in our context is a person who has a interest in security an
- Share what you learn with your team
!!! info
+
A Security Champion is the voice of security, and security is a _team effort_.
You as a champion are the heart of this network. We know time might be tight, but we greatly appreciate all participation.
diff --git a/docs/toolbox/github-advanced-security/faq.md b/docs/toolbox/github-advanced-security/faq.md
index 37745ddd..bfb2c9f2 100644
--- a/docs/toolbox/github-advanced-security/faq.md
+++ b/docs/toolbox/github-advanced-security/faq.md
@@ -16,11 +16,11 @@ We have scheduled five onboarding sessions in October, all Security Champions ha
When you fork a repository, Advanced Security features are not automatically enabled on the fork.
This is because security settings (such as code scanning, secret protection, etc.) are managed per repository,
-and forking does not inherit these settings by default.
+and forking does not inherit these settings by default.
-If you want Advanced Security features in your fork, you need to enable them manually.
+If you want Advanced Security features in your fork, you need to enable them manually.
-We have also observed that pull requests to the upstream repository require that a successful CodeQL scan is performed on the fork.
+We have also observed that pull requests to the upstream repository require that a successful CodeQL scan is performed on the fork.
## Dependabot? CodeQL? What are these?
diff --git a/docs/toolbox/github-advanced-security/index.md b/docs/toolbox/github-advanced-security/index.md
index 5db316e6..778849a1 100644
--- a/docs/toolbox/github-advanced-security/index.md
+++ b/docs/toolbox/github-advanced-security/index.md
@@ -7,6 +7,7 @@ We have been collecting your questions on GHAS and tried answering them in the [
Check out [this repository](https://github.com/equinor/appsec-ghas-examples) for our examples on advanced GHAS setups.
!!! note "TL;DR"
+
🗓️ GHAS is enabled for all repositories
🗣️ Let us know in the [#appsec](https://equinor.enterprise.slack.com/archives/CMM6FSW5V) channel if you have any questions
@@ -17,9 +18,10 @@ Check out [this repository](https://github.com/equinor/appsec-ghas-examples) for
**Container Scanning**: Please note that GHAS does not currently offer container scanning solutions, we have explored other alternatives like Trivy so please reach out if you have any questions.
-**GHAS Features**: We encourage you to enable additional GHAS features like code scanning with CodeQL
+**GHAS Features**: We encourage you to enable additional GHAS features like code scanning with CodeQL
!!! info
+
The information that follows offers guidance to internal Equinor teams and might not apply to the broader public.
## Compliance with Governance
@@ -31,7 +33,7 @@ To comply with [TR2375](https://docmap.equinor.com/Docmap/page/doc/dmDocIndex.ht
In the Equinor GitHub Organization:
-- **Secret Scanning** is enabled by default for all repositories
+- **Secret Scanning** is enabled by default for all repositories
- **Dependency Scanning** is enabled by default for most repositories
- **Code Scanning** must be set up manually by a repository Admin
@@ -42,10 +44,11 @@ In the Equinor GitHub Organization:
1. Enable CodeQL (SAST)
CodeQL is not enabled by default. A repository Admin must:
- Navigate to Settings → Advanced Security → CodeQL analysis → Set up → Default
+
- If the default setup fails, you will see a message under Security → Code Scanning.
- In that case, use an advanced setup. Examples of this are available in the [appsec-ghas-examples repository](https://github.com/equinor/appsec-ghas-examples).
+ In that case, use an advanced setup. Examples of this are available in the [appsec-ghas-examples repository](https://github.com/equinor/appsec-ghas-examples).
- 
+ 
!!! warning
@@ -60,4 +63,3 @@ Sometimes workflows fail due to automatic dependency submission when Dependabot
- First, try following [this guide](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/configuring-access-to-private-registries-for-dependabot#configuring-private-registries)
- If that doesn't solve it, request an exception using [this form](https://forms.cloud.microsoft/e/1Zp1vBiN7Q).
-
diff --git a/docs/toolbox/github-advanced-security/multi-branch-setup-dependabot.md b/docs/toolbox/github-advanced-security/multi-branch-setup-dependabot.md
index 130eed4c..bec545e4 100644
--- a/docs/toolbox/github-advanced-security/multi-branch-setup-dependabot.md
+++ b/docs/toolbox/github-advanced-security/multi-branch-setup-dependabot.md
@@ -2,20 +2,24 @@
To set up Dependabot for multiple branches in your repository, use the target-branch property in the `dependabot.yml` file. Example:
- 1. **Enable Dependabot**:
- - Navigate to your repository's **Settings**.
- - Under the "Security" section, click **Advanced Security**.
- - Enable Dependabot alerts, security updates, and version updates.
+1. **Enable Dependabot**:
- 2. **Create or Edit the `dependabot.yml` File**:
- - In your repository, navigate to the `/.github` directory.
- - Create or edit the `dependabot.yml` file to specify configurations for each branch.
+ - Navigate to your repository's **Settings**.
+ - Under the "Security" section, click **Advanced Security**.
+ - Enable Dependabot alerts, security updates, and version updates.
+
+1. **Create or Edit the `dependabot.yml` File**:
+
+ - In your repository, navigate to the `/.github` directory.
+ - Create or edit the `dependabot.yml` file to specify configurations for each branch.
+
+1. **Define Updates for Multiple Branches**:
-3. **Define Updates for Multiple Branches**:
- Use the `updates` key in the `dependabot.yml` file to define configurations for each branch.
- Replace `package-ecosystem`, `directory`, `schedule`, and `target-branch` with values specific to your repository.
Example:
+
```yaml
version: 2
updates:
@@ -30,4 +34,3 @@ updates:
interval: "weekly"
target-branch: "development"
```
-
diff --git a/docs/toolbox/github-advanced-security/setup.md b/docs/toolbox/github-advanced-security/setup.md
index dc2f9b5f..7f2c06a8 100644
--- a/docs/toolbox/github-advanced-security/setup.md
+++ b/docs/toolbox/github-advanced-security/setup.md
@@ -1,7 +1,7 @@
-
# GitHub Advanced Security Enablement and Setup
!!! important
+
- Internal and Private Repositories must wait until **1st of October** before enabling GHAS.
- You must be a repository Admin to proceed.
@@ -16,36 +16,34 @@ To start off, navigate to your repository and under Settings -> Security -> Adva
Below you can find a table summary of the options we recommend to enable:
-
-| **Feature** | **Options** | **Recommendation** |
-| ------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------- |
-| [**Secret Protection** (1)](#1-secret-protection) | | ✅ Yes |
-| | [Validity Checks (2)](#2-validity-checks) | 🤷 Optional |
-| | [Non-provider Patterns (3)](#3-non-provider-patterns) | ✅ Yes |
-| | [Scan for Generic Passwords (4)](#4-scan-for-generic-passwords) | ✅ Yes |
-| | [Push-Protection (5)](#5-push-protection) | ✅ Yes |
-| | [Who can bypass push protection (6)](#6-who-can-bypass-push-protection) | 👤 Anyone (default) |
-| | [Prevent Alert Dismissal (7)](#7-prevent-alert-dismissal) | ❌ No |
-| | [Custom Patterns (8)](#8-custom-patterns) | 🤷 Optional |
-| [**Code Security** (1)](#1-code-security) | | ✅ Yes |
-| | [CodeQL analysis (2)](#2-codeql-analysis) | ⚙️ Default setup |
-| | [Other tools (3)](#3-other-tools) | 🤷 Optional |
-| | [Copilot Autofix (4)](#4-copilot-autofix) | ✅ Yes |
-| | [Copilot Autofix for Third Party Tools (5)](#5-copilot-autofix-for-third-party-tools)| 🤷 Optional |
-| | [Prevent Direct Alert Dismissal (6)](#6-prevent-direct-alert-dismissal) | ❌ No |
-| | [Protection Rules (7)](#7-protection-rules) | ⚙️ Default setup |
-| | [Private Vulnerability Reporting (8)](#8-private-vulnerability-reporting) | 🤷 Optional |
-| [**Dependency Graph** (1)](#1-dependency-graph) | | ✅ Yes |
-| | [Automatic dependency submission (2)](#2-automatic-dependency-submission) | ✅ Yes |
-| | [Dependabot Alerts (3)](#3-dependabot-alerts) | ✅ Yes |
-| | [Dependabot Security Updates (4)](#4-dependabot-security-updates) | ✅ Yes |
-| | [Grouped Security Updates (5)](#5-grouped-security-updates) | 🤷 Optional |
-| | [Dependabot Version Updates (6)](#6-dependabot-version-updates) | ✅ Yes\* |
-| | [Dependabot on Action Runners (7)](#7-dependabot-on-action-runners) | ✅ Yes |
-| | [Dependabot on Self-hosted Runners (8)](#8-dependabot-on-self-hosted-runners) | ❌ No |
-
-\* Please see important note and warning on [Dependabot Version Updates (6)](#6-dependabot-version-updates) section
-
+| **Feature** | **Options** | **Recommendation** |
+| ------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------- |
+| [**Secret Protection** (1)](#1-secret-protection) | | ✅ Yes |
+| | [Validity Checks (2)](#2-validity-checks) | 🤷 Optional |
+| | [Non-provider Patterns (3)](#3-non-provider-patterns) | ✅ Yes |
+| | [Scan for Generic Passwords (4)](#4-scan-for-generic-passwords) | ✅ Yes |
+| | [Push-Protection (5)](#5-push-protection) | ✅ Yes |
+| | [Who can bypass push protection (6)](#6-who-can-bypass-push-protection) | 👤 Anyone (default) |
+| | [Prevent Alert Dismissal (7)](#7-prevent-alert-dismissal) | ❌ No |
+| | [Custom Patterns (8)](#8-custom-patterns) | 🤷 Optional |
+| [**Code Security** (1)](#1-code-security) | | ✅ Yes |
+| | [CodeQL analysis (2)](#2-codeql-analysis) | ⚙️ Default setup |
+| | [Other tools (3)](#3-other-tools) | 🤷 Optional |
+| | [Copilot Autofix (4)](#4-copilot-autofix) | ✅ Yes |
+| | [Copilot Autofix for Third Party Tools (5)](#5-copilot-autofix-for-third-party-tools) | 🤷 Optional |
+| | [Prevent Direct Alert Dismissal (6)](#6-prevent-direct-alert-dismissal) | ❌ No |
+| | [Protection Rules (7)](#7-protection-rules) | ⚙️ Default setup |
+| | [Private Vulnerability Reporting (8)](#8-private-vulnerability-reporting) | 🤷 Optional |
+| [**Dependency Graph** (1)](#1-dependency-graph) | | ✅ Yes |
+| | [Automatic dependency submission (2)](#2-automatic-dependency-submission) | ✅ Yes |
+| | [Dependabot Alerts (3)](#3-dependabot-alerts) | ✅ Yes |
+| | [Dependabot Security Updates (4)](#4-dependabot-security-updates) | ✅ Yes |
+| | [Grouped Security Updates (5)](#5-grouped-security-updates) | 🤷 Optional |
+| | [Dependabot Version Updates (6)](#6-dependabot-version-updates) | ✅ Yes\* |
+| | [Dependabot on Action Runners (7)](#7-dependabot-on-action-runners) | ✅ Yes |
+| | [Dependabot on Self-hosted Runners (8)](#8-dependabot-on-self-hosted-runners) | ❌ No |
+
+\* Please see important note and warning on [Dependabot Version Updates (6)](#6-dependabot-version-updates) section
## [Secret Protection](https://docs.github.com/en/code-security/secret-scanning/introduction/about-secret-scanning)
@@ -54,7 +52,7 @@ It gives a brief explanation of each of the options available and our recommenda
The goal is to detect exposed secrets early on thus reducing risk in the SDLC.
-![Secret Protection Options]()
+
### (1) Secret Protection
@@ -62,7 +60,7 @@ Enabling Secret Protection will enable secret scanning on the repository. Upon e
✅ Secret Protection should be enabled.
-### (2) [Validity checks](https://docs.github.com/en/enterprise-cloud@latest/code-security/secret-scanning/introduction/supported-secret-scanning-patterns#supported-secrets)
+### (2) [Validity checks](https://docs.github.com/en/enterprise-cloud@latest/code-security/secret-scanning/introduction/supported-secret-scanning-patterns#supported-secrets)
Enhances secret scanning by verifying whether a detected secret is actually valid. When enabled, GitHub forwards the detected secret to the relevant provider (for example, an Azure secret would be sent to Microsoft) to confirm if it's active. Enabling this feature provides teams with insight into whether a secret is live and exploitable. However, it's important to note that the third party may, in the future, automatically revoke or disable the secret once its validity is confirmed. For instance, if Azure confirms a secret is active, it might proactively disable it to prevent abuse.
@@ -111,7 +109,7 @@ Lets you define your own regex rules to detect secrets specific to your team or
Code Security manages the configuration of GitHub’s Static Application Security Testing (SAST) tool also known as CodeQL, which runs via GitHub Actions to scan your repository for security issues in the code.
-![Code Security Options]()
+
### (1) [Code Security](https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning)
@@ -126,6 +124,7 @@ This has be configured for code security to work. Once enabled a GitHub action w
⚙️ We recommend choosing the [default setup](https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning) for CodeQL Analysis, which works well for most repositories and languages.
!!! warning
+
After CodeQL has run for the first time, we recommend you check the Security tab of your repo and then navigate to Code Scanning.
If the default setup is not working properly you will see the following warning. Check the [tool status page](https://docs.github.com/en/code-security/code-scanning/managing-your-code-scanning-configuration/about-the-tool-status-page) to get more information about the issue.
If you encounter this get in touch with us.
@@ -150,6 +149,7 @@ It’s a helpful way to quickly address straight forward issues but we always re
Copilot Autofix also works with third-party security tools, not just CodeQL. It will generate potential fixes based on issues identified by [other tools](#3-other-tools) installed in the repository.
!!! note
+
Currently this feature only supports ESLint.
ESLint has to be configured in the repository through the [other tools option](#3-other-tools).
@@ -200,6 +200,7 @@ This allows GitHub to detect and report dependencies automatically. It helps kee
Notify you when vulnerabilities are found in your dependencies. Alerts will appear in the GitHub UI under the Security Dashboard, and can also be sent by email and filtered to a separate folder (e.g. by using the "security" keyword).
!!! note
+
Slack notifications are not available with GHAS. We recommend setting up email notifications or configuring GitHub notification rules to make sure your team stays up to date on security issues.
✅ We recommend enabling Dependabot alerts.
@@ -221,10 +222,12 @@ This option allows Dependabot to combine multiple security updates into a single
Allows Dependabot to open pull requests to keep your dependencies up to date, even if there are no known vulnerabilities. This helps reduce technical debt and makes it easier to stay updated to the latest versions.
!!! note
+
If you're using GitHub Actions with SHA versioning, this option allows Dependabot to automatically update your GitHub Actions.
If you're using GitHub Actions with semantic versioning, this option is optional, but we still recommend enabling it to maintain good security practices by keeping all your dependencies updated to latest versions.
!!! warning
+
Dependabot will suggest upgrading dependencies whenever new versions are available. Enabling this option can lead to a significant number of pull requests generated, requiring the team to actively manage them.
It's beneficial to have a well defined process in place to handle these PRs effectively.
diff --git a/docs/toolbox/guidelines/authn-authz.md b/docs/toolbox/guidelines/authn-authz.md
index eefec68b..9d9948bb 100644
--- a/docs/toolbox/guidelines/authn-authz.md
+++ b/docs/toolbox/guidelines/authn-authz.md
@@ -9,9 +9,9 @@ The scope for this guideline is web application and api's.
- Microsoft's [Identity Platform](https://learn.microsoft.com/en-us/entra/identity-platform/) is the primary component in our set-up.
- We use Microsoft Entra ID (was Azure AD) as our primary identity provider and authorization server.
- We use oAuth2 to facilitate authorization
- - [Authorization Code Flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow) is often the preferred flow
- - We avoid the "Implicit Grant Flow"
- - Public Clients should use Auth Code Flow with PKCE
+ - [Authorization Code Flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow) is often the preferred flow
+ - We avoid the "Implicit Grant Flow"
+ - Public Clients should use Auth Code Flow with PKCE
- We use OpenId Connect (OIDC) as an authentication layer on top of oAuth2
- We use SAML2/OIDC for federation
- We refer to the MS Identity Platform for implementation guidelines (guides for web, api, desktop, mobile, service, deamon/script, example code, sdk and libraries)
@@ -37,5 +37,5 @@ The scope for this guideline is web application and api's.
## Equinor "quirks"
- We use conditional access for most our accounts and identities. Parts of this is implemented as "managed device"
- - The [Device Code Flow](https://datatracker.ietf.org/doc/html/rfc8628) will not work on a managed device.
+ - The [Device Code Flow](https://datatracker.ietf.org/doc/html/rfc8628) will not work on a managed device.
- The [Omnia Docs](https://docs.omnia.equinor.com/services/data/concept/authentication-the-basics/) contains important information on AuthN, AuthZ, Entra ID, consent etc.
diff --git a/docs/toolbox/guidelines/dockerfile-linting.md b/docs/toolbox/guidelines/dockerfile-linting.md
index 9802222d..3697180a 100644
--- a/docs/toolbox/guidelines/dockerfile-linting.md
+++ b/docs/toolbox/guidelines/dockerfile-linting.md
@@ -13,8 +13,8 @@ For this guide, we focus on the linter Hadolint.
## Hadolint
From [hadolint](https://github.com/hadolint/hadolint):
-> A smarter Dockerfile linter that helps you build [best practice](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices) Docker images. The linter parses the Dockerfile into an AST and performs rules on top of the AST. It stands on the shoulders of [ShellCheck](https://github.com/koalaman/shellcheck) to lint the Bash code inside RUN instructions.
+> A smarter Dockerfile linter that helps you build [best practice](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices) Docker images. The linter parses the Dockerfile into an AST and performs rules on top of the AST. It stands on the shoulders of [ShellCheck](https://github.com/koalaman/shellcheck) to lint the Bash code inside RUN instructions.
### CI/CD
@@ -22,29 +22,30 @@ A great way to ensure that any changes being brought into your repository comply
??? Note "Read more"
-
- Below is an example workflow that will run linting on our Dockerfiles whenever there is a pull request that contains changes to any Dockerfiles.
-
- name: Lint Dockerfile
- permissions:
- contents: read
- run-name: ${{ github.actor }} is running linting on Dockerfiles
- on:
- pull_request:
- branches: [main] # Limit the workflow to only run on pull requests made to the main branch
- paths:
- - "**Dockerfile" # Ensure that we trigger on any Dockerfile changes
- jobs:
- lint-docker:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout code
- uses: actions/checkout@v5
- - name: Linting Dockerfiles
- uses: hadolint/hadolint-action@v3.2.0
- with:
- failure-threshold: info # Sets the level at which the step will fail, default is info, other levels are warning and error
- recursive: true # This enables us to check all Dockerfiles
+```
+Below is an example workflow that will run linting on our Dockerfiles whenever there is a pull request that contains changes to any Dockerfiles.
+
+ name: Lint Dockerfile
+ permissions:
+ contents: read
+ run-name: ${{ github.actor }} is running linting on Dockerfiles
+ on:
+ pull_request:
+ branches: [main] # Limit the workflow to only run on pull requests made to the main branch
+ paths:
+ - "**Dockerfile" # Ensure that we trigger on any Dockerfile changes
+ jobs:
+ lint-docker:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+ - name: Linting Dockerfiles
+ uses: hadolint/hadolint-action@v3.2.0
+ with:
+ failure-threshold: info # Sets the level at which the step will fail, default is info, other levels are warning and error
+ recursive: true # This enables us to check all Dockerfiles
+```
### Run locally
diff --git a/docs/toolbox/guidelines/logging-monitoring.md b/docs/toolbox/guidelines/logging-monitoring.md
index a4d8416a..32979718 100644
--- a/docs/toolbox/guidelines/logging-monitoring.md
+++ b/docs/toolbox/guidelines/logging-monitoring.md
@@ -1,7 +1,8 @@
# Web Application Logging and Monitoring
!!! info
- This is a work in progress. Any thoughts? Reach out to us on [Slack](https://app.slack.com/client/T02JL00JU/CMM6FSW5V) or e-mail at ``appsec[at]equinor.com``
+
+ This is a work in progress. Any thoughts? Reach out to us on [Slack](https://app.slack.com/client/T02JL00JU/CMM6FSW5V) or e-mail at `appsec[at]equinor.com`
**Target Audience** - Application teams maintaining or implementing web applications.
@@ -33,9 +34,9 @@ Security logging is a huge topic, so we will limit our scope. You should not log
#### Examples
-- ```Scans:``` Somebody keeps poking our end-points, testing known end-points, or trying different malicious inputs. A fitting mitigation for this threat can be throttling / rate limiting. Logging would not properly mitigate the issue, because the application team would be unable to take action to stop such an attack. Such an attack would be up to CDC to detect and respond
-- ```Access control:``` Assume an application holds data that is owned by specific users, and it is required that data can be accessed only by its owner. A risk is identified that, due to either errors or malicious action, user Alice could access user Bob's data. In this case, logging is an acceptable mitigation to this risk. The application can log whenever a user accesses a piece of data. Controls can be set up to check the logs in real time and detect if some accesses are incompatible, or this could happen on a regular basis (e.g. once a month a validation routine is run on the logs)
-- ```Privileged (admin) access:``` Some applications may include special permissions for admins. What application admins can do is determined by the application, so misuse of admin credentials is a threat in the scope of the application. Logging accesses and actions performed by admins is a valid mitigation strategy
+- `Scans:` Somebody keeps poking our end-points, testing known end-points, or trying different malicious inputs. A fitting mitigation for this threat can be throttling / rate limiting. Logging would not properly mitigate the issue, because the application team would be unable to take action to stop such an attack. Such an attack would be up to CDC to detect and respond
+- `Access control:` Assume an application holds data that is owned by specific users, and it is required that data can be accessed only by its owner. A risk is identified that, due to either errors or malicious action, user Alice could access user Bob's data. In this case, logging is an acceptable mitigation to this risk. The application can log whenever a user accesses a piece of data. Controls can be set up to check the logs in real time and detect if some accesses are incompatible, or this could happen on a regular basis (e.g. once a month a validation routine is run on the logs)
+- `Privileged (admin) access:` Some applications may include special permissions for admins. What application admins can do is determined by the application, so misuse of admin credentials is a threat in the scope of the application. Logging accesses and actions performed by admins is a valid mitigation strategy
#### Where to store security logs
@@ -60,7 +61,7 @@ Iterative **threat modeling sessions** should be conducted to access the level o
Depending on your selected log strategy, you should consider having **immutable logs**, especially if you are handling confidential data.
-```Critical or sensitive information, access tokens, secrets, source code, keys, certificates, etc.. are examples of what should never be logged.```
+`Critical or sensitive information, access tokens, secrets, source code, keys, certificates, etc.. are examples of what should never be logged.`
### Personal Identifiable Information (PII) & GDPR
diff --git a/docs/toolbox/guidelines/security-requirements.md b/docs/toolbox/guidelines/security-requirements.md
index 0e01c369..beb58efb 100644
--- a/docs/toolbox/guidelines/security-requirements.md
+++ b/docs/toolbox/guidelines/security-requirements.md
@@ -1,50 +1,51 @@
-# Security Requirements
-
-## Why
-
-Defining your security requirements will help you out when performing various security related activities. When thinking of what can go wrong and trying to secure your system, having defined what is important to you just makes sense :shrug: In addition, it helps prioritizing security-work, ex. patching vulnerabilities found in Github Advanced Security, prioritizing threats as part of Threat Modelling or when doing Security Testing.
-
-
-## What
-
-!!! quote "[OWASP Proactive Controls](https://owasp.org/www-project-proactive-controls/v3/en/c1-security-requirements)"
- A security requirement is a statement of needed security functionality that ensures one of many different security properties of software is being satisfied. Security requirements are derived from industry standards, applicable laws, and a history of past vulnerabilities. Security requirements define new features or additions to existing features to solve a specific security problem or eliminate a potential vulnerability.
-
-
-## How
-
-The first step would be to have a look at the business objectives and the functional requirements. From here you can derive security requirements by asking **"what CAN'T go wrong"** in order to meet these requirements.
-
-A good place to define these requirements would be in the Requirements Document (having everything in one place and all that :nerd_face:), and gradually work towards having automated test-cases for them.
-
-!!! tip
- - Look at [OWASP ASVS](https://raw.githubusercontent.com/OWASP/ASVS/v4.0.3/4.0/OWASP%20Application%20Security%20Verification%20Standard%204.0.3-en.pdf) and find testable security requirements that applies to your application
- - Have a look at our Guidelines
-
-!!! question "TL;DR"
- **Define Security Requirements**
-
- _What are some of the things I care about?_
-
- (_ * Take into account Confidentiality, integrity, availability._)
-
- **Define Compliance Requirements**
-
- _Are there any special requirements that must be met (Compliance, and/or legal)?_
-
- ---
-
- Input:
-
- - Business requirement documents
- - Functional requirements documents
- - Information security policies (ex. [WR1211](https://docmap.equinor.com/Docmap/page/doc/dmDocIndex.html?DOCID=1000011245))
- - Regulatory compliance documents (ex. SOX)
- - Security standards & guidelines (ex. [ASVS](https://raw.githubusercontent.com/OWASP/ASVS/v4.0.3/4.0/OWASP%20Application%20Security%20Verification%20Standard%204.0.3-en.pdf))
- - Identity and access management requirements IT900 ([Identity and Access Management](https://statoilsrm.sharepoint.com/sites/IAMnetwork/SitePages/IAM%20Front%20page.aspx))
-
- Output:
-
- - Description of the application functionality
- - List of business objectives
- - Definition of the application security and compliance requirements
+# Security Requirements
+
+## Why
+
+Defining your security requirements will help you out when performing various security related activities. When thinking of what can go wrong and trying to secure your system, having defined what is important to you just makes sense :shrug: In addition, it helps prioritizing security-work, ex. patching vulnerabilities found in Github Advanced Security, prioritizing threats as part of Threat Modelling or when doing Security Testing.
+
+## What
+
+!!! quote "[OWASP Proactive Controls](https://owasp.org/www-project-proactive-controls/v3/en/c1-security-requirements)"
+
+ A security requirement is a statement of needed security functionality that ensures one of many different security properties of software is being satisfied. Security requirements are derived from industry standards, applicable laws, and a history of past vulnerabilities. Security requirements define new features or additions to existing features to solve a specific security problem or eliminate a potential vulnerability.
+
+## How
+
+The first step would be to have a look at the business objectives and the functional requirements. From here you can derive security requirements by asking **"what CAN'T go wrong"** in order to meet these requirements.
+
+A good place to define these requirements would be in the Requirements Document (having everything in one place and all that :nerd_face:), and gradually work towards having automated test-cases for them.
+
+!!! tip
+
+ - Look at [OWASP ASVS](https://raw.githubusercontent.com/OWASP/ASVS/v4.0.3/4.0/OWASP%20Application%20Security%20Verification%20Standard%204.0.3-en.pdf) and find testable security requirements that applies to your application
+ - Have a look at our Guidelines
+
+!!! question "TL;DR"
+
+ **Define Security Requirements**
+
+ _What are some of the things I care about?_
+
+ (\_ * Take into account Confidentiality, integrity, availability.\_)
+
+ **Define Compliance Requirements**
+
+ _Are there any special requirements that must be met (Compliance, and/or legal)?_
+
+ ______________________________________________________________________
+
+ Input:
+
+ - Business requirement documents
+ - Functional requirements documents
+ - Information security policies (ex. [WR1211](https://docmap.equinor.com/Docmap/page/doc/dmDocIndex.html?DOCID=1000011245))
+ - Regulatory compliance documents (ex. SOX)
+ - Security standards & guidelines (ex. [ASVS](https://raw.githubusercontent.com/OWASP/ASVS/v4.0.3/4.0/OWASP%20Application%20Security%20Verification%20Standard%204.0.3-en.pdf))
+ - Identity and access management requirements IT900 ([Identity and Access Management](https://statoilsrm.sharepoint.com/sites/IAMnetwork/SitePages/IAM%20Front%20page.aspx))
+
+ Output:
+
+ - Description of the application functionality
+ - List of business objectives
+ - Definition of the application security and compliance requirements
diff --git a/docs/toolbox/guidelines/security-screening-os-packages.md b/docs/toolbox/guidelines/security-screening-os-packages.md
index 6ce5028e..5840dc5b 100644
--- a/docs/toolbox/guidelines/security-screening-os-packages.md
+++ b/docs/toolbox/guidelines/security-screening-os-packages.md
@@ -14,7 +14,6 @@ How can we determine if an open source package is "safe to use"? This is a quest
Be cautious with package installs and scripts: review sources and prefer isolated containers/VMs/Codespaces for untrusted code. Avoid exposing real secrets in your local env.
-
!!! Info "TL;DR"
- Vet project health: multiple maintainers, recent releases, tests/docs, transparent governance, signed/provenanced releases.
@@ -22,6 +21,7 @@ How can we determine if an open source package is "safe to use"? This is a quest
- Scan continuously: Dependabot plus ecosystem audits (npm audit, pip-audit).
- Update deliberately: apply critical patches fast; otherwise wait ~1 week before bumping—avoid blind auto-updates.
- Explore safely: read source/install scripts; test in isolated containers/VMs/Codespaces; watch for common supply-chain attacks.
+
# Signal intelligence
??? Info "Signals that can give an indication of an open source project's vitality and security risk"
@@ -42,7 +42,6 @@ How can we determine if an open source package is "safe to use"? This is a quest
- (Does the project provide a SBOM (not just dependency lock files))
- (Look for OpenSSF Scorecard)
- (Examining the source code ...)
-
## 3rd party tools to check package health:
@@ -65,7 +64,7 @@ Some 3rd party tools can give an idea about project health:
We recommend having a strategy for when to do package version updates. Besides the usual features given by semantic versioning and breaking changes, don't auto-update to the latest version without some consideration. If not an important security patch, consider waiting until the new release is a week old before updating - malicious updates are usually identified within a few days.
-## Safe screening
+## Safe screening
??? Tip "Are the safe ways to explore a package?"
@@ -80,7 +79,7 @@ Some 3rd party tools can give an idea about project health:
??? Note "Want to to know more about known attack vectors in the open source ecosystem?"
- Typosquatting – Publishing packages with names similar to popular ones (e.g. reqeusts vs requests).
- - Starjacking - Lack of package metadata validation. Associating a source code repository URL with lot of stars to the package
+ - Starjacking - Lack of package metadata validation. Associating a source code repository URL with lot of stars to the package
- Dependency Confusion – Registering a package name in a public registry that overrides an internal/private package.
- Malicious Maintainer Update – A trusted maintainer intentionally adds backdoors or malware.
- Account Takeover – Maintainer's credentials or tokens are compromised, attacker publishes malicious versions.
diff --git a/docs/toolbox/index.md b/docs/toolbox/index.md
index 8c519924..dbd41cd6 100644
--- a/docs/toolbox/index.md
+++ b/docs/toolbox/index.md
@@ -6,6 +6,7 @@ This is your hub for most things that the AppSec team is working on,
in the navigation on the left you'll find categories of guidelines and tools.
!!! info
+
Our official communications channel is Slack and you can find us in the [#appsec](https://equinor.enterprise.slack.com/archives/CMM6FSW5V) channel.
## Recommended reading
diff --git a/docs/toolbox/threat-modeling/ai-assisted.md b/docs/toolbox/threat-modeling/ai-assisted.md
index d4d1fd8a..0cc79ebf 100644
--- a/docs/toolbox/threat-modeling/ai-assisted.md
+++ b/docs/toolbox/threat-modeling/ai-assisted.md
@@ -31,8 +31,9 @@ AI is only as useful as the context you give it. A vague prompt gets a vague ans
The more context lives in your repo, the better Copilot can reason about your system.
!!! example "Example workflow"
+
1. Run a collaborative threat modeling session in **Miro** with your team
- 2. Translate the model into **Threat Dragon** and store the JSON file alongside your source code
- 3. Spar with **GitHub Copilot** to refine and update the model
- 4. Revisit the threat model at major releases, after significant changes, or on a regular cadence
- 5. Ask Copilot: *"Do these changes affect our current threat model?"*
+ 1. Translate the model into **Threat Dragon** and store the JSON file alongside your source code
+ 1. Spar with **GitHub Copilot** to refine and update the model
+ 1. Revisit the threat model at major releases, after significant changes, or on a regular cadence
+ 1. Ask Copilot: *"Do these changes affect our current threat model?"*
diff --git a/docs/toolbox/threat-modeling/getting-started.md b/docs/toolbox/threat-modeling/getting-started.md
index ed7b927c..acca17ba 100644
--- a/docs/toolbox/threat-modeling/getting-started.md
+++ b/docs/toolbox/threat-modeling/getting-started.md
@@ -3,9 +3,9 @@
Regardless of methodology, all threat modeling revolves around four questions:
1. **What are we working on?**
-2. **What can go wrong?**
-3. **What are we going to do about it?**
-4. **Did we do a good enough job?**
+1. **What can go wrong?**
+1. **What are we going to do about it?**
+1. **Did we do a good enough job?**
There is no shortage of excellent resources on how to work through these questions in practice. Rather than repeating what others have already written well, here are some we recommend:
@@ -28,4 +28,5 @@ Threat modeling does not have to be a big event. Even a short, focused session w
Threat modeling works best as a team activity. The people who build and operate the system are the ones who understand it best. Involve anyone who can contribute to answering the four questions above.
!!! info "Need help getting started?"
+
See our [Getting Help](support.md) page for how to get help with threat modeling.
diff --git a/docs/toolbox/threat-modeling/index.md b/docs/toolbox/threat-modeling/index.md
index 94cde2e7..38465c0a 100644
--- a/docs/toolbox/threat-modeling/index.md
+++ b/docs/toolbox/threat-modeling/index.md
@@ -15,4 +15,5 @@ Threat modeling answers the *why* behind your security efforts. If you don't bas
Threat modeling gives you a structured way to identify what actually matters, so your security work is driven by real risks rather than assumptions or checklists.
!!! tip
- Threat modeling is widely regarded in the security community as one of the most impactful activities a development team can perform to improve their security posture.
\ No newline at end of file
+
+ Threat modeling is widely regarded in the security community as one of the most impactful activities a development team can perform to improve their security posture.
diff --git a/docs/toolbox/threat-modeling/support.md b/docs/toolbox/threat-modeling/support.md
index 8b6e5752..0aa63a27 100644
--- a/docs/toolbox/threat-modeling/support.md
+++ b/docs/toolbox/threat-modeling/support.md
@@ -1,6 +1,7 @@
# Getting Help
!!! warning "Facilitation on pause"
+
The AppSec team has paused facilitation of threat modeling sessions.
Here are some ways to get started on your own:
diff --git a/docs/toolbox/tools/pre-commit-faq.md b/docs/toolbox/tools/pre-commit-faq.md
index b6926729..49515cb0 100644
--- a/docs/toolbox/tools/pre-commit-faq.md
+++ b/docs/toolbox/tools/pre-commit-faq.md
@@ -21,8 +21,8 @@ Pre-commit can be installed in two ways, using Python pip or Homebrew.
### Installing pre-commit
-| Python | Homebrew |
-|---|---|
+| Python | Homebrew |
+| ------------------------ | ------------------------- |
| `pip install pre-commit` | `brew install pre-commit` |
Once pre-commit is installed you need to set up the git hook scripts by running `pre-commit install`. Now pre-commit wil automatically run on git commit!
diff --git a/docs/toolbox/tools/secure-coding.md b/docs/toolbox/tools/secure-coding.md
index b67e0b33..7e0bf646 100644
--- a/docs/toolbox/tools/secure-coding.md
+++ b/docs/toolbox/tools/secure-coding.md
@@ -11,6 +11,7 @@ We have a secure coding learning platform. Use [this form](https://forms.microso
- Learn things that may help advance your career!
!!! info
+
You can gain unique merch if you're a Security Champion while using this platform!
There is a wide arrangement of subjects, and you can do them all if you wish! So there are tracks for:
diff --git a/docs/toolbox/tools/sharing-secrets.md b/docs/toolbox/tools/sharing-secrets.md
index 5399b932..9acaa9e1 100644
--- a/docs/toolbox/tools/sharing-secrets.md
+++ b/docs/toolbox/tools/sharing-secrets.md
@@ -10,6 +10,7 @@ Yopass is a tool designed to securely share sensitive information, such as passw
- **Verify Yopass links before accessing them**.
!!! note "Weekly Cache Clearance"
+
Our hosted instance of Yopass clears its cache every Sunday. Ensure any secrets are accessed within the intended timeframe, as they may be deleted during this routine maintenance.
## Key Risks Yopass Mitigates
@@ -26,38 +27,38 @@ Yopass is a tool designed to securely share sensitive information, such as passw
- Individually, the Yopass link and decode key are harmless. However, when **combined**, they grant access to the secret and should collectively be handled with the same level of confidentiality as the secret itself.
-2. **Transmit Link and Decode Key Separately**
+1. **Transmit Link and Decode Key Separately**
- Whenever possible, send the Yopass link and the decode key via **separate communication channels** to minimize the risk of unauthorized access.
- *Example*: Send the link via email and the decode key via a direct message on teams.
-3. **Use Secure Communication Channels**
+1. **Use Secure Communication Channels**
- Share the Yopass link and decode key through **secure channels** (e.g. email, secure messaging apps).
- **Avoid using public or unsecured networks** to transmit sensitive information.
-4. **Limit the Expiration Period**
+1. **Limit the Expiration Period**
- Set an appropriate **expiration time** when sharing a secret. This limits the exposure window and ensures secrets are available only for the required duration.
- Use the **one-time retrieval** feature to ensure the secret can only be accessed once.
-5. **Password Policies**
+1. **Password Policies**
- Secrets shared via Yopass should comply with [internal security policies](https://aris.equinor.com/#default/search/~AfBbImNvbXBvbmVudF9jb21tZW50U3RyZWFtMTUzIl0~) and **should not be reused across services**.
-6. **Link Verification**
+1. **Link Verification**
- **Verify Yopass links before accessing them** to ensure they originate from trusted sources.
-7. **Use Yopass for Temporary Sharing**
+1. **Use Yopass for Temporary Sharing**
- Avoid using Yopass for long-term secrets. If the secret is expected to be in use for an extended period, consider other methods such as managed secrets vaults.
!!! important "**Exceptions and Practical Considerations**"
+
In scenarios where sending both the link and decode key together is necessary (e.g., time-sensitive situations or recipient limitations), ensure that:
-
+
- A **secure communication channel** is used.
- The recipient is instructed to **access the secret promptly**.
-
diff --git a/docs/toolbox/version-control/gh-actions-runners.md b/docs/toolbox/version-control/gh-actions-runners.md
index 233e7013..4fdff22e 100644
--- a/docs/toolbox/version-control/gh-actions-runners.md
+++ b/docs/toolbox/version-control/gh-actions-runners.md
@@ -12,7 +12,7 @@ When using GitHub Actions, it is good practice to:
- Investigate and align on repository configuration (repository settings)
- _Actions -> General_
- _Action permissions_
- - If you don't use GitHub Actions, set this to "Disable actions"
+ - If you don't use GitHub Actions, set this to "Disable actions"
- If you want to use GitHub Actions, set this to "Allow enterprise, and select non-enterprise, actions and reusable workflows"
- You can check "Allow actions created by GitHub"
- Add the specific actions you are going to use into the "Allow specified actions and reusable workflows" section
@@ -20,7 +20,7 @@ When using GitHub Actions, it is good practice to:
- The default should be "Require approval for all outside collaborators"
- _Fork pull request workflows_ (non-public repos)
- "Run workflows from fork pull requests" should not be checked. Change only if you have a good understanding of the risks involved.
- - All external actions that you plan to use should be examined/audited. This could include examining the repos and using services like [StepSecurity's Action Advisor](https://app.stepsecurity.io/action-advisor).
+ - All external actions that you plan to use should be examined/audited. This could include examining the repos and using services like [StepSecurity's Action Advisor](https://app.stepsecurity.io/action-advisor).
- Pin actions to the full length commit-hash (example "uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1"). You can pin to a tag (version) if you trust the creator (Example: "uses: actions/checkout@v4.1.1")
- Use OpenID Connect for access to Azure Resources ([Configuring OpenID Connect in Azure](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure), [MS Workload Identity Federation](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation))
- Manage secrets securely; they should only be available to workflows and branches where needed
@@ -30,7 +30,6 @@ When using GitHub Actions, it is good practice to:
- Limit the permissions/scope of all tokens/credentials, especially the GITHUB_TOKEN.
- Only refer to values that you control in an action (think risks related to non-validated input).
- Use branch protection for your repository (workflows should be treated as "sensitive", use branch protection and careful review for any changes).
-
## Using Self-Hosted Runners
diff --git a/docs/toolbox/version-control/git-github.md b/docs/toolbox/version-control/git-github.md
index ddbf176d..276cefef 100644
--- a/docs/toolbox/version-control/git-github.md
+++ b/docs/toolbox/version-control/git-github.md
@@ -6,7 +6,6 @@ This guideline contains some basic information on configuration of Git and user
Git is a distributed version control system for tracking changes in source code, while GitHub is a platform that hosts Git repositories online. GitHub builds upon Git, offering a centralized place for developers to share and work on code together.
-
!!! Info "The SCM Policy"
[The Equinor Developer Portal](https://developer.equinor.com/) contains our [Source Code Management System Policy](https://developer.equinor.com/governance/scm-policy/). Please make sure you are familiar with the content.
@@ -26,17 +25,16 @@ We assume that `git` and `ssh` are installed on your system. We do not cover the
The official [Git documentation](https://git-scm.com/doc) is a good source for authoritative answers and deep dives.
-
### How Git manages config
-Git is dependent on proper configuration to work. Configuration can be read from the command line (using the `-c` option), environment variables or files. The [official guide](https://git-scm.com/docs/git-config#_configuration) provides the details.
+Git is dependent on proper configuration to work. Configuration can be read from the command line (using the `-c` option), environment variables or files. The [official guide](https://git-scm.com/docs/git-config#_configuration) provides the details.
We usually store Git config in files. Git will read config from multiple locations depending on their availability. The files are read in the order shown below, the `last value read` will take precedence over values read earlier.
1. System config (usually `/etc/gitconfig`)
-2. Config file in home directory (usually `$HOME/.gitconfig`)
-3. Repository config files (`$GITDIR/config`)
-
+1. Config file in home directory (usually `$HOME/.gitconfig`)
+1. Repository config files (`$GITDIR/config`)
+
The config files can be updated manually with a text editor or by using Git
Git configuration from the command line follow the following structure:
@@ -76,12 +74,10 @@ git config --global user.name "Peter Pan"
Using the command `git config --list --show-origin` will expand all git config across the different levels.
-
### Recommended generic basic config
This section contains the recommended basic generic configuration for Git.
-
```shell
git config --global user.name "value"
git config --global user.email "value"
@@ -94,24 +90,21 @@ git config --global user.email "value"
We also recommend that you check the "Keep my email address private" and even "Block command line pushes that expose my email" in [email section](https://github.com/settings/emails) of your profile on github.com
-
### Using SSH with git
Git uses HTTP or SSH to communicate with github.com. Both alternatives are viable and provide a good level of security. HTTP(S) assumes the usage of PAT (Personal Access Token) tokens rather than account passwords. A short threat model of the options contains the following sections:
-
-| Threat | SSH (with password-protected keys) | HTTPS (with PATs) |
-|-----------------------|---------------------------------------------------------|----------------------------------------------------------------|
-| **Interception** | Encrypted; MITM attack needed. Passphrases protect keys on disk, but not in transit since keys aren't transmitted. | Encrypted; susceptible to MITM, but TLS and certificate validation mitigate risks. PATs are used instead of passwords. |
-| **Impersonation** | Theft of private key and passphrase required for impersonation. | Relies on secure storage of PATs. Impersonation possible if a PAT is exposed. |
-| **Eavesdropping** | Encrypted traffic; passphrase adds security at rest, not in transit. | Encrypted traffic; PATs should be securely stored to prevent unauthorized access. |
-| **Authentication** | Strong, with added layer of passphrase protection for key files. | PATs can be set to expire, enhancing security by limiting the lifespan of access credentials. |
-| **Configuration** | Requires management of key pairs and passphrases, slightly more complex. | Requires management of PATs, including regular rotation and setting appropriate expiration dates. |
-| **Availability** | Direct; less prone to web attacks, but firewalls might block SSH. | High through standard web ports; PATs can be revoked or expire, requiring renewal for continued access. |
-| **Key/Token Expiry** | SSH keys do not expire by default; requires manual rotation for security. | PATs can be configured to expire, forcing regular renewal and review of access permissions. |
-| **Theft of Credentials** | Risk mitigated by passphrase encryption of the private key. Physical access or malware required to steal. | Risk of PAT exposure, especially if stored insecurely or transmitted over insecure channels. |
-| **Least privilege** | SSH keys inherit all permissions of a user. No granularity | PAT tokens can be configured for fine grained permissions and then provide access to all or only specific repos. This could strengthen security. Token management will add extra complexity. |
-
+| Threat | SSH (with password-protected keys) | HTTPS (with PATs) |
+| ------------------------ | ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Interception** | Encrypted; MITM attack needed. Passphrases protect keys on disk, but not in transit since keys aren't transmitted. | Encrypted; susceptible to MITM, but TLS and certificate validation mitigate risks. PATs are used instead of passwords. |
+| **Impersonation** | Theft of private key and passphrase required for impersonation. | Relies on secure storage of PATs. Impersonation possible if a PAT is exposed. |
+| **Eavesdropping** | Encrypted traffic; passphrase adds security at rest, not in transit. | Encrypted traffic; PATs should be securely stored to prevent unauthorized access. |
+| **Authentication** | Strong, with added layer of passphrase protection for key files. | PATs can be set to expire, enhancing security by limiting the lifespan of access credentials. |
+| **Configuration** | Requires management of key pairs and passphrases, slightly more complex. | Requires management of PATs, including regular rotation and setting appropriate expiration dates. |
+| **Availability** | Direct; less prone to web attacks, but firewalls might block SSH. | High through standard web ports; PATs can be revoked or expire, requiring renewal for continued access. |
+| **Key/Token Expiry** | SSH keys do not expire by default; requires manual rotation for security. | PATs can be configured to expire, forcing regular renewal and review of access permissions. |
+| **Theft of Credentials** | Risk mitigated by passphrase encryption of the private key. Physical access or malware required to steal. | Risk of PAT exposure, especially if stored insecurely or transmitted over insecure channels. |
+| **Least privilege** | SSH keys inherit all permissions of a user. No granularity | PAT tokens can be configured for fine grained permissions and then provide access to all or only specific repos. This could strengthen security. Token management will add extra complexity. |
!!! tip "Use SSH with Git"
@@ -124,9 +117,10 @@ The [Connecting to GitHub with SSH](https://docs.github.com/en/authentication/co
The following sections of the guideline contains the usual steps to get started with SSH.
#### [Generate a new SSH key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key)
- - Type of key should be `ed25519`
- - Specify a filename (`-f`). We assume you can have multiple keys and suggest a naming convention like "service"-"identity"-"index". The index could be creation date and then used for automating key rotation.
- - Add a comment (`-C`) to help identify the key's purpose. This will be added to the public key as generic text but will not have any functional impact. It will not be exposed on github.com. However, the purpose of a public key is to "share it", so use with caution (PII).
+
+- Type of key should be `ed25519`
+- Specify a filename (`-f`). We assume you can have multiple keys and suggest a naming convention like "service"-"identity"-"index". The index could be creation date and then used for automating key rotation.
+- Add a comment (`-C`) to help identify the key's purpose. This will be added to the public key as generic text but will not have any functional impact. It will not be exposed on github.com. However, the purpose of a public key is to "share it", so use with caution (PII).
Example; Creating a SSH key for the Github user `larskaare`
@@ -148,7 +142,7 @@ You will be asked "Enter passphrase (empty for no passphrase)", we strongly reco
Adding the generated SSH key to the `ssh-agent` gives you a secure repository for your private keys's passphrases. Adding keys and passphrases to the key agent eliminates the need to repeatedly enter the passphrase.
-Follow the [official documentation](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#adding-your-ssh-key-to-the-ssh-agent) of and add the SSH key to the ssh-agent. Be aware of the operating system selector at the top of the page - it will give you instructions for **Mac**, **Windows** and **Linux**. The sections below covers a minimal set-up of how to add the SSH key to the agent. The official version has more details - you can follow either.
+Follow the [official documentation](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#adding-your-ssh-key-to-the-ssh-agent) of and add the SSH key to the ssh-agent. Be aware of the operating system selector at the top of the page - it will give you instructions for **Mac**, **Windows** and **Linux**. The sections below covers a minimal set-up of how to add the SSH key to the agent. The official version has more details - you can follow either.
#### Adding SSH config
@@ -247,9 +241,8 @@ We recommend the following settings:
#### [Public profile](https://github.com/settings/profile)
-- Use your full name
-- Select an recognizable avatar/picture for profile picture
-
+- Use your full name
+- Select an recognizable avatar/picture for profile picture
#### [Emails](https://github.com/settings/emails)
@@ -281,7 +274,7 @@ We recommend the following settings:
#### [Code security an analysis](https://github.com/settings/security_analysis)
- "Push protection for yourself" should be enabled
- - Block push with supported secrets against public repos
+ - Block push with supported secrets against public repos
#### [Applications](https://github.com/settings/installations)
@@ -293,7 +286,7 @@ We recommend the following settings:
- Review permissions
- Review organization access
- Revoke access if not in use
-
+
#### [Security log](https://github.com/settings/security-log)
- Familiarize with the content of the security log
diff --git a/docs/toolbox/version-control/git-signed-commits.md b/docs/toolbox/version-control/git-signed-commits.md
index 003736b9..02efebba 100644
--- a/docs/toolbox/version-control/git-signed-commits.md
+++ b/docs/toolbox/version-control/git-signed-commits.md
@@ -24,15 +24,15 @@ The code from our software configuration management system (SCM) is the starting
The official [GitHub documentation](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification) on signature verification shows that commits can be signed using GPG, SSH or S/MIME. The 3 different methods have their pros and cons.
-| Feature/Aspect | SSH Signing | GPG Signing | S/MIME Signing |
-|----------------------------|------------------------------------------------------------|----------------------------------------------------------------|--------------------------------------------------------------|
-| **Basic Mechanism** | Uses SSH keys for both authentication and signing. | Utilizes a public-private key pair specifically for signing. | Uses certificates issued by a Certificate Authority (CA). |
-| **Identity Verification** | SSH public keys are used to verify the signature. | Verification is based on a web of trust or direct key sharing. | Relies on certificates verified and issued by trusted CAs. |
-| **Infrastructure** | Requires SSH key setup; already needed for repository access. | Requires GPG software and management of key pairs. | Requires obtaining and managing a certificate from a CA. Potential complex PKI |
-| **Ease of Setup** | Simple for users already using SSH keys for Git operations. | Can be complex due to key generation, management, and sharing. | Varies; obtaining a certificate can be straightforward or complex depending on the provider. |
-| **Cross-platform Support** | Broad support across various platforms and Git tools. | Well-supported, with widespread integration in Git tools. | Support varies; some tools may not support S/MIME directly. |
-| **Pros** | - Simplifies workflow by using the same keys for authentication and signing†.
- Integrated into SSH, which is commonly used for secure Git operations. | - Decentralized and flexible, with a variety of algorithms and key sizes.
- Well-established in the open-source community. | - Trust model is straightforward, based on established CAs.
- May align with existing certificate-based security practices (e.g., email). |
-| **Cons** | - Primarily verifies the commit was pushed by an authenticated user, not necessarily the commit's author.
- SSH key management is crucial; compromised keys pose a risk. | - Key management and the web of trust model can be complex.
- Requires active key maintenance (revocation, expiration). | - Dependent on third-party CAs for issuance and trust.
- Certificates have expiration dates and may incur costs. |
+| Feature/Aspect | SSH Signing | GPG Signing | S/MIME Signing |
+| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Basic Mechanism** | Uses SSH keys for both authentication and signing. | Utilizes a public-private key pair specifically for signing. | Uses certificates issued by a Certificate Authority (CA). |
+| **Identity Verification** | SSH public keys are used to verify the signature. | Verification is based on a web of trust or direct key sharing. | Relies on certificates verified and issued by trusted CAs. |
+| **Infrastructure** | Requires SSH key setup; already needed for repository access. | Requires GPG software and management of key pairs. | Requires obtaining and managing a certificate from a CA. Potential complex PKI |
+| **Ease of Setup** | Simple for users already using SSH keys for Git operations. | Can be complex due to key generation, management, and sharing. | Varies; obtaining a certificate can be straightforward or complex depending on the provider. |
+| **Cross-platform Support** | Broad support across various platforms and Git tools. | Well-supported, with widespread integration in Git tools. | Support varies; some tools may not support S/MIME directly. |
+| **Pros** | - Simplifies workflow by using the same keys for authentication and signing†.
- Integrated into SSH, which is commonly used for secure Git operations. | - Decentralized and flexible, with a variety of algorithms and key sizes.
- Well-established in the open-source community. | - Trust model is straightforward, based on established CAs.
- May align with existing certificate-based security practices (e.g., email). |
+| **Cons** | - Primarily verifies the commit was pushed by an authenticated user, not necessarily the commit's author.
- SSH key management is crucial; compromised keys pose a risk. | - Key management and the web of trust model can be complex.
- Requires active key maintenance (revocation, expiration). | - Dependent on third-party CAs for issuance and trust.
- Certificates have expiration dates and may incur costs. |
† *while reusing the SSH key is a recognized advantage of SSH signing, we recommend against this practice, see below.*
@@ -42,9 +42,9 @@ The official [GitHub documentation](https://docs.github.com/en/authentication/ma
## Configure your local development environment
-For this guideline our reference is using Git from the command line.
+For this guideline our reference is using Git from the command line.
-We assume that Git and SSH is installed on your system.
+We assume that Git and SSH is installed on your system.
We assume that you are using SSH to authenticate Git with github.com. Consult our [guideline](git-github.md) for more information on this topic.
@@ -84,7 +84,6 @@ These lines will tell git to use SSH for signing commit, tell git where to find
More information on this step can be found in the [GitHub documentation](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key)
-
### Examining the git log
To verify that commits are signed locally you can use the following command:
@@ -133,7 +132,6 @@ git verify-commit -v HEAD
We have shown how to sign git commits. You can also sign git tags!
-
## Configure Github
At this stage in the guideline we are able to sign and verify the signature of locally committed changes. If you push your changes to github.com they will get the `Unverified` status. This indicate that Github has found a signature in the commit but it is not able to verify it. You can find more detailed information on the Github docs on [About commit signature verification](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)
@@ -160,7 +158,7 @@ If you select the `Verified` badge you will get information on the signer and th
ssh-keygen -lf ~/.ssh/git_ssh_signing_key_1
```
-!!! Tip Vigilant Mode
+!!! Tip Vigilant Mode
Explore Github's [Vigilant Mode](https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits#about-vigilant-mode) It should increase the trust level of signed commits yet another level.
@@ -173,7 +171,7 @@ The official documentation can be found in [Managing protected branches](https:/
We recommend the following **minimum** protection for important branches:
- Protect matching branches
- - Check "Require a pull request before merging"
+ - Check "Require a pull request before merging"
- Check "**Require signed commits**"
- Check "Do not allow bypassing the above settings"
- Rules applied to everyone including administrators
@@ -182,9 +180,10 @@ We recommend the following **minimum** protection for important branches:
## Signing on GitHub Codespaces
-GitHub Codespaces can take care of setting up GPG signing keys inside the codespace on your behalf. It is trivial to set it up, but it has some security implications, so **do this exclusively for repositories you trust**.
+GitHub Codespaces can take care of setting up GPG signing keys inside the codespace on your behalf. It is trivial to set it up, but it has some security implications, so **do this exclusively for repositories you trust**.
If you trust the repository (and its maintainers), signing in Codespaces can be enabled by following these steps:
+
- Open the [Codespaces settings](https://github.com/settings/codespaces) on GitHub
- Enable "GPG Verification"
- Choose `Selected Repositories` in the "Trusted Repositories" section, and add the relevant repository
@@ -193,10 +192,7 @@ See also [the official GitHub Documentation](https://docs.github.com/en/codespac
If you cannot trust the content or the maintainers of a repository (eg, if you are contributing to a project owned by another company, or to an open source projects) and you want/need to sign your commits, it is better to use a local development setup rather than GitHub Codespaces. The problem is that the codespace could have been configured to run malicious code. That code could syphon the GPG signing keys that GitHub would automatically drop into the environment (and all other sorts of sensitive data you enter in the codespace). For more details, see [here](https://docs.github.com/en/codespaces/reference/security-in-github-codespaces).
-
-
## External resources
- We disagree with the premise of signed git commits not being useful in the [Should We Sign Git Commits? Probably Not!](https://medium.com/@michael.vittrup.larsen/should-we-sign-git-commits-probably-not-c09ad3c18393). Using git signatures are useful, but it's a not a silver bullet. The article contains relevant reflections advocating for focusing on building a foundation of trust through the review and approval of pull requests by multiple trusted individuals and the importance of a collective scrutiny process. **This we fully support**
- We are monitoring the [Git Sign project](https://docs.sigstore.dev/cosign/signing/gitsign/) of Sigstore. It contains a very interesting approach to the trust chain.
-
diff --git a/uv.lock b/uv.lock
index 0d89fb27..a7a00065 100644
--- a/uv.lock
+++ b/uv.lock
@@ -14,14 +14,25 @@ dependencies = [
{ name = "requests" },
]
+[package.optional-dependencies]
+dev = [
+ { name = "mdformat" },
+ { name = "mdformat-mkdocs" },
+ { name = "pre-commit" },
+]
+
[package.metadata]
requires-dist = [
+ { name = "mdformat", marker = "extra == 'dev'" },
+ { name = "mdformat-mkdocs", marker = "extra == 'dev'" },
{ name = "mkdocs-awesome-pages-plugin", specifier = "==2.10.1" },
{ name = "mkdocs-git-revision-date-localized-plugin", specifier = "==1.5.1" },
{ name = "mkdocs-material", specifier = "==9.6.19" },
{ name = "mkdocs-techdocs-core", specifier = "==1.6.0" },
+ { name = "pre-commit", marker = "extra == 'dev'" },
{ name = "requests", specifier = "==2.32.5" },
]
+provides-extras = ["dev"]
[[package]]
name = "babel"
@@ -64,6 +75,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
]
+[[package]]
+name = "cfgv"
+version = "3.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" },
+]
+
[[package]]
name = "charset-normalizer"
version = "3.4.4"
@@ -142,6 +162,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
+[[package]]
+name = "distlib"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
+]
+
+[[package]]
+name = "filelock"
+version = "3.25.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" },
+]
+
[[package]]
name = "ghp-import"
version = "2.1.0"
@@ -178,6 +216,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" },
]
+[[package]]
+name = "identify"
+version = "2.6.18"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" },
+]
+
[[package]]
name = "idna"
version = "3.11"
@@ -220,6 +267,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/74/d7/382f0835d18633e1c45854eb19c066ce47e5694787256d723816a04d3f16/markdown_graphviz_inline-1.1.3-py3-none-any.whl", hash = "sha256:3571f7da594533b6c2c9ef08a6ce582396b64f778e47ac3678f00e75b2652909", size = 3988, upload-time = "2025-01-27T16:58:50.208Z" },
]
+[[package]]
+name = "markdown-it-py"
+version = "4.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
+]
+
[[package]]
name = "markupsafe"
version = "3.0.3"
@@ -283,6 +342,69 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
]
+[[package]]
+name = "mdformat"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3f/05/32b5e14b192b0a8a309f32232c580aefedd9d06017cb8fe8fce34bec654c/mdformat-1.0.0.tar.gz", hash = "sha256:4954045fcae797c29f86d4ad879e43bb151fa55dbaf74ac6eaeacf1d45bb3928", size = 56953, upload-time = "2025-10-16T12:05:03.695Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/9a/8fe71b95985ca7a4001effbcc58e5a07a1f2a2884203f74dcf48a3b08315/mdformat-1.0.0-py3-none-any.whl", hash = "sha256:bca015d65a1d063a02e885a91daee303057bc7829c2cd37b2075a50dbb65944b", size = 53288, upload-time = "2025-10-16T12:05:02.607Z" },
+]
+
+[[package]]
+name = "mdformat-gfm"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+ { name = "mdformat" },
+ { name = "mdit-py-plugins" },
+ { name = "wcwidth" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/56/6f/a626ebb142a290474401b67e2d61e73ce096bf7798ee22dfe6270f924b3f/mdformat_gfm-1.0.0.tar.gz", hash = "sha256:d1d49a409a6acb774ce7635c72d69178df7dce1dc8cdd10e19f78e8e57b72623", size = 10112, upload-time = "2025-10-16T09:12:22.402Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e6/18/6bc2189b744dd383cad03764f41f30352b1278d2205096f77a29c0b327ad/mdformat_gfm-1.0.0-py3-none-any.whl", hash = "sha256:7305a50efd2a140d7c83505b58e3ac5df2b09e293f9bbe72f6c7bee8c678b005", size = 10970, upload-time = "2025-10-16T09:12:21.276Z" },
+]
+
+[[package]]
+name = "mdformat-mkdocs"
+version = "5.1.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdformat" },
+ { name = "mdformat-gfm" },
+ { name = "mdit-py-plugins" },
+ { name = "more-itertools" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/25/df38de72a291b96fb7af7549c5285e934267f8f0b269d3ccacda84ef764c/mdformat_mkdocs-5.1.4.tar.gz", hash = "sha256:d3ecf035100328c5ff2b3bd7de87a16ee0484e5c317add9c70022598d15af6a1", size = 28160, upload-time = "2026-01-26T12:12:41.983Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/35/bf/00ba31df7cd955eb0dbb1b8b0eb388bc6f717f033f807656a12eb8f12a5b/mdformat_mkdocs-5.1.4-py3-none-any.whl", hash = "sha256:6ff339c1023926077c0c598533768433b38981a9eb792ebfe02d2438ba71514d", size = 38377, upload-time = "2026-01-26T12:12:40.326Z" },
+]
+
+[[package]]
+name = "mdit-py-plugins"
+version = "0.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
+]
+
[[package]]
name = "mdx-truly-sane-lists"
version = "1.3"
@@ -449,6 +571,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/db/1e15b44f46ce3b337bfdd8d298f2f68ca0c07a8ed7c48479520409ace05a/mkdocs_techdocs_core-1.6.0-py3-none-any.whl", hash = "sha256:d0a59f934836032bdbc69ac92bfb853fe1b3e83c8fff5e2a35aa04546d229f91", size = 14238, upload-time = "2025-09-16T20:01:41.074Z" },
]
+[[package]]
+name = "more-itertools"
+version = "10.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" },
+]
+
[[package]]
name = "natsort"
version = "8.4.0"
@@ -458,6 +589,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload-time = "2023-06-20T04:17:17.522Z" },
]
+[[package]]
+name = "nodeenv"
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
+]
+
[[package]]
name = "packaging"
version = "25.0"
@@ -508,6 +648,22 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
]
+[[package]]
+name = "pre-commit"
+version = "4.5.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cfgv" },
+ { name = "identify" },
+ { name = "nodeenv" },
+ { name = "pyyaml" },
+ { name = "virtualenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
+]
+
[[package]]
name = "pygments"
version = "2.19.2"
@@ -542,6 +698,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
+[[package]]
+name = "python-discovery"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "filelock" },
+ { name = "platformdirs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9c/90/bcce6b46823c9bec1757c964dc37ed332579be512e17a30e9698095dcae4/python_discovery-1.2.0.tar.gz", hash = "sha256:7d33e350704818b09e3da2bd419d37e21e7c30db6e0977bb438916e06b41b5b1", size = 58055, upload-time = "2026-03-19T01:43:08.248Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl", hash = "sha256:1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a", size = 31524, upload-time = "2026-03-19T01:43:07.045Z" },
+]
+
[[package]]
name = "python-slugify"
version = "8.0.4"
@@ -672,6 +841,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
]
+[[package]]
+name = "virtualenv"
+version = "21.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "distlib" },
+ { name = "filelock" },
+ { name = "platformdirs" },
+ { name = "python-discovery" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" },
+]
+
[[package]]
name = "watchdog"
version = "6.0.0"
@@ -707,3 +891,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/79/3e/c0bdc27cf06f4e476
wheels = [
{ url = "https://files.pythonhosted.org/packages/eb/d8/0d1d2e9d3fabcf5d6840362adcf05f8cf3cd06a73358140c3a97189238ae/wcmatch-10.1-py3-none-any.whl", hash = "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a", size = 39854, upload-time = "2025-06-22T19:14:00.978Z" },
]
+
+[[package]]
+name = "wcwidth"
+version = "0.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" },
+]