mirror of
https://github.com/zizmorcore/zizmor.git
synced 2025-12-23 08:47:33 +00:00
feat: begin prepping zizmor's website (#78)
This commit is contained in:
parent
b25a5bf428
commit
0ce62213f4
25 changed files with 706 additions and 411 deletions
49
.github/workflows/site.yml
vendored
Normal file
49
.github/workflows/site.yml
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
name: Deploy zizmor site
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- site-staging
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v3
|
||||
|
||||
- name: build site
|
||||
run: make site
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: site_html
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1 +1,4 @@
|
|||
/target
|
||||
|
||||
# website artifacts
|
||||
/site_html
|
||||
|
|
|
|||
109
CONTRIBUTING.md
109
CONTRIBUTING.md
|
|
@ -2,69 +2,84 @@
|
|||
|
||||
Thank you for your interest in contributing to `zizmor`!
|
||||
|
||||
The information below will help you set up a local development environment,
|
||||
as well as with performing common development tasks.
|
||||
This is intended to be a "high-level" guide with some suggestions
|
||||
for ways to contribute. Once you've picked a contribution idea,
|
||||
please see our [development docs]
|
||||
for concrete guidance on specific development tasks (such as building `zizmor`
|
||||
itself or the documentation website).
|
||||
|
||||
This is intended to be a "high-level" guide; for concrete development
|
||||
goals (such as modifying an audit or contributing a new audit),
|
||||
please see our [development docs](./docs/DEVELOPMENT.md).
|
||||
## How to contribute
|
||||
|
||||
## Requirements
|
||||
Here's a short list of steps you can follow to contribute:
|
||||
|
||||
`zizmor`'s only development requirement the Rust compiler.
|
||||
1. *Figure out what you want to contribute.* See the
|
||||
[contribution ideas](#contribution-ideas) section below if you're looking
|
||||
for ideas!
|
||||
2. *File or reply to an issue, if appropriate.* Some contributions require
|
||||
new issues (like new bugs), while others involve an existing issue
|
||||
(like known documentation defects). Others don't require an issue at all,
|
||||
like small typo fixes. In general, if you aren't sure, *error on the side
|
||||
of making or replying to an issue* — it helps maintain shared
|
||||
development context.
|
||||
3. *Hack away.* Once you know what you're working on, refer to our
|
||||
[development docs] for help with specific development tasks. And don't be
|
||||
afraid to ask for help!
|
||||
|
||||
You can install Rust by following the steps on [Rust's official website].
|
||||
## Contribution ideas
|
||||
|
||||
[Rust's official website]: https://www.rust-lang.org/tools/install
|
||||
Here are some ways that you can contribute to `zizmor`. These aren't the only
|
||||
ways; they're just for inspiration.
|
||||
|
||||
## Development steps
|
||||
### Good first issues
|
||||
|
||||
To get started, you'll need to clone the repository and perform a debug build:
|
||||
We use the ["good first issue"] label to track issues that we think are
|
||||
(somewhat) easy and/or straightforward, making them good choices for an
|
||||
early contribution.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/woodruffw/zizmor
|
||||
cd zizmor
|
||||
cargo build
|
||||
```
|
||||
To work on one of these, **please leave a comment** on its issue before opening
|
||||
a pull request to make sure nobody else duplicates your work!
|
||||
|
||||
Once `cargo build` completes, you should have a functional development
|
||||
build of `zizmor`!
|
||||
["good first issue"]: https://github.com/woodruffw/zizmor/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
|
||||
|
||||
```bash
|
||||
# cargo run -- --help also works
|
||||
./target/debug/zizmor --help
|
||||
```
|
||||
### Writing documentation
|
||||
|
||||
## Linting
|
||||
One of the best ways to help us with `zizmor` is to help us improve our
|
||||
documentation!
|
||||
|
||||
`zizmor` is linted with `cargo clippy` and auto-formatted with `cargo fmt`.
|
||||
Our CI enforces both, but you should also run them locally to minimize
|
||||
unnecessary review cycles:
|
||||
Here are some things we could use help with:
|
||||
|
||||
```bash
|
||||
cargo fmt
|
||||
cargo clippy --fix
|
||||
```
|
||||
* Improving our [CLI usage recipes](https://woodruffw.github.io/zizmor/usage/).
|
||||
* Improving the detail in our
|
||||
[audit documentation pages](https://woodruffw.github.io/zizmor/audits/).
|
||||
* Improving our internal (Rust API) documentation, especially in conjunction
|
||||
with more unit tests.
|
||||
|
||||
## Testing
|
||||
More generally, see [issues labeled with `documentation`] for a potential
|
||||
list of documentation efforts to contribute on.
|
||||
|
||||
`zizmor` uses `cargo test`:
|
||||
[issues labeled with `documentation`]: https://github.com/woodruffw/zizmor/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
### Writing unit tests
|
||||
|
||||
## Development practices
|
||||
We can always use more unit tests! Pick a part of the Rust codebase and
|
||||
start testing.
|
||||
|
||||
Here are some guidelines to follow if you're working on `zizmor`:
|
||||
Keep the cardinal rule of unit testing in mind: a unit test must test
|
||||
**a single unit** of behavior. If it tests more than one unit, then
|
||||
consider making it an integration test instead.
|
||||
|
||||
### Reducing false positives/negatives in audits
|
||||
|
||||
Static analysis is inherently imprecise, and `zizmor` is no exception.
|
||||
|
||||
We track imprecision bugs with the ["false positive"] and ["false negative"]
|
||||
labels. These can sometimes be tricky to address, so we recommend
|
||||
(but don't require) leaving an explanatory comment on the issue before
|
||||
beginning a pull request.
|
||||
|
||||
["false positive"]: https://github.com/woodruffw/zizmor/issues?q=is%3Aopen+label%3Afalse-positive
|
||||
|
||||
["false negative"]: https://github.com/woodruffw/zizmor/issues?q=is%3Aopen+label%3Afalse-negative
|
||||
|
||||
[development docs]: https://woodruffw.github.io/zizmor/development/
|
||||
|
||||
* *Document internal APIs*. `zizmor` doesn't have a public Rust API (yet),
|
||||
but the internal APIs should be documented *as if* they might become public
|
||||
one day. Plus, well-documented internals make life easier for new
|
||||
contributors.
|
||||
* *Write unit tests*. It's easy for small changes in `zizmor`'s internals to
|
||||
percolate into large bugs (e.g. incorrect location information); help us
|
||||
catch these bugs earlier by testing your changes at the smallest unit of
|
||||
behavior.
|
||||
* *Test on real inputs*. If you're contributing to or adding a new audit,
|
||||
make sure your analysis is reliable and accurate on non-sample inputs.
|
||||
|
|
|
|||
18
Makefile
Normal file
18
Makefile
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
VENV = .venv
|
||||
VENV_BIN := $(VENV)/bin
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
@echo "Run my targets individually!"
|
||||
|
||||
.PHONY: site
|
||||
site: $(VENV)
|
||||
$(VENV_BIN)/mkdocs build
|
||||
|
||||
.PHONY: site-live
|
||||
site-live: $(VENV)
|
||||
$(VENV_BIN)/mkdocs serve
|
||||
|
||||
$(VENV): site-requirements.txt
|
||||
uv venv
|
||||
uv pip install -r site-requirements.txt
|
||||
128
README.md
128
README.md
|
|
@ -1,4 +1,4 @@
|
|||
# zizmor
|
||||
# 🌈 zizmor
|
||||
|
||||
[](https://github.com/woodruffw/zizmor/actions/workflows/ci.yml)
|
||||
[](https://crates.io/crates/zizmor)
|
||||
|
|
@ -8,129 +8,17 @@ A tool for finding security issues in GitHub Actions CI/CD setups.
|
|||
> [!IMPORTANT]
|
||||
> `zizmor` is currently in beta. You will encounter bugs; please file them!
|
||||
|
||||
Quick links:
|
||||

|
||||
|
||||
* [Installation](#installation)
|
||||
* [Quick start guide](#quickstart)
|
||||
* [Usage](#usage)
|
||||
* [Online and offline use](#online-and-offline-use)
|
||||
* [Output formats](#output-formats)
|
||||
* [Audit documentation](./docs/audit/)
|
||||
* [Integration](#integration)
|
||||
* [Use in GitHub Actions](#use-in-github-actions)
|
||||
* [Technical details](#technical-details)
|
||||
* [Contributing](#contributing)
|
||||
* [The name?](#the-name)
|
||||
See [`zizmor`'s documentation](https://woodruffw.github.io/zizmor/)
|
||||
for [installation steps], as well as a [quickstart] and
|
||||
[detailed usage recipes].
|
||||
|
||||
Go right to the [Quickstart](#quickstart) or [Usage](#usage) to learn
|
||||
how to use `zizmor` locally or in your CI/CD.
|
||||
[installation steps]: https://woodruffw.github.io/zizmor/installation/
|
||||
|
||||
## Installation
|
||||
[quickstart]: https://woodruffw.github.io/zizmor/installation/
|
||||
|
||||
You can install `zizmor` from <https://crates.io> via `cargo`:
|
||||
|
||||
```bash
|
||||
cargo install zizmor
|
||||
```
|
||||
|
||||
or via [Homebrew](https://brew.sh/):
|
||||
|
||||
```bash
|
||||
brew install zizmor
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
You can run `zizmor` on any file(s) you have locally:
|
||||
|
||||
```bash
|
||||
# audit a specific workflow
|
||||
zizmor my-workflow.yml
|
||||
# discovers .github/workflows/*.yml automatically
|
||||
zizmor path/to/repo
|
||||
```
|
||||
|
||||
By default, `zizmor` will emit a Rust-style human-friendly findings, e.g.:
|
||||
|
||||
```console
|
||||
error[pull-request-target]: use of fundamentally insecure workflow trigger
|
||||
--> /home/william/devel/gha-hazmat/.github/workflows/pull-request-target.yml:20:1
|
||||
|
|
||||
20 | / on:
|
||||
21 | | # NOT OK: pull_request_target should almost never be used
|
||||
22 | | pull_request_target:
|
||||
| |______________________^ triggers include pull_request_target, which is almost always used insecurely
|
||||
|
|
||||
|
||||
1 findings (0 unknown, 0 informational, 0 low, 0 medium, 1 high)
|
||||
```
|
||||
|
||||
See the [Usage](#usage) for more examples, including examples of configuration.
|
||||
|
||||
## Usage
|
||||
|
||||
### Online and offline use
|
||||
|
||||
Some of `zizmor`'s audits require access to GitHub's API. `zizmor` will perform
|
||||
online audits by default *if* the user has a `GH_TOKEN` specified
|
||||
in their environment. If no `GH_TOKEN` is present, then `zizmor` will operate
|
||||
in offline mode by default.
|
||||
|
||||
Both of these can be made explicit through their respective command-line flags:
|
||||
|
||||
```bash
|
||||
# force offline, even if a GH_TOKEN is present
|
||||
zizmor --offline workflow.yml
|
||||
|
||||
# passing a token explicitly will forcefully enable online mode
|
||||
zizmor --gh-token ghp-... workflow.yml
|
||||
```
|
||||
|
||||
### Output formats
|
||||
|
||||
`zizmor` always produces output on `stdout`. If a terminal is detected,
|
||||
`zizmor` will default to a human-readable diagnostic output; if no terminal
|
||||
is detected, `zizmor` will emit JSON.
|
||||
|
||||
Output formats can be controlled explicitly via the `--format` option:
|
||||
|
||||
```bash
|
||||
# force diagnostic output, even if not a terminal
|
||||
zizmor --format plain
|
||||
|
||||
# emit zizmor's own JSON format
|
||||
zizmor --format json
|
||||
|
||||
# emit SARIF JSON instead of normal JSON
|
||||
zizmor --format sarif
|
||||
```
|
||||
|
||||
See [Integration](#integration) for suggestions on when to use each format.
|
||||
|
||||
## Integration
|
||||
|
||||
### Use in GitHub Actions
|
||||
|
||||
`zizmor` is trivial to use within GitHub Actions; you can run it just like
|
||||
you would locally.
|
||||
|
||||
`zizmor --format sarif` specifies [SARIF] as the output format, which GitHub's
|
||||
code scanning feature also supports.
|
||||
|
||||
See [GitHub's documentation] for advice on how to integrate `zizmor`'s results
|
||||
directly into a repository's scanning setup.
|
||||
|
||||
For a specific example, see `zizmor`'s own [repository workflow scan].
|
||||
GitHub's example of [running ESLint] as a security workflow provides additional
|
||||
relevant links.
|
||||
|
||||
[SARIF]: https://sarifweb.azurewebsites.net/
|
||||
|
||||
[GitHub's documentation]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github
|
||||
|
||||
[repository workflow scan]: https://github.com/woodruffw/zizmor/blob/main/.github/workflows/zizmor.yml
|
||||
|
||||
[running ESLint]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github#example-workflow-that-runs-the-eslint-analysis-tool
|
||||
[detailed usage recipes]: https://woodruffw.github.io/zizmor/installation/
|
||||
|
||||
## Technical details
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
# Hacking on `zizmor`
|
||||
|
||||
## Writing documentation
|
||||
|
||||
One of the best ways to help us with `zizmor` is to help us improve our
|
||||
documentation!
|
||||
|
||||
Here are some things we could use help with:
|
||||
|
||||
* Improving the detail in our [audit documentation pages](./audit/).
|
||||
* Improving the detail in our [README](../README.md), e.g. by adding
|
||||
or improving our usage examples.
|
||||
|
||||
More generally, see [issues labeled with `documentation`] for a potential
|
||||
list of documentation efforts to contribute on.
|
||||
|
||||
[issues labeled with `documentation`]: https://github.com/woodruffw/zizmor/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Adocumentation
|
||||
|
||||
## Adding or modifying an audit
|
||||
|
||||
These docs could use help.
|
||||
|
||||
For now, please run `cargo doc --open` and refer to our internal
|
||||
documentation!
|
||||
|
||||
BIN
docs/assets/zizmor-demo.gif
Normal file
BIN
docs/assets/zizmor-demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
|
|
@ -1,33 +0,0 @@
|
|||
# `artipacked`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `artipacked` | Workflow | [`artipacked.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/artipacked.yml)
|
||||
|
||||
## What
|
||||
|
||||
Unexpected credential use and potential credential persistence,
|
||||
typically via GitHub Actions artifact creation or action logs.
|
||||
|
||||
## Why
|
||||
|
||||
The default checkout action is [`actions/checkout`].
|
||||
|
||||
By default, using `actions/checkout` causes a credential to be persisted
|
||||
in the checked-out repo's `.git/config`, so that subsequent `git` operations
|
||||
can be authenticated.
|
||||
|
||||
Subsequent steps may accidentally publicly persist `.git/config`, e.g. by
|
||||
including it in a publicly accessible artifact via [`actions/upload-artifact`].
|
||||
|
||||
However, even without this, persisting the credential in the `.git/config`
|
||||
is non-ideal and should be disabled with `persist-credentials: false` unless
|
||||
the job actually needs the persisted credential.
|
||||
|
||||
## Other resources
|
||||
|
||||
* <https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/>
|
||||
|
||||
[`actions/checkout`]: https://github.com/actions/checkout
|
||||
|
||||
[`actions/upload-artifact`]: https://github.com/actions/upload-artifact
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
# `dangerous-triggers`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `dangerous-triggers` | Workflow | [`pull-request-target.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/pull-request-target.yml)
|
||||
|
||||
## What
|
||||
|
||||
Fundamentally dangerous GitHub Actions workflow triggers.
|
||||
|
||||
## Why
|
||||
|
||||
Many of GitHub's workflow triggers are difficult to use securely.
|
||||
This audit checks for some of the biggest offenders:
|
||||
|
||||
* `pull_request_target`
|
||||
* `workflow_run`
|
||||
|
||||
These triggers are dangerous because they run in the context of the
|
||||
*target repository* rather than the *fork repository*, while also being
|
||||
typically triggerable by the latter. This can lead to attacker controlled
|
||||
code execution or unexpected action runs with context controlled by a malicious
|
||||
fork.
|
||||
|
||||
## Other resources
|
||||
|
||||
* <https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# `excessive-permissions`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `excessive-permissions` | Workflow | [`excessive-permissions.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/excessive-permissions.yml)
|
||||
|
||||
## What
|
||||
|
||||
Looks for excessive permissions in workflows, both at
|
||||
the workflow level and individual job levels.
|
||||
|
||||
## Why
|
||||
|
||||
Users frequently over-scope their workflow and job permissions,
|
||||
or set broad workflow-level permissions without realizing that
|
||||
all jobs inherit those permissions.
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# `hardcoded-container-credentials`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `hardcoded-container-credentials` | Workflow | [`hardcoded-credentials.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/hardcoded-credentials.yml)
|
||||
|
||||
## What
|
||||
|
||||
GitHub Actions allows Docker credentials (usernames and passwords)
|
||||
to be hardcoded in various places within workflows.
|
||||
|
||||
## Why
|
||||
|
||||
Hardcoding credentials is bad.
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# `impostor-commit`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `impostor-commit` | Workflow | [`impostor-commit.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/impostor-commit.yml)
|
||||
|
||||
## What
|
||||
|
||||
GitHub represents a repository and its forks as a "network" of commits.
|
||||
This results in ambiguity about where a commit comes from: a commit
|
||||
that exists only in a fork can be referenced via its parent's
|
||||
`owner/repo` slug, and vice versa.
|
||||
|
||||
## Why
|
||||
|
||||
GitHub's network design can be used to obscure a commit's true origin
|
||||
in a fully-pinned `uses:` workflow reference. This can be used by an attacker
|
||||
to surreptitiously introduce a backdoored action into a victim's workflows(s).
|
||||
|
||||
## Other resources
|
||||
|
||||
* <https://www.chainguard.dev/unchained/what-the-fork-imposter-commits-in-github-actions-and-ci-cd>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# `known-vulnerable-actions`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `known-vulnerable-actions` | Workflow | [`known-vulnerable-actions.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/known-vulnerable-actions.yml)
|
||||
|
||||
## What
|
||||
|
||||
Actions with known, publicly disclosed vulnerabilities are tracked in the
|
||||
[GitHub Advisories database]. Examples of commonly disclosed vulnerabilities
|
||||
in GitHub Actions include [credential disclosure] and code injection
|
||||
via [template injection].
|
||||
|
||||
## Why
|
||||
|
||||
You shouldn't use actions with known vulnerabilities.
|
||||
|
||||
[GitHub Advisories database]: https://github.com/advisories
|
||||
|
||||
[credential disclosure]: ./artipacked.md
|
||||
|
||||
[template injection]: ./template-injection.md
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# `ref-confusion`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `ref-confusion` | Workflow | [`ref-confusion.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/ref-confusion.yml)
|
||||
|
||||
## What
|
||||
|
||||
Like with [impostor commits], actions that are used with a symbolic ref
|
||||
in their `uses:` are subject to a degree of ambiguity: a ref like
|
||||
`@v1` might refer to either a branch or tag ref.
|
||||
|
||||
## Why
|
||||
|
||||
An attacker can exploit this ambiguity to publish a branch or tag ref that
|
||||
takes precedence over a legitimate one, delivering a malicious action to
|
||||
pre-existing consumers of that action without having to modify those consumers.
|
||||
|
||||
[impostor commits]: ./impostor-commits.md
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# `self-hosted-runner`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `self-hosted-runner` | Workflow | [`self-hosted.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/self-hosted-runner.yml)
|
||||
|
||||
## What
|
||||
|
||||
GitHub supports self-hosted runners, which behave similarly to GitHub-hosted
|
||||
runners but use client-managed compute resources.
|
||||
|
||||
## Why
|
||||
|
||||
Self-hosted runners are very hard to secure by default, which is why
|
||||
GitHub does not recommend their use in public repositories.
|
||||
|
||||
## Other resources
|
||||
|
||||
* <https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security>
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# `template-injection`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `template-injection` | Workflow | [`self-hosted.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/template-injection.yml)
|
||||
|
||||
## What
|
||||
|
||||
GitHub Actions allows workflows to define *template expansions*, which
|
||||
occur within special `${{ ... }}` delimiters. These expansions happen
|
||||
before workflow and job execution, meaning the expansion
|
||||
of a given expression appears verbatim in whatever context it was performed in.
|
||||
|
||||
## Why
|
||||
|
||||
Template expansions aren't syntax-aware, meaning that they can result in
|
||||
unintended shell injection vectors. This is especially true when they're
|
||||
used with attacker-controllable expression contexts, such as
|
||||
`github.event.issue.title` (which the attacker can fully control by supplying
|
||||
a new issue title).
|
||||
|
||||
## Other resources
|
||||
|
||||
* <https://securitylab.github.com/resources/github-actions-untrusted-input/>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# `use-trusted-publishing`
|
||||
|
||||
| Audit ID | Type | Examples |
|
||||
| -------- | ---- | -------- |
|
||||
| `use-trusted-publishing` | Workflow | [`pypi-manual-credential.yml`](https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/pypi-manual-credential.yml)
|
||||
|
||||
## What
|
||||
|
||||
Some packaging ecosystems/indices (like PyPI and RubyGems) support
|
||||
"Trusted Publishing," which is an OIDC-based "tokenless" authentication
|
||||
mechanism for uploading to the index from within a CI/CD workflow.
|
||||
|
||||
## Why
|
||||
|
||||
This "tokenless" flow has significant security benefits over a traditional
|
||||
manually configured API token, and should be preferred wherever supported
|
||||
and possible.
|
||||
|
||||
## Other resources
|
||||
|
||||
* <https://docs.pypi.org/trusted-publishers/>
|
||||
* <https://guides.rubygems.org/trusted-publishing/>
|
||||
* <https://blog.trailofbits.com/2023/05/23/trusted-publishing-a-new-benchmark-for-packaging-security/>
|
||||
255
docs/audits.md
Normal file
255
docs/audits.md
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
# Audit Rules
|
||||
|
||||
This page documents each of the audits currently implemented in `zizmor`.
|
||||
|
||||
See each audit's section for its scope, behavior, and other information.
|
||||
|
||||
## `artipacked`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [artipacked.yml] | v0.1.0 |
|
||||
|
||||
[artipacked.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/artipacked.yml
|
||||
|
||||
### What
|
||||
|
||||
Unexpected credential use and potential credential persistence,
|
||||
typically via GitHub Actions artifact creation or action logs.
|
||||
|
||||
### Why
|
||||
|
||||
The default checkout action is [`actions/checkout`].
|
||||
|
||||
By default, using `actions/checkout` causes a credential to be persisted
|
||||
in the checked-out repo's `.git/config`, so that subsequent `git` operations
|
||||
can be authenticated.
|
||||
|
||||
Subsequent steps may accidentally publicly persist `.git/config`, e.g. by
|
||||
including it in a publicly accessible artifact via [`actions/upload-artifact`].
|
||||
|
||||
However, even without this, persisting the credential in the `.git/config`
|
||||
is non-ideal and should be disabled with `persist-credentials: false` unless
|
||||
the job actually needs the persisted credential.
|
||||
|
||||
### Other resources
|
||||
|
||||
* <https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/>
|
||||
|
||||
[`actions/checkout`]: https://github.com/actions/checkout
|
||||
|
||||
[`actions/upload-artifact`]: https://github.com/actions/upload-artifact
|
||||
|
||||
## `dangerous-triggers`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [pull-request-target.yml] | v0.1.0 |
|
||||
|
||||
[pull-request-target.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/pull-request-target.yml
|
||||
|
||||
### What
|
||||
|
||||
Fundamentally dangerous GitHub Actions workflow triggers.
|
||||
|
||||
### Why
|
||||
|
||||
Many of GitHub's workflow triggers are difficult to use securely.
|
||||
This audit checks for some of the biggest offenders:
|
||||
|
||||
* `pull_request_target`
|
||||
* `workflow_run`
|
||||
|
||||
These triggers are dangerous because they run in the context of the
|
||||
*target repository* rather than the *fork repository*, while also being
|
||||
typically triggerable by the latter. This can lead to attacker controlled
|
||||
code execution or unexpected action runs with context controlled by a malicious
|
||||
fork.
|
||||
|
||||
### Other resources
|
||||
|
||||
* <https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/>
|
||||
|
||||
## `excessive-permissions`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [excessive-permissions.yml] | v0.1.0 |
|
||||
|
||||
[excessive-permissions.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/excessive-permissions.yml
|
||||
|
||||
### What
|
||||
|
||||
Looks for excessive permissions in workflows, both at
|
||||
the workflow level and individual job levels.
|
||||
|
||||
### Why
|
||||
|
||||
Users frequently over-scope their workflow and job permissions,
|
||||
or set broad workflow-level permissions without realizing that
|
||||
all jobs inherit those permissions.
|
||||
|
||||
## `hardcoded-container-credentials`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [hardcoded-credentials.yml] | v0.1.0 |
|
||||
|
||||
[hardcoded-credentials.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/hardcoded-credentials.yml
|
||||
|
||||
### What
|
||||
|
||||
GitHub Actions allows Docker credentials (usernames and passwords)
|
||||
to be hardcoded in various places within workflows.
|
||||
|
||||
### Why
|
||||
|
||||
Hardcoding credentials is bad.
|
||||
|
||||
## `impostor-commit`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [impostor-commit.yml] | v0.1.0 |
|
||||
|
||||
[impostor-commit.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/impostor-commit.yml
|
||||
|
||||
### What
|
||||
|
||||
GitHub represents a repository and its forks as a "network" of commits.
|
||||
This results in ambiguity about where a commit comes from: a commit
|
||||
that exists only in a fork can be referenced via its parent's
|
||||
`owner/repo` slug, and vice versa.
|
||||
|
||||
### Why
|
||||
|
||||
GitHub's network-of-forks design can be used to obscure a commit's true origin
|
||||
in a fully-pinned `uses:` workflow reference. This can be used by an attacker
|
||||
to surreptitiously introduce a backdoored action into a victim's workflows(s).
|
||||
|
||||
### Other resources
|
||||
|
||||
* <https://www.chainguard.dev/unchained/what-the-fork-imposter-commits-in-github-actions-and-ci-cd>
|
||||
|
||||
## `known-vulnerable-actions`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [known-vulnerable-actions.yml] | v0.1.0 |
|
||||
|
||||
[known-vulnerable-actions.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/known-vulnerable-actions.yml
|
||||
|
||||
### What
|
||||
|
||||
Actions with known, publicly disclosed vulnerabilities are tracked in the
|
||||
[GitHub Advisories database]. Examples of commonly disclosed vulnerabilities
|
||||
in GitHub Actions include [credential disclosure] and code injection
|
||||
via [template injection].
|
||||
|
||||
### Why
|
||||
|
||||
You shouldn't use actions with known vulnerabilities.
|
||||
|
||||
[GitHub Advisories database]: https://github.com/advisories
|
||||
|
||||
[credential disclosure]: #artipacked
|
||||
|
||||
[template injection]: #template-injection
|
||||
|
||||
## `ref-confusion`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [ref-confusion.yml] | v0.1.0 |
|
||||
|
||||
[ref-confusion.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/ref-confusion.yml
|
||||
|
||||
### What
|
||||
|
||||
Like with [impostor commits], actions that are used with a symbolic ref
|
||||
in their `uses:` are subject to a degree of ambiguity: a ref like
|
||||
`@v1` might refer to either a branch or tag ref.
|
||||
|
||||
### Why
|
||||
|
||||
An attacker can exploit this ambiguity to publish a branch or tag ref that
|
||||
takes precedence over a legitimate one, delivering a malicious action to
|
||||
pre-existing consumers of that action without having to modify those consumers.
|
||||
|
||||
[impostor commits]: #impostor-commit
|
||||
|
||||
## `self-hosted-runner`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [self-hosted.yml] | v0.1.0 |
|
||||
|
||||
[self-hosted.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/self-hosted.yml
|
||||
|
||||
### What
|
||||
|
||||
GitHub supports self-hosted runners, which behave similarly to GitHub-hosted
|
||||
runners but use client-managed compute resources.
|
||||
|
||||
### Why
|
||||
|
||||
Self-hosted runners are very hard to secure by default, which is why
|
||||
GitHub does not recommend their use in public repositories.
|
||||
|
||||
### Other resources
|
||||
|
||||
* <https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security>
|
||||
|
||||
## `template-injection`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [template-injection.yml] | v0.1.0 |
|
||||
|
||||
[template-injection.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/template-injection.yml
|
||||
|
||||
|
||||
### What
|
||||
|
||||
GitHub Actions allows workflows to define *template expansions*, which
|
||||
occur within special `${{ ... }}` delimiters. These expansions happen
|
||||
before workflow and job execution, meaning the expansion
|
||||
of a given expression appears verbatim in whatever context it was performed in.
|
||||
|
||||
### Why
|
||||
|
||||
Template expansions aren't syntax-aware, meaning that they can result in
|
||||
unintended shell injection vectors. This is especially true when they're
|
||||
used with attacker-controllable expression contexts, such as
|
||||
`github.event.issue.title` (which the attacker can fully control by supplying
|
||||
a new issue title).
|
||||
|
||||
### Other resources
|
||||
|
||||
* <https://securitylab.github.com/resources/github-actions-untrusted-input/>
|
||||
|
||||
## `use-trusted-publishing`
|
||||
|
||||
| Type | Examples | Introduced in |
|
||||
| ---- | -------- | ------------- |
|
||||
| Workflow | [pypi-manual-credential.yml] | v0.1.0 |
|
||||
|
||||
[pypi-manual-credential.yml]: https://github.com/woodruffw/gha-hazmat/blob/main/.github/workflows/pypi-manual-credential.yml
|
||||
|
||||
### What
|
||||
|
||||
Some packaging ecosystems/indices (like PyPI and RubyGems) support
|
||||
"Trusted Publishing," which is an OIDC-based "tokenless" authentication
|
||||
mechanism for uploading to the index from within a CI/CD workflow.
|
||||
|
||||
### Why
|
||||
|
||||
This "tokenless" flow has significant security benefits over a traditional
|
||||
manually configured API token, and should be preferred wherever supported
|
||||
and possible.
|
||||
|
||||
### Other resources
|
||||
|
||||
* <https://docs.pypi.org/trusted-publishers/>
|
||||
* <https://guides.rubygems.org/trusted-publishing/>
|
||||
* <https://blog.trailofbits.com/2023/05/23/trusted-publishing-a-new-benchmark-for-packaging-security/>
|
||||
118
docs/development.md
Normal file
118
docs/development.md
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# Hacking on `zizmor`
|
||||
|
||||
!!! important
|
||||
|
||||
This page contains information on specific development processes.
|
||||
For more general information on *how and what* to contribute to `zizmor`,
|
||||
see our [CONTRIBUTING.md].
|
||||
|
||||
[CONTRIBUTING.md]: https://github.com/woodruffw/zizmor/blob/main/CONTRIBUTING.md
|
||||
|
||||
## General development practices
|
||||
|
||||
Here are some guidelines to follow if you're working on `zizmor`:
|
||||
|
||||
* *Document internal APIs*. `zizmor` doesn't have a public Rust API (yet),
|
||||
but the internal APIs should be documented *as if* they might become public
|
||||
one day. Plus, well-documented internals make life easier for new
|
||||
contributors.
|
||||
* *Write unit tests*. It's easy for small changes in `zizmor`'s internals to
|
||||
percolate into large bugs (e.g. incorrect location information); help us
|
||||
catch these bugs earlier by testing your changes at the smallest unit of
|
||||
behavior.
|
||||
* *Test on real inputs*. If you're contributing to or adding a new audit,
|
||||
make sure your analysis is reliable and accurate on non-sample inputs.
|
||||
|
||||
## Requirements
|
||||
|
||||
`zizmor`'s only development requirement the Rust compiler.
|
||||
|
||||
You can install Rust by following the steps on [Rust's official website].
|
||||
|
||||
[Rust's official website]: https://www.rust-lang.org/tools/install
|
||||
|
||||
## Building `zizmor` locally
|
||||
|
||||
`zizmor` is a pure Rust codebase, and can be built with a single `cargo build`:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/woodruffw/zizmor && cd zizmor
|
||||
cargo build
|
||||
# cargo run -- --help also works
|
||||
./target/debug/zizmor --help
|
||||
```
|
||||
|
||||
Similarly, you can build the developer-only documentation with
|
||||
`cargo doc`:
|
||||
|
||||
```bash
|
||||
# build only
|
||||
cargo doc
|
||||
|
||||
# build and open in the local browser
|
||||
cargo doc --open
|
||||
```
|
||||
|
||||
### Linting
|
||||
|
||||
`zizmor` is linted with `cargo clippy` and auto-formatted with `cargo fmt`.
|
||||
Our CI enforces both, but you should also run them locally to minimize
|
||||
unnecessary review cycles:
|
||||
|
||||
```bash
|
||||
cargo fmt
|
||||
cargo clippy --fix
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
`zizmor` uses `cargo test`:
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
|
||||
## Building the website
|
||||
|
||||
`zizmor`'s website is built with [MkDocs](https://www.mkdocs.org/), which
|
||||
means you'll need a Python runtime to develop against it locally.
|
||||
|
||||
The easiest way to do this is to use [`uv`](https://github.com/astral-sh/uv),
|
||||
which is what `zizmor`'s own CI uses. See
|
||||
[the `uv` docs](https://docs.astral.sh/uv/getting-started/installation/) for
|
||||
installation instructions.
|
||||
|
||||
Once you have `uv`, run `make site` in the repo root to build a local
|
||||
copy of `zizmor`'s website in the `site_html` directory:
|
||||
|
||||
```bash
|
||||
make site
|
||||
```
|
||||
|
||||
Alternatively, for live development, you can run `make site-live`
|
||||
to run a development server that'll monitor for changes to the docs:
|
||||
|
||||
```bash
|
||||
make site-live
|
||||
```
|
||||
|
||||
With `make site-live`, you should see something roughly like this:
|
||||
|
||||
```console
|
||||
INFO - Building documentation...
|
||||
INFO - Cleaning site directory
|
||||
INFO - Documentation built in 0.40 seconds
|
||||
INFO - [22:18:39] Watching paths for changes: 'docs', 'mkdocs.yml'
|
||||
INFO - [22:18:39] Serving on http://127.0.0.1:9999/zizmor/
|
||||
INFO - [22:18:40] Browser connected: http://127.0.0.1:9999/zizmor/development/
|
||||
```
|
||||
|
||||
Visit the listed URL to see your live changes.
|
||||
|
||||
## Adding or modifying an audit
|
||||
|
||||
These docs could use help.
|
||||
|
||||
For now, please run `cargo doc --open` and refer to our internal
|
||||
documentation!
|
||||
|
||||
22
docs/index.md
Normal file
22
docs/index.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Welcome to `zizmor`'s documentation!
|
||||
|
||||
[](https://github.com/woodruffw/zizmor/actions/workflows/ci.yml)
|
||||
[](https://crates.io/crates/zizmor)
|
||||
|
||||
!!! warning
|
||||
|
||||
`zizmor` is currently in beta. You will encounter bugs; [please file them]!
|
||||
|
||||
[please file them]: https://github.com/woodruffw/zizmor/issues/new
|
||||
|
||||
:rainbow: Hello, and welcome to `zizmor`'s documentation! :rainbow:
|
||||
|
||||
`zizmor` is a tool for finding security issues in GitHub Actions setups.
|
||||
|
||||
Go right to our [Installation Steps](./installation.md), and then to
|
||||
[Quickstart](./quickstart.md) or [Usage Recipes](./usage.md) to
|
||||
learn more about how to use `zizmor` locally or in your CI/CD.
|
||||
|
||||
<figure markdown="1">
|
||||

|
||||
</figure>
|
||||
36
docs/installation.md
Normal file
36
docs/installation.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Installation
|
||||
|
||||
!!! info
|
||||
|
||||
Are you interested in packaging `zizmor` for another ecosystem!
|
||||
Let us know by [filing an issue](https://github.com/woodruffw/zizmor/issues/new)!
|
||||
|
||||
## From crates.io
|
||||
|
||||
You can install `zizmor` from <https://crates.io> with `cargo`:
|
||||
|
||||
```bash
|
||||
cargo install zizmor
|
||||
```
|
||||
|
||||
## From Homebrew
|
||||
|
||||
`zizmor` is provided by [Homebrew](https://brew.sh/) as well:
|
||||
|
||||
```bash
|
||||
brew install zizmor
|
||||
```
|
||||
|
||||
## From source
|
||||
|
||||
!!! warning
|
||||
|
||||
Most ordinary users **should not** install directly from `zizmor`'s
|
||||
source repository. No stability or correctness guarantees are made about
|
||||
direct source installations.
|
||||
|
||||
You can install the latest unstable `zizmor` directly from GitHub with `cargo`:
|
||||
|
||||
```bash
|
||||
cargo install --git https://github.com/woodruffw/zizmor
|
||||
```
|
||||
27
docs/quickstart.md
Normal file
27
docs/quickstart.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Quickstart
|
||||
|
||||
You can run `zizmor` on any file(s) you have locally:
|
||||
|
||||
```bash
|
||||
# audit a specific workflow
|
||||
zizmor my-workflow.yml
|
||||
# discovers .github/workflows/*.yml automatically
|
||||
zizmor path/to/repo
|
||||
```
|
||||
|
||||
By default, `zizmor` will emit a Rust-style human-friendly findings, e.g.:
|
||||
|
||||
```console
|
||||
error[pull-request-target]: use of fundamentally insecure workflow trigger
|
||||
--> /home/william/devel/gha-hazmat/.github/workflows/pull-request-target.yml:20:1
|
||||
|
|
||||
20 | / on:
|
||||
21 | | # NOT OK: pull_request_target should almost never be used
|
||||
22 | | pull_request_target:
|
||||
| |______________________^ triggers include pull_request_target, which is almost always used insecurely
|
||||
|
|
||||
|
||||
1 findings (0 unknown, 0 informational, 0 low, 0 medium, 1 high)
|
||||
```
|
||||
|
||||
See [Usage](./usage.md) for more examples, including examples of configuration.
|
||||
64
docs/usage.md
Normal file
64
docs/usage.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Usage Recipes
|
||||
|
||||
## Online and offline use
|
||||
|
||||
Some of `zizmor`'s audits require access to GitHub's API. `zizmor` will perform
|
||||
online audits by default *if* the user has a `GH_TOKEN` specified
|
||||
in their environment. If no `GH_TOKEN` is present, then `zizmor` will operate
|
||||
in offline mode by default.
|
||||
|
||||
Both of these can be made explicit through their respective command-line flags:
|
||||
|
||||
```bash
|
||||
# force offline, even if a GH_TOKEN is present
|
||||
zizmor --offline workflow.yml
|
||||
|
||||
# passing a token explicitly will forcefully enable online mode
|
||||
zizmor --gh-token ghp-... workflow.yml
|
||||
```
|
||||
|
||||
## Output formats
|
||||
|
||||
`zizmor` always produces output on `stdout`. If a terminal is detected,
|
||||
`zizmor` will default to a human-readable diagnostic output; if no terminal
|
||||
is detected, `zizmor` will emit JSON.
|
||||
|
||||
Output formats can be controlled explicitly via the `--format` option:
|
||||
|
||||
```bash
|
||||
# force diagnostic output, even if not a terminal
|
||||
zizmor --format plain
|
||||
|
||||
# emit zizmor's own JSON format
|
||||
zizmor --format json
|
||||
|
||||
# emit SARIF JSON instead of normal JSON
|
||||
zizmor --format sarif
|
||||
```
|
||||
|
||||
See [Integration](#integration) for suggestions on when to use each format.
|
||||
|
||||
## Integration
|
||||
|
||||
### Use in GitHub Actions
|
||||
|
||||
`zizmor` is trivial to use within GitHub Actions; you can run it just like
|
||||
you would locally.
|
||||
|
||||
`zizmor --format sarif` specifies [SARIF] as the output format, which GitHub's
|
||||
code scanning feature also supports.
|
||||
|
||||
See [GitHub's documentation] for advice on how to integrate `zizmor`'s results
|
||||
directly into a repository's scanning setup.
|
||||
|
||||
For a specific example, see `zizmor`'s own [repository workflow scan].
|
||||
GitHub's example of [running ESLint] as a security workflow provides additional
|
||||
relevant links.
|
||||
|
||||
[SARIF]: https://sarifweb.azurewebsites.net/
|
||||
|
||||
[GitHub's documentation]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github
|
||||
|
||||
[repository workflow scan]: https://github.com/woodruffw/zizmor/blob/main/.github/workflows/zizmor.yml
|
||||
|
||||
[running ESLint]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github#example-workflow-that-runs-the-eslint-analysis-tool
|
||||
42
mkdocs.yml
Normal file
42
mkdocs.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
site_name: "🌈 zizmor"
|
||||
# site_description: zizmor
|
||||
site_url: https://woodruffw.github.io/zizmor
|
||||
docs_dir: docs
|
||||
site_dir: site_html
|
||||
dev_addr: "127.0.0.1:9999"
|
||||
|
||||
nav:
|
||||
- Welcome: "index.md"
|
||||
- "installation.md"
|
||||
- "quickstart.md"
|
||||
- "usage.md"
|
||||
- "audits.md"
|
||||
- "development.md"
|
||||
- External links:
|
||||
- "GitHub 🔗": https://github.com/woodruffw/zizmor
|
||||
- "crates.io 🔗": https://crates.io/crates/zizmor
|
||||
|
||||
theme:
|
||||
name: material
|
||||
palette:
|
||||
scheme: slate
|
||||
features:
|
||||
- navigation.expand
|
||||
- navigation.sections
|
||||
- navigation.tracking
|
||||
|
||||
markdown_extensions:
|
||||
# Makes sure we render `<https://...>`-style links correctly.
|
||||
- pymdownx.magiclink
|
||||
- pymdownx.emoji
|
||||
- admonition
|
||||
- pymdownx.details
|
||||
- pymdownx.superfences
|
||||
- md_in_html
|
||||
- toc:
|
||||
permalink: 🔗
|
||||
|
||||
validation:
|
||||
omitted_files: warn
|
||||
absolute_links: warn
|
||||
unrecognized_links: warn
|
||||
2
site-requirements.txt
Normal file
2
site-requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
mkdocs ~= 1.6
|
||||
mkdocs-material ~= 9.5
|
||||
Loading…
Add table
Add a link
Reference in a new issue