Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d9d67f5
Add static OWASP Hunting website and GitHub Pages deployment
0x5t4l1n Oct 15, 2026
c766521
Document how to contribute to the website
0x5t4l1n Oct 15, 2026
347373b
Merge pull request #17 from OWASP/docs/website-contributing
0x5t4l1n Oct 15, 2026
fe4aecf
Add one-way docs-hunting website mirror
0x5t4l1n Oct 15, 2026
96260b6
Clarify reported Netlify connection
0x5t4l1n Oct 15, 2026
dcec064
Merge pull request #18 from OWASP/chore/sync-docs-hunting-netlify
0x5t4l1n Oct 15, 2026
19d1651
Forward OWASP Pages visitors to primary website
0x5t4l1n Oct 15, 2026
c76636c
Merge pull request #19 from OWASP/feat/redirect-pages-to-hunting
0x5t4l1n Oct 15, 2026
992e15d
Remove default browser favicon
0x5t4l1n Oct 15, 2026
c5c90ee
Merge pull request #20 from OWASP/fix/owasp-browser-icon
0x5t4l1n Oct 15, 2026
a3631d2
fix: keep website mobile navigation behavior scoped to its controls
0x5t4l1n Oct 27, 2026
83c29a3
fix: resolve website category anchors below the sticky header
0x5t4l1n Oct 27, 2026
7f4d1db
Merge pull request #48 from 0x5t4l1n/fix-website-section-anchors
0x5t4l1n Oct 27, 2026
ac81655
Merge pull request #47 from 0x5t4l1n/fix-website-mobile-navigation
0x5t4l1n Oct 27, 2026
f8de91c
Fix ordered list rendering in documentation
Ashishchoudhary07 Oct 27, 2026
f6f47e7
Merge pull request #56 from Ashishchoudhary07/fix/ordered-doc-lists
0x5t4l1n Oct 27, 2026
052456d
Fix Markdown blockquote rendering
Ashishchoudhary07 Oct 27, 2026
1731e11
Merge pull request #65 from Ashishchoudhary07/fix/blockquote-rendering
0x5t4l1n Oct 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/deploy-website.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Deploy website to GitHub Pages

on:
push:
branches: [website]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: github-pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
- run: npm ci
- run: npx tsc --noEmit
- run: npm run lint
- run: npm run build
- run: python3 scripts/validate_website_export.py
- uses: actions/upload-pages-artifact@v3
with:
path: out

deploy:
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
72 changes: 72 additions & 0 deletions .github/workflows/sync-docs-hunting.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Mirror website to docs-hunting

on:
push:
branches: [website]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: docs-hunting-mirror-main
cancel-in-progress: false

jobs:
sync:
if: github.repository == 'OWASP/OWASP-Hunting' && github.ref == 'refs/heads/website'
runs-on: ubuntu-latest
steps:
- name: Check dedicated destination token
env:
DOCS_SYNC_TOKEN: ${{ secrets.DOCS_SYNC_TOKEN }}
run: |
if [ -z "$DOCS_SYNC_TOKEN" ]; then
echo "::error::Add DOCS_SYNC_TOKEN to OWASP-Hunting Actions secrets before enabling the mirror."
exit 1
fi

- name: Check out OWASP website
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Check out deployment mirror
uses: actions/checkout@v4
with:
repository: th30d4y/docs-hunting
ref: main
path: docs-hunting
token: ${{ secrets.DOCS_SYNC_TOKEN }}
persist-credentials: false

- name: Copy managed website files
run: python3 scripts/sync_docs_hunting.py "$GITHUB_WORKSPACE" "$GITHUB_WORKSPACE/docs-hunting"

- name: Commit and push mirror changes
env:
DOCS_SYNC_TOKEN: ${{ secrets.DOCS_SYNC_TOKEN }}
SOURCE_SHA: ${{ github.sha }}
run: |
set -euo pipefail
cd docs-hunting
git add -A -- src public package.json package-lock.json eslint.config.mjs postcss.config.mjs tsconfig.json .owasp-website-sync.json
if git diff --cached --quiet; then
echo "Mirror is already current."
exit 0
fi
git config user.name "OWASP Hunting Sync"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -m "Sync website from OWASP-Hunting" -m "Source commit: $SOURCE_SHA"

askpass="$RUNNER_TEMP/docs-sync-askpass.sh"
cat > "$askpass" <<'ASKPASS'
#!/bin/sh
case "$1" in
*Username*) printf '%s\n' 'x-access-token' ;;
*Password*) printf '%s\n' "$DOCS_SYNC_TOKEN" ;;
*) exit 1 ;;
esac
ASKPASS
chmod 700 "$askpass"
GIT_ASKPASS="$askpass" GIT_TERMINAL_PROMPT=0 git push origin HEAD:main
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,44 @@ Thumbs.db
node_modules/
dist/
build/
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- BEGIN:nextjs-agent-rules -->

## This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.

This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.

<!-- END:nextjs-agent-rules -->
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

This is the canonical contribution guide for the project.

For changes to the GitHub Pages documentation site, follow [Contributing to the OWASP Hunting website](./WEBSITE_CONTRIBUTING.md) and open a PR against `website`.

## Rules

- Content must be legal, ethical, and support authorized security testing only.
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,25 @@ cat SQL-Injection/README.md
grep -r "onerror=" XSS/xss-payloads.txt
```

No build or install step — it's plain Markdown and text files. Read a category's `README.md` first; it gives the context the payload list assumes.
The payload collection is plain Markdown and text files. Read a category's `README.md` first; it gives the context the payload list assumes.

## Website

The documentation website lives on the [`website`](https://github.com/OWASP/OWASP-Hunting/tree/website) branch. Its primary URL is [hunting.0d4y.dev](https://hunting.0d4y.dev/); the [OWASP GitHub Pages URL](https://owasp.github.io/OWASP-Hunting/) forwards visitors there. To build it locally on that branch:

```bash
npm ci
npx tsc --noEmit
npm run lint
npm run build
python3 scripts/validate_website_export.py
```

The static site is generated in `out/`. Pushes to `website` deploy through [the GitHub Pages workflow](.github/workflows/deploy-website.yml). The original website's README and attribution are preserved in [`website-source/`](website-source/ATTRIBUTION.md).

To improve the site, follow the [website contribution guide](./WEBSITE_CONTRIBUTING.md) and target the `website` branch in your pull request.

The [deployment mirror guide](./WEBSITE_MIRROR.md) explains the optional one-way sync from `website` to `th30d4y/docs-hunting` and its required destination token.

## Contributing

Expand Down
69 changes: 69 additions & 0 deletions WEBSITE_CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Contributing to the OWASP Hunting website

This guide is for the documentation website at https://hunting.0d4y.dev/. The OWASP GitHub Pages URL forwards visitors to that primary domain. Website changes are reviewed against the **`website` branch**. For new security payloads or changes to the canonical payload collection, use the [general contribution guide](./CONTRIBUTING.md) and target `main`.

## Find the right files

| What you want to change | Where to look |
| --- | --- |
| Pages and routes | `src/app/` |
| Shared layout, navigation, search, and UI | `src/components/` and `src/lib/navigation.ts` |
| Documentation text and website payload reference data | `src/lib/docs-data.ts` and `src/lib/payload-data.ts` |
| Search entries | `src/lib/search-data.ts` |
| Styles and static images | `src/app/globals.css` and `public/` |
| Static export and deployment | `next.config.ts` and `.github/workflows/deploy-website.yml` |

The website's reference data is maintained in its source files. If a change also adds or corrects a payload in the canonical collection, submit that payload change separately against `main` so it receives the project's payload review.

## Start a website change

1. Fork [OWASP/OWASP-Hunting](https://github.com/OWASP/OWASP-Hunting), then clone **your fork**.
2. Create your working branch from the latest official `website` branch:

```bash
git clone https://github.com/YOUR_USERNAME/OWASP-Hunting.git
cd OWASP-Hunting
git remote add upstream https://github.com/OWASP/OWASP-Hunting.git
git fetch upstream website
git switch -c docs/improve-website upstream/website
```

3. Make a focused change. Keep the existing layout, accessible labels, keyboard behavior, and small-screen navigation working. Check factual content and credit any material or assets you did not create.
4. Preview locally with Node.js 22:

```bash
npm ci
npm run dev
```

Open `http://localhost:3000/OWASP-Hunting/` and inspect the changed page and its navigation.

## Keep the GitHub Pages export working

This site uses the Next.js App Router with `output: 'export'`. GitHub Pages serves files generated at build time from `out/`; it does not run a Next.js server.

- Keep links and assets working under the `/OWASP-Hunting` base path. `next/link` applies it to internal routes; plain HTML links and references to files in `public/` need the prefix explicitly.
- Generate every dynamic route at build time with `generateStaticParams`. Avoid features that require request-time rendering, API routes, or Server Actions.
- Preserve `public/.nojekyll`, which allows Pages to serve `_next/` assets. Do not add a `CNAME`; the source site's former domain is archived in `website-source/` and is not the OWASP site domain.
- Keep payload examples within the project's [responsible-use policy](./DISCLAIMER.md). Never commit secrets or private data.

Before pushing, run the same checks as the deployment workflow:

```bash
npx tsc --noEmit
npm run lint
npm run build
python3 scripts/validate_website_export.py
```

The build must create `out/`, and the validator must pass. Review the changed page in a browser, including its links, images, and mobile layout. `out/` and `node_modules/` are generated files and should not be committed.

## Open a pull request

Push your branch to your fork and open a PR with **base repository `OWASP/OWASP-Hunting`, base branch `website`**, and your fork branch as the head. GitHub may suggest `main` by default, so check the base branch before submitting.

```bash
git push -u origin docs/improve-website
```

In the PR template, describe the website change, why it helps, the pages or files affected, and the checks and browser review you performed. Use `N/A — website-only change` for payload duplicate checking when no payload files changed. Include source attribution and the [AI assistance disclosure](./CONTRIBUTING.md#ai-assisted-contributions) when applicable. The website is deployed automatically after a change is merged into `website`; opening a PR does not publish it.
65 changes: 65 additions & 0 deletions WEBSITE_MIRROR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Website deployment mirror

`OWASP/OWASP-Hunting:website` is the source of truth for website code and content. [`.github/workflows/sync-docs-hunting.yml`](./.github/workflows/sync-docs-hunting.yml) copies reviewed changes to `th30d4y/docs-hunting:main`, where a normal commit can trigger the destination's hosting integration.

```text
Contributor fork → PR into OWASP-Hunting:website → merge
→ OWASP GitHub Actions → docs-hunting:main commit → destination deployment
```

The workflow runs only for pushes to `website` or manual dispatch **from `website`** in `OWASP/OWASP-Hunting`. Pull request events cannot access the destination token or run the sync job. The destination is never force-pushed or rebased.

The OWASP GitHub Pages URL forwards browser visitors to `https://hunting.0d4y.dev/`, preserving the route, query, and fragment. The forwarding script runs only on `owasp.github.io` and is included only in the OWASP build; the deployment mirror does not redirect to itself. GitHub Pages serves this as a browser redirect, so it requires JavaScript.

## Current destination state

As checked on 2026-10-15, `docs-hunting` has only a `main` branch. Its repository also publishes `hunting.0d4y.dev` through **GitHub Pages** from `main`, and its tracked workflow is `.github/workflows/deploy.yml`. No `netlify.toml` was present. The destination owner reports that the repository is connected to Netlify, but the connected Netlify account available during setup could not verify that site's settings or deployments. The mirror is prepared for `main` and does not change Netlify settings or disable the existing GitHub Pages workflow.

If Netlify uses a different production branch, update the destination checkout `ref`, push ref, and concurrency group in the sync workflow before enabling it.

## Files owned by the source

The mirror copies `src/`, `public/`, `package.json`, `package-lock.json`, `eslint.config.mjs`, `postcss.config.mjs`, and `tsconfig.json`. A manifest (`.owasp-website-sync.json`) records copied files. Later runs remove only files listed in that manifest when they have been removed from the source. Unrelated destination files are never removed.

On the first run, pre-existing destination files that are absent from the source stay in place because they have no manifest history. Review those files separately if they appear to be obsolete website content; the sync will not guess which destination-only files are safe to remove.

These destination-specific files are preserved:

| Destination file or setting | Why it stays on the destination |
| --- | --- |
| `next.config.ts` | The destination has no `/OWASP-Hunting` base path and retains its own build settings. |
| `src/lib/site-variant.ts` | Defines the destination URL and empty base path. The first sync creates it from `public/CNAME` if it does not exist. Later syncs preserve it. |
| `CNAME`, `public/CNAME`, `public/robots.txt` | Keep the destination domain and indexing rules. |
| `.github/`, `README.md`, `AGENTS.md`, `CLAUDE.md`, `.gitignore`, `netlify.toml` if present | Keep destination workflows, documentation, developer instructions, ignore rules, and hosting configuration. |

The source's `src/lib/site-variant.ts` selects the OWASP GitHub Pages URL and base path. Its navigation, metadata, and Markdown links use that module, so the same website code works with the destination's preserved variant. If the Netlify site's public URL differs from `public/CNAME`, set the destination variant's `url` to the intended production URL.

## Enable the sync

1. Create a **dedicated fine-grained personal access token** with resource owner `th30d4y`, selected repository `docs-hunting`, and repository permission **Contents: Read and write**. GitHub includes Metadata: Read automatically. Give it an expiration and rotate it before expiry.
2. In `OWASP/OWASP-Hunting`, open **Settings → Secrets and variables → Actions → New repository secret**. Name it `DOCS_SYNC_TOKEN` and paste the token there. Do not add the token to source files, PR text, or workflow logs. The source repository's `GITHUB_TOKEN` retains only `contents: read`.
3. Confirm that the existing Netlify connection for `th30d4y/docs-hunting` builds `main`, with its existing build command and publish directory. This workflow does not configure Netlify.
4. Review and merge the setup change into `website` only after the secret and destination branch are ready. That merge triggers the first sync. A manual run must select the `website` ref.

The sync uses the token for a destination checkout and for a single non-force `git push`. It stores no credential in Git configuration and uses a temporary askpass helper so the token is not included in a command line or printed in logs.

## Validate safely before enabling

The copy script can be run against a **disposable clone** of `docs-hunting`; it never pushes:

```bash
git clone https://github.com/th30d4y/docs-hunting.git /tmp/docs-hunting-sync-test
python3 scripts/sync_docs_hunting.py "$PWD" /tmp/docs-hunting-sync-test
git -C /tmp/docs-hunting-sync-test status --short
```

Inspect the diff and verify the destination's `CNAME`, `public/CNAME`, `public/robots.txt`, `next.config.ts`, workflows, and any Netlify configuration are unchanged. Run the script again to confirm it produces no further changes.

## Troubleshooting

- **Missing token:** Add `DOCS_SYNC_TOKEN` to the OWASP repository's Actions secrets before merging the workflow.
- **Checkout or push returns 403:** Check token expiration, selected repository, Contents write permission, and any token approval policy. Do not paste the token into logs or rerun with command tracing.
- **Non-fast-forward push rejected:** Someone updated `docs-hunting:main` during the job. Inspect that commit, then rerun the workflow from `website`; never force-push.
- **No destination commit:** The managed files already match the source. This is the expected result for an idempotent rerun.
- **Destination build has the wrong URL or paths:** Check its preserved `next.config.ts` and `src/lib/site-variant.ts`. The destination variant should use an empty `basePath` for a custom domain.
- **Netlify does not deploy:** Confirm that a Netlify site is connected to `th30d4y/docs-hunting` and builds `main`. A successful mirror commit alone does not create a Netlify integration; the existing GitHub Pages workflow may still deploy on that commit.
18 changes: 18 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);

export default eslintConfig;
15 changes: 15 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { NextConfig } from "next";

const basePath = "/OWASP-Hunting";

const nextConfig: NextConfig = {
output: "export",
basePath,
assetPrefix: basePath,
images: {
unoptimized: true,
},
trailingSlash: true,
};

export default nextConfig;
Loading
Loading