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/.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 }} 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. 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. 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: