Files
AgriciDaniel 2020316123 fix(audit): grand-audit-2 remediation, security + currency + drift
Independent re-audit (6 Codex slices) of shipped v1.11.0 surfaced real
issues the first pass missed. Remediated across 54 files:

Security: SSRF DNS-rebinding revalidation (generate_hero, blog_preflight);
symlink TOCTOU on chart/hero/preflight/image writers (O_NOFOLLOW atomic);
path traversal in blog-notebooklm/blog-google/blog-audio wrapper run.py;
preflight allowlist-before-safety bypass (Gate 5); unescaped untrusted
text to XSS in discourse + schema JSON-LD; arbitrary file read/write in
audio/indexing/gsc batch flows.

Correctness: validate_image_setup args reassignment crash; chart
AttributeError on malformed input; preflight accepting 999/100 scores.

Currency (verified vs live Google docs): Indexing API scoped to
JobPosting/livestream only (was told for generic blog posts in 6 files);
gemini-3.1-flash-image stable + gemini-3.1-flash-tts-preview; nanobanana
preview-shutdown warnings.

Hygiene: SKILL.md trimmed 470->330 lines (under token budget); count /
version / command-contract coherence across README/CLAUDE/docs/.github;
stale install.ps1 SHA; 10 new SSRF/injection/symlink regression tests.

252 tests pass, prose lint + plugin validate green, no secret leaks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:29:39 +03:00

6.4 KiB

Contributing to claude-blog

Thank you for your interest in contributing to claude-blog!

Getting Started

  1. Fork and clone the repository
  2. Install dev dependencies: pip install -e ".[dev]"
  3. Run tests: python -m pytest tests/ -v
  4. Validate plugin metadata: claude plugin validate .

Development

Project Structure

claude-blog/
├── .claude-plugin/          # Plugin metadata
│   ├── plugin.json          # Manifest
│   └── marketplace.json     # Marketplace catalog entry
├── skills/
│   ├── blog/                # Main orchestrator + references + templates
│   ├── blog-write/          # User-facing sub-skill
│   ├── blog-chart/          # Internal-only sub-skill
│   ├── blog-rewrite/
│   └── ...                  # 31 sub-skill dirs: 30 user-facing + 1 internal
├── agents/                  # 5 specialized agents
├── scripts/                 # 14 root-level Python helper scripts
├── tests/                   # pytest test suite (security guardrails + script tests)
├── docs/                    # Documentation (installation, commands, architecture, templates, troubleshooting, MCP)
└── .github/workflows/       # CI pipeline

Making Changes

  • SKILL.md frontmatter must include name and description. Optional valid fields: user-invokable, argument-hint, license, compatibility, metadata, disable-model-invocation. Do NOT add allowed-tools (not a valid Claude Code spec field; the tests/test_security_guardrails.py test enforces).
  • Reference paths in sub-skills use references/ (relative to installed location) OR full repo-root paths like skills/blog/references/X.md for cross-skill references.
  • Template paths in sub-skills use templates/ (relative to installed location).
  • Agent frontmatter must NOT include Bash in the tools list (blast-radius reduction for prompt-injection surfaces; enforced by tests).
  • Run python -m pytest tests/ -v before submitting.
  • Run claude plugin validate . before submitting.

Code Style

Python (scripts/, skills/*/scripts/):

  • Python 3.11+. Use from __future__ import annotations in new files for forward-compatible type hints.
  • Stdlib-only for new scripts unless a hard dependency is justified. Existing scripts use argparse, json, pathlib, datetime, re, collections, sys.
  • CLI shape: docstring (with Usage block) + argparse + --format json|markdown flag + return-code int.
  • File I/O: use pathlib.Path, NEVER unrestricted user paths. Validate via the _validate_input_path / _validate_output_path helpers in scripts/discourse_research.py (refuses symlinks, enforces size cap, checks regular file).
  • Exception handling: catch specifically (FileNotFoundError, json.JSONDecodeError, ValueError); avoid bare except: (test enforces).
  • Tests live in tests/test_<module>.py mirroring the script name; follow the test_cognitive_load.py and test_discourse_research.py patterns (subprocess invocation; happy / empty / contract tests).

Prose (SKILL.md, references/, docs/, CHANGELOG.md, CONTRIBUTORS.md):

  • No em dashes or en dashes (U+2014, U+2013, or ASCII --). Use periods, commas, semicolons, colons, or parentheses. Em dashes are the strongest AI-content tell and the project deliberately avoids them. Exception: pedagogical use inside backticks (e.g. when documenting the character itself).
  • Inline citations as [name](url) markdown links (LAW 5 of skills/blog/references/synthesis-contract.md).
  • No invented titles for sources (LAW 2).
  • No trailing "Sources" block when sources are already cited inline (LAW 1).
  • Tables and bullet lists where structure helps; prose where prose helps.

Commit messages:

  • Conventional commits style: type(scope): subject. Types: feat, fix, docs, chore, refactor, test, security.
  • Examples used in the project: fix(lint):, docs(security):, chore(release):, feat(blog-discourse):.
  • Subject line under 72 characters.
  • Body explains WHY, not WHAT (the diff shows what).

Security expectations

  • Read SECURITY.md for the project's threat model and trust boundaries.
  • Treat any new untrusted-data path (file from user, network response, agent output, project-root file) as crossing a trust boundary. Fence content explicitly (see agents/blog-researcher.md for the WebFetch pattern; see skills/blog/SKILL.md "Untrusted-Data Contract" section for the project-root pattern).
  • Project-root file loading (BRAND.md / VOICE.md / DISCOURSE.md and any future additions): use scripts/load_untrusted_root.py via Bash; do NOT hand-roll a fence in the orchestrator's own token output. The helper generates CSPRNG nonces, scans for instruction-shaped patterns, emits mtime provenance, and refuses symlinks via O_NOFOLLOW. Adding a new project-root file means: (a) add its basename to ALLOWED_BASENAMES in scripts/load_untrusted_root.py, (b) add behavioral tests in tests/test_load_untrusted_root.py, (c) document the new T12 surface in SECURITY.md. Hand-rolled fences re-introduce the documentation-only state v1.8.3 closed.
  • Prose hygiene: scripts/lint_prose.py enforces CONTRIBUTING.md prose rules (no em-dashes / en-dashes / ASCII double-hyphen) on every PR via CI. Run locally with python3 scripts/lint_prose.py before pushing.
  • Credentials, API keys, tokens: NEVER hardcoded. Use environment variables; store files at mode 0o600 with atomic write (see skills/blog-google/scripts/google_auth.py _harden_perms pattern).
  • New CLI scripts must enforce size caps (DoS guard) and refuse symlinks (CWE-59) on any path argument.

Pull Requests

  1. Create a feature branch from main.
  2. Make your changes with clear conventional-commit messages.
  3. Ensure all tests pass (python -m pytest tests/).
  4. Ensure plugin validates (claude plugin validate .).
  5. Update CHANGELOG.md with an [Unreleased] entry describing the change.
  6. Submit a PR using the template. Fill in: type of change, linked issue, test plan, docs checklist, and security checklist (if applicable).

Reporting Issues