Added web exploitation IDOR LAB #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto-generate GitHub Pages index | |
| on: | |
| push: | |
| branches: ["main"] | |
| paths: | |
| - "labs/**" | |
| - ".github/workflows/generate-pages-index.yml" | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| generate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.x" | |
| - name: Generate docs/index.html (Labs section) | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| run: | | |
| python - <<'PY' | |
| from __future__ import annotations | |
| import html | |
| import re | |
| from pathlib import Path | |
| ROOT = Path('.').resolve() | |
| DOCS_INDEX = ROOT / 'docs' / 'index.html' | |
| LABS_DIR = ROOT / 'labs' | |
| START = '<!-- LABS:START -->' | |
| END = '<!-- LABS:END -->' | |
| def sanitize_text(text: str) -> str: | |
| text = text.strip() | |
| if not text: | |
| return '' | |
| # Markdown cleanup (best-effort) | |
| text = re.sub(r'!\[[^\]]*\]\([^)]+\)', '', text) # images | |
| text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text) # links | |
| text = text.replace('`', '') | |
| text = re.sub(r'[*_]{1,3}', '', text) # emphasis markers | |
| text = re.sub(r'\s+', ' ', text).strip() | |
| return text | |
| def clamp(text: str, max_len: int = 160) -> str: | |
| if len(text) <= max_len: | |
| return text | |
| cut = text[: max_len - 1].rstrip() | |
| cut = re.sub(r'[\s,;:.!?]+$', '', cut) | |
| return cut + '…' | |
| def extract_title_and_description(readme_text: str) -> tuple[str, str]: | |
| lines = readme_text.splitlines() | |
| title = '' | |
| for line in lines: | |
| s = line.strip() | |
| if s.startswith('# '): | |
| title = sanitize_text(s[2:]) | |
| break | |
| if not title: | |
| title = 'Untitled lab' | |
| # Description = first paragraph after the H1 | |
| desc_lines: list[str] = [] | |
| in_body = False | |
| started = False | |
| for line in lines: | |
| s = line.strip() | |
| if not in_body: | |
| if s.startswith('# '): | |
| in_body = True | |
| continue | |
| if s.startswith('#'): | |
| break | |
| if not started: | |
| if not s: | |
| continue | |
| started = True | |
| if started and not s: | |
| break | |
| if s: | |
| desc_lines.append(s) | |
| desc = sanitize_text(' '.join(desc_lines)) | |
| desc = clamp(desc, 160) | |
| if not desc: | |
| desc = 'Self-contained lab with vulnerable and fixed builds.' | |
| return title, desc | |
| def discover_labs() -> list[tuple[str, str, str]]: | |
| labs: list[tuple[str, str, str]] = [] | |
| if not LABS_DIR.exists(): | |
| return labs | |
| for child in sorted(LABS_DIR.iterdir()): | |
| if not child.is_dir(): | |
| continue | |
| readme = child / 'README.md' | |
| if not readme.exists(): | |
| continue | |
| title, desc = extract_title_and_description(readme.read_text(encoding='utf-8')) | |
| labs.append((child.name, title, desc)) | |
| labs.sort(key=lambda t: t[1].lower()) | |
| return labs | |
| def render_cards(labs: list[tuple[str, str, str]]) -> str: | |
| repo = __import__('os').environ.get('GITHUB_REPOSITORY', 'DebaA17/cybersecurity-labs') | |
| branch = 'main' | |
| if not labs: | |
| return ( | |
| ' <article class="card">\n' | |
| ' <h3>No labs found</h3>\n' | |
| ' <p class="muted">Add a lab under <code>labs/</code> with a README.md.</p>\n' | |
| ' </article>\n' | |
| ) | |
| parts: list[str] = [] | |
| for slug, title, desc in labs: | |
| href = f'https://github.com/{repo}/tree/{branch}/labs/{slug}' | |
| parts.append( | |
| ' <article class="card">\n' | |
| f' <h3>{html.escape(title)}</h3>\n' | |
| f' <p class="muted">{html.escape(desc)}</p>\n' | |
| ' <div class="links">\n' | |
| f' <a href="{href}">Open lab</a>\n' | |
| ' </div>\n' | |
| ' </article>' | |
| ) | |
| return '\n\n'.join(parts) + '\n' | |
| index = DOCS_INDEX.read_text(encoding='utf-8') | |
| a = index.find(START) | |
| b = index.find(END) | |
| if a == -1 or b == -1 or b < a: | |
| raise SystemExit(f'Markers not found in {DOCS_INDEX}') | |
| rendered = render_cards(discover_labs()) | |
| line_start = index.rfind('\n', 0, b) + 1 | |
| indent = index[line_start:b] | |
| updated = index[: a + len(START)] + '\n' + rendered + indent + index[b:] | |
| if updated != index: | |
| DOCS_INDEX.write_text(updated, encoding='utf-8') | |
| print('Updated docs/index.html') | |
| else: | |
| print('No changes needed') | |
| PY | |
| - name: Commit and push if changed | |
| run: | | |
| if git diff --quiet; then | |
| echo "No changes to commit." | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add docs/index.html | |
| git commit -m "chore(pages): auto-update labs index [skip ci]" | |
| git push |