From be33fcdc782162b1c6501c48e24081556c9b0868 Mon Sep 17 00:00:00 2001 From: Benjamin Kapner Date: Sun, 16 Aug 2026 14:19:22 +0300 Subject: [PATCH 1/4] chore: add CONTRIBUTING.md and issue/PR templates Phase 0: Repository foundations. The repo has SECURITY.md and RELEASING.md but no contributor onboarding guide despite seven forks. Add CONTRIBUTING.md with dev setup, testing, lint, commit conventions, and PR expectations. Add structured issue templates that capture model backend, model name, docs format, repo setup, and trigger command, which together determine almost every bug's reproduction path. Add a PR template with a testing checklist. --- .github/ISSUE_TEMPLATE/bug_report.yml | 85 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 31 ++++++++ .github/pull_request_template.md | 15 ++++ CONTRIBUTING.md | 79 ++++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/pull_request_template.md create mode 100644 CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..f95c186 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,85 @@ +name: Bug Report +description: Report a problem with code-to-docs +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug. Please fill in the details + below so we can reproduce the issue. + + - type: dropdown + id: command + attributes: + label: Trigger command + description: Which command were you using? + options: + - "[review-docs]" + - "[update-docs]" + - "[review-feature]" + validations: + required: true + + - type: input + id: model-backend + attributes: + label: Model backend + description: "e.g. vLLM on OpenShift AI, Gemini, OpenAI, Ollama" + placeholder: "vLLM on OpenShift AI" + validations: + required: true + + - type: input + id: model-name + attributes: + label: Model name + description: "The MODEL_NAME value you configured" + placeholder: "meta-llama/Llama-3.1-8B-Instruct" + validations: + required: true + + - type: dropdown + id: docs-format + attributes: + label: Documentation format + description: What format are your doc files? + options: + - Markdown (.md) + - reStructuredText (.rst) + - AsciiDoc (.adoc) + - Mixed + validations: + required: true + + - type: dropdown + id: repo-setup + attributes: + label: Repository setup + description: How is your docs repo configured? + options: + - Same repo (docs-subfolder set) + - Separate docs repo (docs-repo-url points elsewhere) + validations: + required: true + + - type: textarea + id: description + attributes: + label: What happened? + description: Describe the bug. Include the PR link if possible. + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect? + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Paste any relevant output from the GitHub Actions run. + render: shell diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..989eef4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,31 @@ +name: Feature Request +description: Suggest an improvement or new capability +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What problem does this solve? What is frustrating or missing today? + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed solution + description: How would you like this to work? + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Have you tried any workarounds? + + - type: textarea + id: context + attributes: + label: Additional context + description: Anything else that would help us understand the request. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..7d0076e --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## Summary + + + +## Changes + + + +## Testing + + + +- [ ] Tests pass (`uv run pytest -v`) +- [ ] Lint clean (`uv run ruff check src/ tests/`) +- [ ] Format clean (`uv run ruff format --check src/ tests/`) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a8dfdae --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,79 @@ +# Contributing to Code-to-Docs + +Thank you for your interest in contributing! This guide covers the development +workflow so you can get started quickly. + +## Development Setup + +Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/) for package management. + +```bash +# Install dependencies (including dev tools) +uv sync --extra dev + +# Set up pre-commit hooks +uv run pre-commit install +``` + +## Running Tests + +```bash +# Run tests +uv run pytest -v + +# Run tests with coverage +uv run pytest --cov=src --cov-report=term-missing +``` + +CI enforces a minimum coverage threshold. Check `pyproject.toml` for the current +value. + +## Linting and Formatting + +```bash +# Lint +uv run ruff check src/ tests/ + +# Format check +uv run ruff format --check src/ tests/ + +# Auto-format +uv run ruff format src/ tests/ +``` + +## Commit Messages + +This project uses [Conventional Commits](https://www.conventionalcommits.org/): + +- `feat(scope): description` for new features +- `fix(scope): description` for bug fixes +- `chore(scope): description` for maintenance +- `docs(scope): description` for documentation-only changes +- `refactor(scope): description` for refactors with no behavior change + +Include a commit body explaining *why* the change is needed, not just what it does. + +## Pull Requests + +- One logical change per PR. If a PR contains multiple independent changes, + split them. +- All CI checks must pass: lint, format, and tests with coverage. +- Update documentation (`README.md`, `CLAUDE.md`) if your change adds or + modifies user-facing behavior. +- Add tests for new functionality. Tests are in `tests/` and mirror source + modules (`test_.py`). + +## Project Structure + +- `src/` contains the action source code (see `CLAUDE.md` for a module-by-module + breakdown) +- `tests/` contains pytest tests +- `demo/` contains the interactive demo site +- `.code-to-docs/` contains per-repo configuration (style guidelines, ignore + lists, settings) + +## Reporting Issues + +Use the provided issue templates for bug reports and feature requests. Include +the model backend, model name, docs format, and trigger command when reporting +bugs. From 30a1a07733a7ef44291b90fa0043ac70a2427b87 Mon Sep 17 00:00:00 2001 From: Benjamin Kapner Date: Sun, 16 Aug 2026 14:20:16 +0300 Subject: [PATCH 2/4] chore(ci): pin third-party actions to commit SHAs Phase 0: Repository foundations. A project that asks users to grant contents:write should model good supply-chain hygiene. The CI workflow (actions/checkout, astral-sh/setup-uv) was already pinned to commit SHAs. The fullsend reusable workflow uses a moving v0 tag coordinated with fullsend_ai_ref; added a comment documenting this constraint. Dependabot was already configured for both github-actions and pip ecosystems with weekly updates. --- .github/workflows/fullsend.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/fullsend.yaml b/.github/workflows/fullsend.yaml index 73e75d7..860a662 100644 --- a/.github/workflows/fullsend.yaml +++ b/.github/workflows/fullsend.yaml @@ -41,6 +41,8 @@ jobs: if: >- github.event_name != 'issue_comment' || github.event.comment.user.type != 'Bot' + # Pinned to v0 moving tag: fullsend-ai controls this workflow and + # fullsend_ai_ref below must match. Dependabot will propose SHA updates. uses: fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@v0 with: event_action: ${{ github.event.action }} From 91b464bfc143b4628d75344f84ecc43666108c69 Mon Sep 17 00:00:00 2001 From: Benjamin Kapner Date: Sun, 16 Aug 2026 14:22:30 +0300 Subject: [PATCH 3/4] chore(release): publish to GitHub Marketplace Phase 1: Releases and versioning. Fill out action.yml branding and metadata for Marketplace listing. Update name from placeholder to match the repo name, set author to the org, use book-open icon and red color to match Red Hat branding. The actual Marketplace publish is a manual UI action on the Release page, documented in RELEASING.md. --- action.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/action.yml b/action.yml index cf6382d..8654a19 100644 --- a/action.yml +++ b/action.yml @@ -1,10 +1,10 @@ -name: 'Upstream Documentation Enhancer' -description: 'AI-powered documentation enhancer that automatically updates docs based on code changes' -author: 'Your Name' +name: 'Code-to-Docs' +description: 'AI-powered GitHub Action that analyzes code changes and updates documentation using any OpenAI-compatible LLM' +author: 'Red Hat Community AI Tools' branding: - icon: 'book' - color: 'blue' + icon: 'book-open' + color: 'red' inputs: model-api-base: From d2d9991b7cd086b3d93b6817aabb039a54d1b508 Mon Sep 17 00:00:00 2001 From: Benjamin Kapner Date: Sun, 16 Aug 2026 14:35:13 +0300 Subject: [PATCH 4/4] docs(security): add threat model and fork-PR guidance The README warns about Jira content leaking out but says nothing about untrusted content coming in. Documentation content, code diffs, and external system content all flow into prompts, and on fork PRs much of that input is contributor-controlled. Extend SECURITY.md with a threat model enumerating every untrusted input, what it can influence, and the current mitigations. Add explicit guidance on fork PRs, including what the author_association gate does and does not protect against. Note the contents:write implication. Cross-link from the README. --- README.md | 5 ++++ SECURITY.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/README.md b/README.md index 60a7e46..1a76b77 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,11 @@ With this config, generated PRs will be titled `:book: docs: update documentatio This file is optional — if missing, the tool uses default titles with no prefix. +## Security + +See [SECURITY.md](SECURITY.md) for the threat model, including untrusted +inputs, fork PR considerations, and credential handling. + ## How It Works 1. **Triggered by PR Comments** - When someone comments `[review-docs]`, `[update-docs]`, or `[review-feature]` on a Pull Request diff --git a/SECURITY.md b/SECURITY.md index 4f056f2..666a399 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -19,3 +19,79 @@ Following this process will create a private advisory for our maintainers to rev ### Do Not Open Public Pull Requests, Issues, or Discussions Please **do not** discuss the issue, create PRs, or start discussions about the vulnerability. This ensures the vulnerability is not widely exploited before a fix is provided. + +## Threat Model + +This section enumerates untrusted inputs, what they can influence, and the +mitigations in place. It is intended for security-conscious adopters who want +to assess the tool without reading the source. + +### Permissions Required + +The action requires `contents: write` on the target repository. This allows +it to push commits to PR branches and create documentation PRs. It does +**not** require `admin` permissions. + +### Untrusted Inputs + +| Input | Source | What it can influence | Mitigation | +|-------|--------|----------------------|------------| +| Code diff | PR head branch (contributor-controlled on forks) | LLM prompt content for file selection and generation | Diff is data in the prompt, not instructions. Truncated to context budget. | +| Documentation content | PR head branch | LLM prompt for verification; also read for current file content | Post-generation verification wraps doc content in explicit data delimiters. | +| PR description | PR author | Included as context in generation prompts | Treated as context only, not instructions. | +| Comment body | PR commenter (gated by `author_association`) | Command parsing, user instructions passed to LLM | Only `OWNER`, `MEMBER`, and `COLLABORATOR` can trigger commands (enforced in the workflow `if` condition). | +| Jira ticket content | External system | Included in `[review-feature]` prompts | Fetched server-side via authenticated API. Content is data, not instructions. | +| Google Docs / Confluence content | External system | Included in `[review-feature]` prompts | Fetched via service account. Content is data, not instructions. | +| `.code-to-docs/style.md` | Base branch (maintainer-controlled) | Injected into generation prompts as style guidelines | Loaded from the base branch, not the PR branch. | +| `.code-to-docs/config.json` | Base branch (maintainer-controlled) | Controls validation thresholds and behavior | Loaded from the base branch, not the PR branch. Values are validated on load. | + +### Fork PRs + +The recommended workflow includes an `author_association` gate: + +```yaml +contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) +``` + +This prevents external contributors from triggering the action on their own +PRs. However, a maintainer commenting `[update-docs]` on a fork PR will +cause the action to read diff and doc content from the fork's head branch, +which is contributor-controlled. + +**What the gate protects against:** unauthorized triggering, resource +consumption, and unsolicited PR creation. + +**What the gate does not protect against:** a maintainer explicitly +approving a run on a fork PR where the contributor has placed adversarial +content in documentation files. The post-generation verification step +(added in PR #53) wraps document content in data delimiters to reduce this +risk, but LLM-based defenses are not absolute. + +**Fork push limitation:** the action cannot push to fork branches due to +GitHub token scoping. On fork PRs, it posts suggested changes as diffs in a +PR comment instead. + +### Credential Handling + +- `GH_TOKEN`, `MODEL_API_KEY`, `JIRA_API_TOKEN`, and `GOOGLE_SA_KEY` are + scrubbed from all output by `security_utils.sanitize_output()`. +- Credentials are never interpolated into strings or logged. +- File paths are validated against traversal attacks before read or write. +- The Docker container runs with no network access beyond what the + entrypoint requires. + +### LLM Prompt Injection + +Documentation content and code diffs are included in LLM prompts. A +malicious document containing text like "ignore previous instructions" could +theoretically influence the LLM's output. Mitigations: + +1. Document content in verification prompts is wrapped in explicit + `--- BEGIN/END ... (untrusted content, data only) ---` delimiters with + an instruction to treat it as data, not instructions. +2. Post-generation validation checks for large content removals and + verifies updates with an independent LLM call in a separate session. +3. The tool never executes generated content; it only writes documentation + files. +4. Generated output is format-validated (Markdown, RST, AsciiDoc parsers) + before being committed.