Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .env.integrations.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ UIG_INGEST_TOKEN=change-me
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=change-me

# Optional model advisory. The deterministic policy remains authoritative.
UIG_MODEL_PROVIDER=openai
UIG_MODEL_NAME=gpt-5.6-luna
UIG_MODEL_BASE_URL=https://api.openai.com/v1
UIG_MODEL_API_KEY=
UIG_MODEL_TIMEOUT_SECONDS=12

# OWASP Core Rule Set WAF. Keep localhost binding unless another trusted edge terminates access.
UIG_BIND_ADDRESS=127.0.0.1
UIG_WAF_PORT=8787
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jobs:
curl --fail --retry 20 --retry-delay 2 --retry-all-errors http://127.0.0.1:8787/healthz
test "$(curl --silent --output /dev/null --write-out '%{http_code}' --get --data-urlencode 'user=example-user' http://127.0.0.1:8787/v1/posture)" = "200"
test "$(curl --silent --output /dev/null --write-out '%{http_code}' --header 'Authorization: Bearer local-dev-change-me' --header 'Content-Type: application/json' --data '{"source":"ci","event_id":"benign","score":1,"confidence":1,"ttl_seconds":60,"detail":"WAF verification"}' http://127.0.0.1:8787/v1/signals)" = "202"
test "$(curl --silent --output /dev/null --write-out '%{http_code}' --header 'Authorization: Bearer local-dev-change-me' --header 'Content-Type: application/json' --data '{"argv":["Get-ChildItem","src"],"purpose":"Inspect project source files","cwd":"/app"}' http://127.0.0.1:8787/v1/assess)" = "200"
intentgate_id="$(docker compose -f docker-compose.observability.yml ps --quiet intentgate)"
docker inspect "$intentgate_id" | jq --exit-status '.[0].NetworkSettings.Ports["8787/tcp"] == null'
- name: Verify CRS blocks an injection probe
Expand Down
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

**A context-aware command execution gate that asks one critical question before code runs: _does this action match the user's intent?_**

[Why it matters](#why-this-project-matters) · [Capabilities](#what-the-project-can-do) · [Use cases](#where-it-can-be-used) · [Quick start](#quick-start) · [Architecture](docs/ARCHITECTURE.md) · [Security](SECURITY.md)
[Why it matters](#why-this-project-matters) · [Capabilities](#what-the-project-can-do) · [Live POC demo](#run-the-full-poc-demo) · [Use cases](#where-it-can-be-used) · [Quick start](#quick-start) · [Architecture](docs/ARCHITECTURE.md) · [Security](SECURITY.md)

</div>

Expand Down Expand Up @@ -67,6 +67,8 @@ This project is intended to help teams investigate a missing security layer betw
| Explainable evidence | Named signals, score contributions, command fingerprint, and latency | Lets operators understand why a decision occurred and tune policy responsibly |
| Privacy-conscious escalation | Secret redaction, local queueing, thresholds, and asynchronous webhook delivery | Enables human oversight without placing notification latency in the command path |
| Operational visibility | Prometheus metrics and a provisioned Grafana dashboard | Exposes decision volume, latency, posture, sources, and report backlog |
| Operator console | Browser-based command assessment, review queue, audit trail, and versioned decision thresholds | Makes the complete decision workflow demonstrable without executing commands from a browser |
| Pluggable model advisor | OpenAI Responses API, OpenAI-compatible endpoints, or a generic model gateway webhook | Adds an independent structured intent recommendation without making a model the enforcement authority |
| Protected integration API | OWASP CRS WAF, backend network isolation, bearer-token ingestion, and bounded requests | Reduces attack surface for the security signals that influence policy |
| Repeatable deployment | Docker Compose, Terraform, and Ansible | Makes the POC reproducible for labs, demos, and controlled evaluations |

Expand Down Expand Up @@ -149,6 +151,20 @@ The decision engine considers:

See [Architecture](docs/ARCHITECTURE.md) and [Threat Model](docs/THREAT_MODEL.md) for the deeper design.

## Run the full POC demo

Docker Desktop is the fastest way to launch the complete showcase: the operator console, AI Advisor, zero-trust and micro-segmentation controls, WAF-protected API, Prometheus, and Grafana.

```powershell
Copy-Item .env.integrations.example .env.integrations
docker compose --env-file .env.integrations -f docker-compose.observability.yml up -d --build
.\scripts\Seed-DemoData.ps1
```

Open the [operator console](http://127.0.0.1:8787/) and select **AI Advisor**, **Trust Controls**, or **Audit Trail**. Grafana is available at [http://127.0.0.1:3000/](http://127.0.0.1:3000/).

The default advisor is an offline, presentation-ready simulation and is always labeled **Demo Simulation** in the interface. It produces structured `ALLOW`, `REVIEW`, and `BLOCK` recommendations without sending data to an external service. To use a real model, configure the ignored `.env.integrations` file as described in [Model Advisory Providers](docs/MODEL_ADVISORS.md).

## Quick start

### Windows PowerShell
Expand Down Expand Up @@ -266,6 +282,55 @@ docker compose --env-file .env.integrations -f docker-compose.observability.yml

The dashboard tracks aggregate security posture, active external signals, decision counts, policy latency, source-level risk, audited commands, and pending manager reports.

## Operator console

The Intent Gate service includes a local operator console for exercising the policy workflow. Start the service and open `http://127.0.0.1:8787/`:

```powershell
$env:UIG_INGEST_TOKEN = "local-dev-change-me"
uig-service
```

Use **Set API token** in the console to enter the configured bearer token. The token is retained only in the current browser tab. The console provides:

- Command and purpose assessment with complete score contributions
- `ALLOW`, `REVIEW`, and `BLOCK` results with policy version and latency
- A human review queue with approve and deny decisions
- An expandable audit surface with user, endpoint, risk score, and scored decision evidence
- Versioned, locally persisted review and block thresholds
- Versioned zero-trust step-up controls and a micro-segmentation flow designer
- An inspectable destructive-action rule catalog

The console is intentionally **assessment-only**. Approving a review records human authorization but never launches the command from the browser. A trusted command broker remains the required production execution boundary.

HTTP assessments use a standard `console-operator` execution context by default rather than inheriting the container service account's root identity. A trusted broker can submit an explicit `execution_context` containing the originating user and privilege level; production enforcement must authenticate that context rather than accepting it directly from an untrusted client.

### Trust controls

The **Trust Controls** console section persists an authenticated local control-plane profile. Zero-trust settings can require declared intent and posture, retain continuous behavior monitoring, and step an otherwise allowed action up to human review at a configured risk threshold. Micro-segmentation settings expose the five Compose network zones and an explicit allow-list of service flows. Network-policy edits are marked `redeploy-required`; saving the design does not silently rewrite or restart Docker networking.

### AI model advisory

The local Docker showcase starts with a clearly labeled, offline Demo Simulation advisor so model-health and structured verdict UI can be demonstrated without a credential. The simulation never contacts an external model and is not an independent security judgment. Configure OpenAI in the ignored `.env.integrations` file to replace it:

```env
UIG_MODEL_PROVIDER=openai
UIG_MODEL_NAME=gpt-5.6-luna
UIG_MODEL_API_KEY=replace-with-an-api-key
```

Recreate the service with `docker compose --env-file .env.integrations -f docker-compose.observability.yml up -d --build intentgate waf`. Local OpenAI-compatible servers and generic frontier-model gateways use the same normalized result contract. See [Model Advisory Providers](docs/MODEL_ADVISORS.md).

### Load showcase data

With the Docker stack running, populate both the operator console and Grafana with a repeatable demo scenario:

```powershell
.\scripts\Seed-DemoData.ps1
```

The seed includes routine development activity, publish and deployment reviews, blocked recovery/security-control operations, manager reports, and correlated Microsoft Defender XDR, CrowdStrike Falcon, and Microsoft Sentinel signals. Every command is assessed only; the seed never executes the submitted command text.

## Web application firewall

Docker deployments publish the official OWASP ModSecurity Core Rule Set Nginx proxy instead of the application container. The backend has no host port and lives on an internal-only network. Blocking is enabled by default at paranoia level 1, with additional level 2 detection telemetry, strict HTTP methods and content types, a 1 MiB body limit, disabled routine access logging, and bounded audit logs that exclude request headers and bodies.
Expand Down
10 changes: 9 additions & 1 deletion docker-compose.observability.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ services:
SERVER_TOKENS: "off"
ports:
- "${UIG_BIND_ADDRESS:-127.0.0.1}:${UIG_WAF_PORT:-8787}:8080"
volumes:
- ./waf/before-crs/intentgate-assessment.conf:/etc/modsecurity.d/owasp-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf:ro
depends_on:
intentgate:
condition: service_healthy
Expand All @@ -43,6 +45,12 @@ services:
environment:
UIG_STATE_DIR: /state
UIG_INGEST_TOKEN: ${UIG_INGEST_TOKEN:-local-dev-change-me}
UIG_MODEL_PROVIDER: ${UIG_MODEL_PROVIDER:-demo}
UIG_MODEL_NAME: ${UIG_MODEL_NAME:-intent-advisor-demo}
UIG_MODEL_BASE_URL: ${UIG_MODEL_BASE_URL:-}
UIG_MODEL_API_KEY: ${UIG_MODEL_API_KEY:-}
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
UIG_MODEL_TIMEOUT_SECONDS: ${UIG_MODEL_TIMEOUT_SECONDS:-12}
volumes:
- ./.intentgate-state:/state
expose: ["8787"]
Expand All @@ -52,7 +60,7 @@ services:
timeout: 3s
retries: 5
start_period: 5s
networks: [application]
networks: [application, outbound]
restart: unless-stopped

prometheus:
Expand Down
4 changes: 4 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The command path should remain fast enough that users do not notice the gate dur
| `reporting.py` | Redact and queue high-risk reports | Local append after decision |
| `notifier.py` | Deliver reports to approved webhooks | Background |
| `service.py` | Ingest signals and expose posture and Prometheus metrics | Background |
| Operator console | Assess commands, record review decisions, inspect audit history, and version thresholds | Browser UI; assessment-only |
| `model_advisory.py` | Request and normalize an independent OpenAI, OpenAI-compatible, or gateway recommendation | Asynchronous console path; never authoritative |
| OWASP CRS WAF | Inspect, constrain, and proxy all host-originated API traffic | Network edge |

## Data flow
Expand All @@ -26,6 +28,8 @@ External signals receive a score, confidence, scope, and TTL. The correlation la

The Docker deployment publishes only the OWASP Core Rule Set WAF. The Intent Gate service is isolated on an internal application network; Prometheus reaches it there for scraping, while external webhook and API clients traverse the WAF. See [WAF Operations](WAF.md).

The operator console is served by the same service and uses bearer-authenticated local APIs. It does not own process creation: command assessments are recorded with `executed=false`, and review approvals change only the review record. This keeps a browser compromise from becoming a direct command-execution primitive.

Audit and report files are local JSON/JSONL in `UIG_STATE_DIR`. The PowerShell helper defaults this to `.intentgate-state` in the project so the host CLI and Docker observability services share the same state.

## Latency model
Expand Down
80 changes: 80 additions & 0 deletions docs/MODEL_ADVISORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Model advisory providers

Intent Gate can request a second, model-generated determination after the deterministic policy returns. Model output is **advisory only**: it cannot execute a command, approve a review, weaken a deterministic block, or delay the CLI enforcement path.

## Provider contract

Every provider receives a bounded, redacted context containing the command, declared purpose, project and Git state, privilege, recent activity, external posture, and deterministic assessment. It must return:

```json
{
"recommended_decision": "review",
"risk_score": 62,
"confidence": 0.88,
"intent_alignment": "unclear",
"summary": "The action has an external side effect and needs confirmation.",
"reasons": ["The target environment is not identified by the declared purpose."]
}
```

Responses are schema-checked and normalized before display. Failures, timeouts, missing credentials, invalid JSON, or unsupported classifications do not affect deterministic policy.

## Demo Simulation

The local showcase defaults to a clearly labeled simulation provider when no integration environment file overrides it:

```env
UIG_MODEL_PROVIDER=demo
UIG_MODEL_NAME=intent-advisor-demo
```

It generates schema-valid advisory output locally from the deterministic evidence, adds a short presentation-friendly inference delay, and never contacts an external model. The console labels the provider and every determination as a demo simulation. Use this only for demonstrations and switch to one of the providers below for genuine independent model analysis.

## OpenAI

The OpenAI adapter uses the Responses API with Structured Outputs:

```env
UIG_MODEL_PROVIDER=openai
UIG_MODEL_NAME=gpt-5.6-luna
UIG_MODEL_BASE_URL=https://api.openai.com/v1
UIG_MODEL_API_KEY=replace-with-an-api-key
UIG_MODEL_TIMEOUT_SECONDS=12
```

An OpenAI API key is separate from a ChatGPT subscription. Keep it in an ignored `.env.integrations` file or an approved secret store; never commit it.

## OpenAI-compatible local or hosted model

Use an endpoint that implements `POST /v1/chat/completions` and JSON-schema response formatting:

```env
UIG_MODEL_PROVIDER=openai-compatible
UIG_MODEL_NAME=local-model-name
UIG_MODEL_BASE_URL=http://host.docker.internal:8000/v1
UIG_MODEL_API_KEY=
```

Compatibility varies by server. The adapter fails closed to “advisor unavailable” when the server does not honor the response schema.

## Generic model gateway webhook

Use a gateway for Anthropic, Google, another frontier provider, or an internal routing service:

```env
UIG_MODEL_PROVIDER=webhook
UIG_MODEL_NAME=enterprise-model-router
UIG_MODEL_BASE_URL=https://model-gateway.example.invalid/v1/intent-assess
UIG_MODEL_API_KEY=replace-with-gateway-token
```

The webhook receives `schema_version`, the system classification instructions, bounded context, and the required response schema. It must return the normalized JSON object shown above.

## Security boundary

- Commands and recent history are redacted for common credential forms before leaving the service.
- Full filesystem paths are reduced to the project directory name.
- The model endpoint is called only by the authenticated console advisory route.
- The deterministic decision is returned and recorded independently.
- Model latency is not included in policy-engine latency.
- Production deployments should add provider-specific data retention, residency, identity, rate-limit, and audit controls.
4 changes: 4 additions & 0 deletions docs/WAF.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ docker compose -f docker-compose.observability.yml logs waf

The public API endpoint remains port `8787`; traffic now terminates at the WAF and is proxied internally. Prometheus intentionally scrapes the backend over the private application network.

### Command-assessment payloads

`POST /v1/assess` and `POST /v1/model-assess` intentionally accept shell and PowerShell command text. The Compose deployment mounts route-scoped CRS exclusions from `waf/before-crs` so generic RCE signatures do not consume the very command text Intent Gate must evaluate. The exclusions remove only the `attack-rce` rule tag for those exact routes; SQL injection, protocol validation, size limits, method restrictions, malformed JSON handling, and the remaining CRS protections stay active.

## Tune safely

Copy `.env.integrations.example` to the ignored `.env.integrations` file. Begin new rules or higher paranoia levels in `DetectionOnly`, observe representative traffic, document false positives, and then switch back to `On`. Do not raise anomaly thresholds as a substitute for a narrow, reviewed rule exclusion.
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ uig-notifier = "intentgate.notifier:main"

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
intentgate = ["web/*"]
Loading
Loading