docs(raw-api): document gitea_request, env vars and policy examples
Adds docs/raw-api.md (two-layer policy, sensitive denylist, env vars, write-mode warning), links it from index and api-reference, documents RAW_API_ENABLED / RAW_API_ALLOW_SENSITIVE in .env.example, and adds commented virtual-tool-name deny examples to policy.yaml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,17 @@ WRITE_MODE=false
|
|||||||
WRITE_REPOSITORY_WHITELIST=
|
WRITE_REPOSITORY_WHITELIST=
|
||||||
WRITE_ALLOW_ALL_TOKEN_REPOS=false
|
WRITE_ALLOW_ALL_TOKEN_REPOS=false
|
||||||
|
|
||||||
|
# Raw API dispatch (gitea_request escape hatch). See docs/raw-api.md.
|
||||||
|
# gitea_request can call any Gitea REST endpoint (method + path). It is still
|
||||||
|
# subject to policy.yaml, WRITE_MODE + the write whitelist, and a built-in
|
||||||
|
# admin/credential denylist. Set RAW_API_ENABLED=false to remove the tool's
|
||||||
|
# ability to dispatch entirely.
|
||||||
|
RAW_API_ENABLED=true
|
||||||
|
# Allow gitea_request to reach admin/credential surfaces (/admin, *tokens*,
|
||||||
|
# *secrets*, *hooks*, *keys*, applications/oauth2, runner registration tokens).
|
||||||
|
# Leave false unless you fully understand the exposure.
|
||||||
|
RAW_API_ALLOW_SENSITIVE=false
|
||||||
|
|
||||||
# Automation mode (disabled by default)
|
# Automation mode (disabled by default)
|
||||||
AUTOMATION_ENABLED=false
|
AUTOMATION_ENABLED=false
|
||||||
AUTOMATION_SCHEDULER_ENABLED=false
|
AUTOMATION_SCHEDULER_ENABLED=false
|
||||||
|
|||||||
+12
-2
@@ -90,8 +90,18 @@ Scope requirements:
|
|||||||
- `create_milestone` (`owner`, `repo`, `title`, optional `description`, `due_on`)
|
- `create_milestone` (`owner`, `repo`, `title`, optional `description`, `due_on`)
|
||||||
- `edit_issue_comment` (`owner`, `repo`, `comment_id`, `body`)
|
- `edit_issue_comment` (`owner`, `repo`, `comment_id`, `body`)
|
||||||
|
|
||||||
Not supported by design: merge, branch/label/release deletion, force push, repo/admin
|
Not supported by the dedicated tools by design: merge, branch/label/release deletion,
|
||||||
management.
|
force push, repo/admin management. Endpoints not covered above are reachable through the
|
||||||
|
generic `gitea_request` escape hatch (subject to policy, write-mode, and a sensitive-path
|
||||||
|
denylist) — see [Raw API Dispatch](raw-api.md).
|
||||||
|
|
||||||
|
## Raw API Dispatch
|
||||||
|
|
||||||
|
- `gitea_request` (`method`, `path`, optional `query`, `body`)
|
||||||
|
- Calls an arbitrary Gitea REST endpoint. `GET`/`HEAD` are reads; other methods are
|
||||||
|
writes and require write-mode plus a whitelisted repository. Admin/credential
|
||||||
|
endpoints are blocked unless `RAW_API_ALLOW_SENSITIVE=true`. See
|
||||||
|
[Raw API Dispatch](raw-api.md) for the two-layer policy model and full details.
|
||||||
|
|
||||||
Note: `create_issue`, `add_labels`, and `remove_labels` accept label **names**; the
|
Note: `create_issue`, `add_labels`, and `remove_labels` accept label **names**; the
|
||||||
server resolves them to Gitea label ids and returns a clear error for unknown labels.
|
server resolves them to Gitea label ids and returns a clear error for unknown labels.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ AegisGitea MCP acts as a secure bridge between AI assistants (such as Claude, Cl
|
|||||||
| [Getting Started](getting-started.md) | Installation and first-time setup |
|
| [Getting Started](getting-started.md) | Installation and first-time setup |
|
||||||
| [Configuration](configuration.md) | All environment variables and settings |
|
| [Configuration](configuration.md) | All environment variables and settings |
|
||||||
| [API Reference](api-reference.md) | HTTP endpoints and MCP tools |
|
| [API Reference](api-reference.md) | HTTP endpoints and MCP tools |
|
||||||
|
| [Raw API Dispatch](raw-api.md) | The generic `gitea_request` escape-hatch tool |
|
||||||
| [Architecture](architecture.md) | System design and data flow |
|
| [Architecture](architecture.md) | System design and data flow |
|
||||||
| [Security](security.md) | Authentication, rate limiting, and audit logging |
|
| [Security](security.md) | Authentication, rate limiting, and audit logging |
|
||||||
| [Deployment](deployment.md) | Docker and production deployment |
|
| [Deployment](deployment.md) | Docker and production deployment |
|
||||||
|
|||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
# Raw API Dispatch (`gitea_request`)
|
||||||
|
|
||||||
|
`gitea_request` is a generic escape hatch that can call **any** Gitea REST
|
||||||
|
endpoint by method and path. It exists for the long tail of the Gitea API that
|
||||||
|
the curated, typed tools do not cover (merging PRs, reviews, writing files,
|
||||||
|
webhooks, branch/tag protections, collaborators, Actions/CI, packages,
|
||||||
|
notifications, and so on).
|
||||||
|
|
||||||
|
> Prefer the dedicated tools whenever one exists. Use `gitea_request` only for
|
||||||
|
> endpoints they do not cover. It is subject to policy, write-mode, and the
|
||||||
|
> sensitive-path denylist described below.
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
| Field | Type | Notes |
|
||||||
|
|-------|------|-------|
|
||||||
|
| `method` | enum | `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` (case-insensitive). Any other method is rejected before any network call. |
|
||||||
|
| `path` | string | Gitea REST path. The `/api/v1` prefix is optional. A full URL may be supplied — the host and query string are stripped. |
|
||||||
|
| `query` | object | Optional query-string parameters. |
|
||||||
|
| `body` | object | Optional JSON request body. **Never logged.** |
|
||||||
|
|
||||||
|
The response is returned in a stable envelope:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/v1/repos/acme/app/pulls/1",
|
||||||
|
"write": false,
|
||||||
|
"repository": "acme/app",
|
||||||
|
"data": { "...": "..." }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
List responses add `count` and `omitted`; oversized objects are returned as a
|
||||||
|
truncated JSON string with `"truncated": true`. All responses are bounded by
|
||||||
|
`MAX_TOOL_RESPONSE_ITEMS` / `MAX_TOOL_RESPONSE_CHARS`.
|
||||||
|
|
||||||
|
## Two-layer authorization
|
||||||
|
|
||||||
|
A single tool surface would normally collapse the granularity of `policy.yaml`.
|
||||||
|
To preserve it, every call is authorized twice:
|
||||||
|
|
||||||
|
1. **Central gate (`server.py`).** The registered `gitea_request` tool name is
|
||||||
|
allowed/denied like any other tool. In service-PAT mode the central gate also
|
||||||
|
parses the target repository from the path and verifies that the signed-in
|
||||||
|
user has permission on that repository before the service PAT is used.
|
||||||
|
2. **Handler gate (`raw_tools.py`).** The handler derives a coarse **virtual
|
||||||
|
tool name** of the form `gitea_request:<METHOD>:<top-path-segment>` (for
|
||||||
|
example `gitea_request:GET:repos` or `gitea_request:DELETE:repos`) and runs
|
||||||
|
it back through the policy engine with the parsed repository, target path, and
|
||||||
|
a `is_write` flag (`true` for any method other than GET/HEAD). This reuses the
|
||||||
|
existing write-mode + write-whitelist enforcement and lets `policy.yaml` allow
|
||||||
|
or deny raw dispatch per method and per top-level path segment.
|
||||||
|
|
||||||
|
Because the policy engine matches tool names by **exact set membership** (only
|
||||||
|
`paths` use globbing), the virtual name is deliberately coarse and stable.
|
||||||
|
|
||||||
|
### Example: lock raw dispatch to reads
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
tools:
|
||||||
|
deny:
|
||||||
|
- gitea_request:POST:repos
|
||||||
|
- gitea_request:PUT:repos
|
||||||
|
- gitea_request:PATCH:repos
|
||||||
|
- gitea_request:DELETE:repos
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sensitive-path denylist
|
||||||
|
|
||||||
|
Independently of `policy.yaml`, the handler blocks endpoints that touch an
|
||||||
|
admin or credential surface **for every method, including GET** (a GET on these
|
||||||
|
already leaks credentials or privileged configuration):
|
||||||
|
|
||||||
|
- `/admin`
|
||||||
|
- `*tokens*`
|
||||||
|
- `*secrets*`
|
||||||
|
- `*hooks*`
|
||||||
|
- `*keys*` (and `*gpg_keys*`)
|
||||||
|
- `applications/oauth2`
|
||||||
|
- `actions/runners/registration-token`
|
||||||
|
|
||||||
|
This denylist lives in the handler and **cannot be re-opened from
|
||||||
|
`policy.yaml`.** It is overridden only by setting `RAW_API_ALLOW_SENSITIVE=true`.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
| Variable | Default | Notes |
|
||||||
|
|----------|---------|-------|
|
||||||
|
| `RAW_API_ENABLED` | `true` | Killswitch. When `false`, `gitea_request` refuses every dispatch with a `403`. |
|
||||||
|
| `RAW_API_ALLOW_SENSITIVE` | `false` | When `true`, the admin/credential denylist is bypassed. Leave `false` unless you fully understand the exposure. |
|
||||||
|
|
||||||
|
## Security warning
|
||||||
|
|
||||||
|
> With `WRITE_MODE=true`, the **write whitelist is the only brake** on
|
||||||
|
> `POST`/`PUT`/`PATCH`/`DELETE` across the *entire* Gitea API surface reachable
|
||||||
|
> by `gitea_request`. Any write method against a whitelisted repository will be
|
||||||
|
> attempted. Keep the whitelist tight, prefer denying the write virtual tool
|
||||||
|
> names in `policy.yaml`, and keep `RAW_API_ALLOW_SENSITIVE=false`.
|
||||||
|
|
||||||
|
## Behavioral notes and edge cases
|
||||||
|
|
||||||
|
- **Full URL supplied instead of a path:** only the path is used; the host and
|
||||||
|
query string are discarded (`query` carries query parameters).
|
||||||
|
- **Path traversal (`..`):** rejected during argument validation (`400`).
|
||||||
|
- **Unknown / non-HTTP method:** rejected during argument validation, before any
|
||||||
|
network call.
|
||||||
|
- **Cross-repo endpoints** such as `/repos/search` and `/repos/issues/search`
|
||||||
|
are intentionally *not* treated as repository-scoped, so `repository` is
|
||||||
|
`null` for them.
|
||||||
|
- **Non-repository writes** such as `POST /user/repos` or `POST /orgs` are denied
|
||||||
|
with *"write operation requires a repository target"*. This is the secure
|
||||||
|
default — the per-user permission model is repository-scoped, so there is no
|
||||||
|
repository against which to verify the write. This behavior is intentional and
|
||||||
|
is not worked around.
|
||||||
|
- **Service-PAT mode:** non-repository endpoints (for example `GET /user/orgs`)
|
||||||
|
are denied by the central gate because per-user permission can only be verified
|
||||||
|
against a repository target. Use the dedicated tools for those, or run in
|
||||||
|
OAuth-only mode.
|
||||||
+15
@@ -4,5 +4,20 @@ defaults:
|
|||||||
|
|
||||||
tools:
|
tools:
|
||||||
deny: []
|
deny: []
|
||||||
|
# The generic `gitea_request` tool authorizes each call under a coarse virtual
|
||||||
|
# tool name of the form `gitea_request:<METHOD>:<top-path-segment>`, e.g.
|
||||||
|
# `gitea_request:GET:repos` or `gitea_request:DELETE:repos`. To keep raw
|
||||||
|
# dispatch read-only while still allowing GETs, deny the write methods here:
|
||||||
|
#
|
||||||
|
# deny:
|
||||||
|
# - gitea_request:POST:repos
|
||||||
|
# - gitea_request:PUT:repos
|
||||||
|
# - gitea_request:PATCH:repos
|
||||||
|
# - gitea_request:DELETE:repos
|
||||||
|
#
|
||||||
|
# NOTE: The admin/credential denylist (/admin, *tokens*, *secrets*, *hooks*,
|
||||||
|
# *keys*, applications/oauth2, runner registration tokens) is enforced in the
|
||||||
|
# handler independently of this file and is NOT configured here. It can only be
|
||||||
|
# overridden by setting RAW_API_ALLOW_SENSITIVE=true.
|
||||||
|
|
||||||
repositories: {}
|
repositories: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user