Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Project Guidelines

## Project Overview

SETools is a Python 3.10+ SELinux policy-analysis library and tool suite. It includes Python APIs,
command-line tools, a PyQt GUI, a FastMCP server, and a Cython extension linked against libselinux
and libsepol.

## Repository Layout

- `setools/` contains the public Python library and policy query modules.
- `setools/checker/` contains the policy checker framework and built-in checks.
- `setools/diff/` contains policy comparison implementations.
- `setools/mcp/` contains the FastMCP server, configuration, session management, and result
encoding.
- `setools/policyrep.pyx` and `setools/policyrep/` contain the Cython policy-representation layer.
Treat `setools/policyrep.c` as generated output; make source changes in the `.pyx` and `.pxi`
files.
- `setoolsgui/` contains the PyQt GUI.
- `man/` contains the command-line tool man pages and their translations.
- `docs/` contains project documentation and example configurations.
- The root scripts (`seinfo`, `sesearch`, `sediff`, `sedta`, `seinfoflow`, `sechecker`, `apol`, and
`setools-mcp`) are installed command-line entry points.
- `tests/library/` mirrors the public library and includes dedicated tests for `checker`, `mcp`, and
`policyrep`; `tests/gui/` covers the PyQt GUI. Keep new tests near the subsystem they cover and
reuse the policy fixtures in those directories.

## Documentation

- Use the [SELinux Notebook](https://github.com/SELinuxProject/selinux-notebook/tree/main/src) as
the most authoritative reference for general SELinux concepts, behavior, policy languages, object
classes and permissions, tools, and configuration.
- If the SELinux Notebook does not answer the question, consult the upstream SELinux userspace
man pages under `https://github.com/SELinuxProject/selinux/*/man/**` for further information.
- Format Markdown documentation with a maximum line length of 100 columns. Preformatted code
blocks and Markdown tables may exceed this limit.
- Align Markdown table cells and delimiters so tables are readable in the source text.
- Use spaces for indentation in Markdown documentation; do not use tabs.
- When a code change affects documented behavior or configuration, update the applicable man pages,
project documentation, and example configurations in the same change. Cross-check all three for
consistent option names, defaults, behavior, and examples.
- Name project documentation files with an uppercase basename and lowercase `.md` extension, for
example `CONFIGURATION.md`.
- Every command-line tool must have a corresponding man page under `man/`. When adding or changing
a tool, create or update its man page in the same change so options, defaults, and behavior match.

## Code Conventions

- Endeavor to use idiomatic Python and follow the existing code conventions demonstrated by the
surrounding implementation. Preserve public APIs unless a change explicitly requires an update.
- All class definitions, functions, and methods should have a docstring, except magic methods.
- When changing public API behavior, add compatibility code to preserve previous access when
possible and emit a `DeprecationWarning` from the deprecated path.
- Use type annotations for new Python code and keep it compatible with the mypy settings in
`pyproject.toml`.
- Keep lines at or below 100 characters, as configured in `tox.ini`.
- Prefer existing query mixins, descriptors, and policy-representation abstractions over adding
parallel implementations.
- Keep changes narrowly scoped. Do not reformat unrelated code or modify generated/build output.
- When a lint exemption is necessary, apply it at the narrowest reasonable scope rather than
weakening lint checks broadly.
- Every code change must include a corresponding unit test change. Ensure all new and modified code
paths are covered by unit tests.
- When a majority of a dictionary's keys are known in advance and used in the code, define an enum
for the keys and use its members at every access site. Use named constants or enum members for
repeated domain-specific values; do not repeat magic string or numeric literals.
- Do not add a dependency without user confirmation. First, compare an implementation using the
standard library and existing dependencies with the proposed dependency. Summarize the tradeoffs
in implementation complexity, features, and maintenance. Also report the proposed dependency's
transitive dependency cost and availability in supported Fedora and Ubuntu releases, then ask for
approval before changing dependency metadata.

## Build and Test

The extension requires the libselinux and libsepol development headers and libraries. Build it in
place before running tests directly:

```bash
python setup.py build_ext -i
python -m pytest tests
```

Use tox for the standard project checks:

```bash
tox -e python3 # unit tests
tox -e mypy # type checking
tox -e lint # pylint errors
tox -e pep8 # pycodestyle
tox -p -e python3,mypy,lint,pep8
```

During development, run the narrowest relevant test first, for example:

```bash
tox -e python3 -- tests/library/test_typequery.py
```

Set `USERSPACE_SRC` when building against a local SELinux userspace tree. The tox environments
pass this variable through. Graph-analysis tests may also require NetworkX and pygraphviz, while GUI
tests require PyQt6 and pytest-qt.

## MCP Security

- Review every MCP server change for session security.
- Keep session-owned state, caches, inputs, and results isolated. One session must not be able to
observe, mutate, evict, or otherwise interfere with another session's data.
- Add unit tests using at least two independent sessions to verify that state and data cannot leak
between them, including under concurrent access when the changed code supports concurrency.

## Evidence and Uncertainty

- Inspect the relevant current code before answering questions or making changes.
- Read the applicable specifications and authoritative documentation; do not infer requirements
from code alone when a specification governs the behavior.
- Fetch web references directly from their authoritative source. Do not rely on cached web search
results, summaries, or snippets.
- Do not make unsupported assumptions or speculate when evidence is unavailable. If the code and
authoritative references do not determine the answer, or if they conflict, state the uncertainty
and ask the user a clarifying question before proceeding.

## Change Discipline

- Do not commit local policy binaries, analysis output, build directories, or compiled extension
artifacts unless the task explicitly calls for a tracked fixture or generated release artifact.
- Update `README.md`, command help, or man pages when user-visible behavior changes.
- Report the focused checks run and any checks that could not run because native or GUI dependencies
are unavailable.
- Keep each commit to one logical change and ensure the tree builds after each commit.
- Include a `Signed-off-by` line using the contributor's real name in every commit.
- Submit changes through a GitHub pull request.
96 changes: 96 additions & 0 deletions docs/MCP-CONFIG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# SETools MCP Server Configuration

The SETools MCP server reads its settings from an INI-format file parsed by
`setools.mcp.config.MCPConfig`. The file has a `[server]` section for the bind address, an
`[auth]` section selecting the authentication method, and one section per authentication method.
All sections are optional; the defaults are a loopback bind address with no authentication.

The server uses the HTTP transport when a configuration file is supplied and the stdio transport
when it is not, so the transport is not a setting in this file.

Start the server with an optional positional configuration path:

```console
setools-mcp [CONFIG]
```

For example, `setools-mcp docs/mcp-auth-jwt.ini` starts the HTTP server using that file. Running
`setools-mcp` without `CONFIG` starts the server over stdio without authentication.

## Server Settings

```ini
[server]
# Address to bind to.
host = 127.0.0.1

# Port to bind to.
port = 8000
```

Authentication only applies to the HTTP transport; the stdio transport inherits the security of
its local execution environment.

## Authentication Settings

The `[auth]` section selects the authentication method:

```ini
[auth]
method = jwt
```

The remaining sections are named after the method they configure. The option names match the
FastMCP provider constructor parameters, so the
[FastMCP authentication documentation](https://gofastmcp.com/servers/auth/authentication) is the
authoritative reference for their meaning.

| `method` | FastMCP class | Purpose |
| ---------------- | ---------------------------- | ------------------------------------------------ |
| `none` | (none) | Disable authentication. |
| `jwt` | `JWTVerifier` | Verify JWT bearer tokens. |
| `static` | `StaticTokenVerifier` | Verify hard-coded development tokens. |
| `debug` | `DebugTokenVerifier` | Accept any non-empty token (development only). |
| `introspection` | `IntrospectionTokenVerifier` | Verify opaque tokens via RFC 7662 introspection. |
| `remote` | `RemoteAuthProvider` | Token verification plus OAuth discovery metadata for identity providers supporting dynamic client registration. |
| `oauth_proxy` | `OAuthProxy` | Proxy an upstream OAuth provider that does not support dynamic client registration. |
| `oidc_proxy` | `OIDCProxy` | Proxy an OpenID Connect provider using its discovery document. |

`OAuthProvider`, the full OAuth 2.0 authorization server implementation, is not configurable
through this file.

## Value Syntax

- Options documented as lists are comma-separated, for example `required_scopes = read, write`.
- `issuer` and `audience` accept either a single value or a comma-separated list.
- Boolean options accept the usual INI values: `true`/`false`, `yes`/`no`, `on`/`off`, `1`/`0`.
- Parameters that cannot be expressed in a text file, such as the `DebugTokenVerifier` `validate`
callable and the OAuth proxy `client_storage` backend, are not supported.

## Nested Token Verifiers

The `remote` and `oauth_proxy` methods wrap a token verifier. Set their `token_verifier` option to
one of `jwt`, `static`, `debug`, or `introspection` and configure the verifier in the section of
that name:

```ini
[auth]
method = remote

[remote]
token_verifier = jwt

[jwt]
jwks_uri = https://auth.example.com/.well-known/jwks.json
```

## Examples

- [mcp-auth-none.ini](mcp-auth-none.ini)
- [mcp-auth-jwt.ini](mcp-auth-jwt.ini)
- [mcp-auth-static.ini](mcp-auth-static.ini)
- [mcp-auth-debug.ini](mcp-auth-debug.ini)
- [mcp-auth-introspection.ini](mcp-auth-introspection.ini)
- [mcp-auth-remote.ini](mcp-auth-remote.ini)
- [mcp-auth-oauth-proxy.ini](mcp-auth-oauth-proxy.ini)
- [mcp-auth-oidc-proxy.ini](mcp-auth-oidc-proxy.ini)
24 changes: 24 additions & 0 deletions docs/mcp-auth-debug.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Accept any non-empty bearer token without validating it
# (fastmcp.server.auth.providers.debug.DebugTokenVerifier).
#
# WARNING: this bypasses all token validation. Only use it in a controlled
# development or test environment, or when an upstream service performs the
# actual validation. The custom "validate" callable supported by the FastMCP
# API cannot be expressed in this file, so every non-empty token is accepted.

[server]
host = 127.0.0.1
port = 8000

[auth]
method = debug

[debug]
# Client identity reported for every accepted token.
client_id = debug-client

# Scopes granted to every accepted token.
scopes = setools:read, setools:analyze

# Scopes that every token must carry.
required_scopes = setools:read
29 changes: 29 additions & 0 deletions docs/mcp-auth-introspection.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Verify opaque bearer tokens using OAuth 2.0 token introspection (RFC 7662)
# (fastmcp.server.auth.providers.introspection.IntrospectionTokenVerifier).
#
# Every request queries the authorization server, so revoked tokens are
# rejected immediately at the cost of network latency per request.

[server]
host = 127.0.0.1
port = 8000

[auth]
method = introspection

[introspection]
# Introspection endpoint of the authorization server.
introspection_url = https://auth.example.com/oauth/introspect

# Credentials this server uses to authenticate to the introspection endpoint.
client_id = setools-mcp
client_secret = CHANGEME

# Timeout for introspection requests, in seconds.
timeout_seconds = 10

# Scopes that every token must carry.
required_scopes = setools:read

# Public base URL of this server, used to build the OAuth metadata endpoints.
#base_url = https://setools.example.com
43 changes: 43 additions & 0 deletions docs/mcp-auth-jwt.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Verify JWT bearer tokens issued by an external authentication system
# (fastmcp.server.auth.providers.jwt.JWTVerifier).

[server]
host = 127.0.0.1
port = 8000

[auth]
method = jwt

[jwt]
# Exactly one key source is required: jwks_uri, public_key, or public_key_file.
#
# A JWKS endpoint is preferred in production, as it supports key rotation
# without changing this file.
jwks_uri = https://auth.example.com/.well-known/jwks.json

# A PEM-encoded public key may be given inline. Continuation lines must be
# indented. Despite the name, this option also accepts the shared secret when
# an HMAC algorithm (HS256/HS384/HS512) is used.
#public_key = -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
# -----END PUBLIC KEY-----

# Alternatively, read the PEM-encoded public key from a file.
#public_key_file = /etc/setools/mcp-jwt-public-key.pem

# Reject tokens that were not issued by this issuer. A comma-separated list
# accepts tokens from any of the listed issuers.
issuer = https://auth.example.com

# Reject tokens that are not intended for this server. A comma-separated list
# accepts any of the listed audiences.
audience = setools-mcp

# Signature algorithm, if the issuer does not use the default RS256.
#algorithm = ES256

# Scopes that every token must carry.
required_scopes = setools:read

# Public base URL of this server, used to build the OAuth metadata endpoints.
#base_url = https://setools.example.com
11 changes: 11 additions & 0 deletions docs/mcp-auth-none.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Disable MCP server authentication.
#
# Only use this when the server is not reachable by untrusted clients, such as
# an HTTP listener bound to the loopback interface.

[server]
host = 127.0.0.1
port = 8000

[auth]
method = none
Loading
Loading