apparitor 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- apparitor-0.1.1/.gitignore +30 -0
- apparitor-0.1.1/CHANGELOG.md +242 -0
- apparitor-0.1.1/CONTRIBUTING.md +116 -0
- apparitor-0.1.1/LICENSE +201 -0
- apparitor-0.1.1/NOTICE +8 -0
- apparitor-0.1.1/PKG-INFO +409 -0
- apparitor-0.1.1/README.md +358 -0
- apparitor-0.1.1/ROADMAP.md +159 -0
- apparitor-0.1.1/SECURITY.md +71 -0
- apparitor-0.1.1/docs/architecture.md +119 -0
- apparitor-0.1.1/docs/audit-log.md +264 -0
- apparitor-0.1.1/docs/eu-ai-act.md +112 -0
- apparitor-0.1.1/docs/requirements.md +194 -0
- apparitor-0.1.1/docs/security-review.md +224 -0
- apparitor-0.1.1/docs/setup.md +183 -0
- apparitor-0.1.1/examples/README.md +30 -0
- apparitor-0.1.1/examples/avp/README.md +6 -0
- apparitor-0.1.1/examples/cedar/README.md +137 -0
- apparitor-0.1.1/examples/cedar/docker-compose.yml +18 -0
- apparitor-0.1.1/examples/cedar/entities.json +19 -0
- apparitor-0.1.1/examples/cedar/gateway/Dockerfile +18 -0
- apparitor-0.1.1/examples/cedar/gateway/gateway.py +214 -0
- apparitor-0.1.1/examples/cedar/policies.cedar +19 -0
- apparitor-0.1.1/examples/cedar/smoke.sh +67 -0
- apparitor-0.1.1/examples/gateway/README.md +65 -0
- apparitor-0.1.1/examples/gateway/demo.py +198 -0
- apparitor-0.1.1/examples/mock_pdp/README.md +37 -0
- apparitor-0.1.1/examples/mock_pdp/mock_pdp.py +116 -0
- apparitor-0.1.1/examples/opa/README.md +140 -0
- apparitor-0.1.1/examples/opa/data.json +7 -0
- apparitor-0.1.1/examples/opa/docker-compose.yml +20 -0
- apparitor-0.1.1/examples/opa/gateway/Dockerfile +19 -0
- apparitor-0.1.1/examples/opa/gateway/gateway.py +224 -0
- apparitor-0.1.1/examples/opa/policy.rego +17 -0
- apparitor-0.1.1/examples/opa/smoke.sh +67 -0
- apparitor-0.1.1/examples/openfga/README.md +85 -0
- apparitor-0.1.1/examples/openfga/docker-compose.yml +22 -0
- apparitor-0.1.1/examples/openfga/model.json +19 -0
- apparitor-0.1.1/examples/openfga/smoke.sh +56 -0
- apparitor-0.1.1/examples/openfga/tuples.json +4 -0
- apparitor-0.1.1/examples/scenarios/README.md +47 -0
- apparitor-0.1.1/examples/scenarios/run.py +282 -0
- apparitor-0.1.1/examples/three-peps/README.md +51 -0
- apparitor-0.1.1/examples/three-peps/demo.py +216 -0
- apparitor-0.1.1/pyproject.toml +190 -0
- apparitor-0.1.1/src/apparitor/__init__.py +172 -0
- apparitor-0.1.1/src/apparitor/a2a.py +293 -0
- apparitor-0.1.1/src/apparitor/adapters.py +137 -0
- apparitor-0.1.1/src/apparitor/backends.py +153 -0
- apparitor-0.1.1/src/apparitor/cache.py +82 -0
- apparitor-0.1.1/src/apparitor/cedar.py +158 -0
- apparitor-0.1.1/src/apparitor/client.py +270 -0
- apparitor-0.1.1/src/apparitor/config.py +117 -0
- apparitor-0.1.1/src/apparitor/decision.py +205 -0
- apparitor-0.1.1/src/apparitor/engine.py +548 -0
- apparitor-0.1.1/src/apparitor/errors.py +70 -0
- apparitor-0.1.1/src/apparitor/fastmcp.py +487 -0
- apparitor-0.1.1/src/apparitor/mapping.py +381 -0
- apparitor-0.1.1/src/apparitor/metrics.py +99 -0
- apparitor-0.1.1/src/apparitor/models.py +163 -0
- apparitor-0.1.1/src/apparitor/nemo.py +204 -0
- apparitor-0.1.1/src/apparitor/py.typed +0 -0
- apparitor-0.1.1/src/apparitor/scanner.py +136 -0
- apparitor-0.1.1/tests/conformance/README.md +52 -0
- apparitor-0.1.1/tests/conformance/cases.json +151 -0
- apparitor-0.1.1/tests/conformance/interop_todo_cases.json +452 -0
- apparitor-0.1.1/tests/conformance/test_conformance.py +116 -0
- apparitor-0.1.1/tests/conformance/test_interop_todo.py +157 -0
- apparitor-0.1.1/tests/conftest.py +93 -0
- apparitor-0.1.1/tests/integration/__init__.py +0 -0
- apparitor-0.1.1/tests/integration/_helpers.py +191 -0
- apparitor-0.1.1/tests/integration/test_cedar.py +85 -0
- apparitor-0.1.1/tests/integration/test_opa.py +85 -0
- apparitor-0.1.1/tests/integration/test_opa_native.py +77 -0
- apparitor-0.1.1/tests/integration/test_openfga.py +125 -0
- apparitor-0.1.1/tests/unit/test_a2a_executor.py +765 -0
- apparitor-0.1.1/tests/unit/test_adapters.py +90 -0
- apparitor-0.1.1/tests/unit/test_backends.py +369 -0
- apparitor-0.1.1/tests/unit/test_cache.py +80 -0
- apparitor-0.1.1/tests/unit/test_cedar_backend.py +314 -0
- apparitor-0.1.1/tests/unit/test_cedar_gateway.py +213 -0
- apparitor-0.1.1/tests/unit/test_client.py +302 -0
- apparitor-0.1.1/tests/unit/test_decision.py +209 -0
- apparitor-0.1.1/tests/unit/test_engine.py +1173 -0
- apparitor-0.1.1/tests/unit/test_fastmcp_middleware.py +910 -0
- apparitor-0.1.1/tests/unit/test_golden.py +41 -0
- apparitor-0.1.1/tests/unit/test_log_contract.py +285 -0
- apparitor-0.1.1/tests/unit/test_mapping.py +351 -0
- apparitor-0.1.1/tests/unit/test_metrics.py +239 -0
- apparitor-0.1.1/tests/unit/test_mock_pdp.py +67 -0
- apparitor-0.1.1/tests/unit/test_models.py +63 -0
- apparitor-0.1.1/tests/unit/test_nemo_backend.py +179 -0
- apparitor-0.1.1/tests/unit/test_opa_gateway.py +253 -0
- apparitor-0.1.1/tests/unit/test_scanner.py +106 -0
- apparitor-0.1.1/tests/unit/test_security.py +123 -0
- apparitor-0.1.1/tests/unit/test_smoke.py +201 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
sbom/
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
|
|
12
|
+
# Test / coverage
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.hypothesis/
|
|
17
|
+
.coverage
|
|
18
|
+
.coverage.*
|
|
19
|
+
htmlcov/
|
|
20
|
+
coverage.xml
|
|
21
|
+
|
|
22
|
+
# Secrets — never commit real credentials or env files
|
|
23
|
+
.env
|
|
24
|
+
.env.*
|
|
25
|
+
!.env.example
|
|
26
|
+
|
|
27
|
+
# Editors / OS
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
30
|
+
.DS_Store
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.1.1] - 2026-06-16
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **`request_context_scope(context)` context manager** (`apparitor.mapping`, exported from
|
|
11
|
+
`apparitor`). Mirrors `subject_scope`: binds `current_request_context` for the duration
|
|
12
|
+
of the `with` block and resets it in `finally`, so a host that forgets to reset cannot
|
|
13
|
+
leak a prior request's injected context into a later request on a reused task/loop. This
|
|
14
|
+
is now the preferred pattern over calling `.set()/.reset()` directly (documented in
|
|
15
|
+
`docs/setup.md`).
|
|
16
|
+
- **Security posture & assurance document** (`docs/security-review.md`): standing
|
|
17
|
+
invariants, threat-model coverage, test citations, known limitations, and re-review
|
|
18
|
+
triggers. A continuously scannable baseline.
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
- **PDP response parsing now rejects duplicate JSON keys (§3.6), failing closed with
|
|
22
|
+
`MalformedPDPResponseError`.** An `object_pairs_hook` in the new `_strict_json` helper
|
|
23
|
+
raises `MalformedPDPResponseError` on any duplicate key within a JSON object, closing a
|
|
24
|
+
response-validation gap at the PDP trust boundary. The fix applies to both the AuthZEN
|
|
25
|
+
transport and the OPA backend. Sibling objects in a batch response (multiple `"decision"`
|
|
26
|
+
fields in distinct array entries) are correctly not flagged; the check is per-object.
|
|
27
|
+
- **Error-path `VerdictResult.reason` is now always a generic string (§3.10).** All
|
|
28
|
+
error-path verdicts use the fixed `_DENY_REASON` constant, ensuring internal detail
|
|
29
|
+
(exception text, transport state) is never exposed to callers. Exception detail continues
|
|
30
|
+
to appear in operator logs for diagnostics. The fix also adds log lines on early-return
|
|
31
|
+
paths that previously had none (unparseable tool call, 4xx client error).
|
|
32
|
+
- **`asyncio.CancelledError` is now recorded in metrics before re-propagating.** A
|
|
33
|
+
dedicated `except asyncio.CancelledError` clause in `_evaluate_guarded` records a
|
|
34
|
+
`block/error` decision metric and immediately re-raises, preserving
|
|
35
|
+
structured-concurrency invariants. The public `evaluate_normalized` and
|
|
36
|
+
`evaluate_requests` docstrings note that `CancelledError` propagates and callers must
|
|
37
|
+
treat an unfinished scan as non-authorized.
|
|
38
|
+
- **Cedar `_entity_uid` hardened to reject backslash and control characters.** The
|
|
39
|
+
rejection is now explicit at the validation seam (covering double-quote, backslash, and
|
|
40
|
+
control characters), so invalid identifiers are caught before Cedar processes them. The
|
|
41
|
+
engine already failed closed on a Cedar parse error (NoDecision becomes BLOCK); this makes
|
|
42
|
+
the boundary explicit.
|
|
43
|
+
- **`.env` added to `.gitignore`; `llamafirewall` extra capped at `<1`.** Prevents
|
|
44
|
+
accidental credential commits and pins the audited major version of the ML stack.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
- **`AuthZENConfigError` now also inherits `ValueError` for backward compatibility.**
|
|
48
|
+
Adapter constructors previously raised plain `ValueError` for misconfiguration (missing
|
|
49
|
+
PDP URL, reserved subject type, invalid label, now all four adapters, including the
|
|
50
|
+
scanner). Code catching `except ValueError` still catches these; code that was already
|
|
51
|
+
catching `except AuthZENConfigError` is unaffected. Pre-1.0 decision: keeping the mixin
|
|
52
|
+
avoids a breaking change for callers that only imported the public error hierarchy after
|
|
53
|
+
the fact. Update existing `except ValueError` handlers that handle only configuration
|
|
54
|
+
errors to catch `AuthZENConfigError` instead for precision. `build_engine` now also
|
|
55
|
+
rejects providing both `pdp_url` and `config` simultaneously (previously config silently
|
|
56
|
+
won; now a `AuthZENConfigError` is raised).
|
|
57
|
+
- **Inline/gateway SKIP semantics and batch-item merge logic unified across all adapters.**
|
|
58
|
+
`is_allowed_inline`, `is_allowed_gateway`, `record_pre_engine_refusal`, and
|
|
59
|
+
`DUAL_PRINCIPAL_CACHE_WARNING` are shared helpers in `apparitor.decision` (lowest-common
|
|
60
|
+
import, engine-free and host-SDK-free to avoid import cycles); their divergent SKIP
|
|
61
|
+
behaviour and the `is not None` batch-item merge semantics are pinned by tests.
|
|
62
|
+
|
|
63
|
+
## [0.1.0] - 2026-06-11
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
- **Audit-log schema frozen as a stability contract (`docs/audit-log.md`).** Logger,
|
|
67
|
+
levels, C1/C2/C3 line grammar (format strings, field tables, rendered examples),
|
|
68
|
+
fingerprint derivation, parsing guidance, timestamp/clock requirements, an EU
|
|
69
|
+
regulatory mapping (AI Act record-keeping and retention, GDPR handling, transfer
|
|
70
|
+
routing), and stability policy. Pinned by
|
|
71
|
+
`tests/unit/test_log_contract.py`. A failure there is a breaking log-schema change
|
|
72
|
+
requiring a CHANGELOG "Update log parsers" entry and a version bump.
|
|
73
|
+
- **Runnable MCP authorization-gateway example (`examples/gateway/`).** A FastMCP proxy
|
|
74
|
+
fronting an unmodifiable upstream, middleware enforcing at the chokepoint: denied calls
|
|
75
|
+
proven never to reach the upstream, listing filtered to hide what the subject may not
|
|
76
|
+
call. Exercised on both supported fastmcp lines by the `gateway-demo` CI job.
|
|
77
|
+
- **Runnable scenario walk-through (`examples/scenarios/`).** Dependency-free (core install
|
|
78
|
+
only) self-asserting script over the mock PDP covering seven scenarios: allow, deny
|
|
79
|
+
(2-part deny key), unparseable input fails closed, PDP unreachable with `on_error=deny`,
|
|
80
|
+
PDP unreachable with `on_error=human_review`, batch all-or-nothing aggregation, and
|
|
81
|
+
dual-principal (user AND agent boundary) evaluation. Gated in CI by the `scenarios` job.
|
|
82
|
+
Also: the mock PDP now supports a subject-scoped **3-part deny form**
|
|
83
|
+
(`"<subject_id>:<action>:<resource_id>"`) in addition to the existing 2-part form.
|
|
84
|
+
- **Dual-principal evaluation (`DualPrincipalMapper`).** Emits two requests per tool
|
|
85
|
+
call (the end user's grant and the agent's own permission boundary), ANDed by the
|
|
86
|
+
engine's all-allow-or-block aggregation, so an agent can never exercise a permission
|
|
87
|
+
its boundary denies even when the human holds it. The user leg is request-scoped only
|
|
88
|
+
(no `agent_id` fallback, which would collapse the AND); either principal unresolvable
|
|
89
|
+
fails closed; `"workload"` stays reserved. Works via the `mapper=` seam in the scanner,
|
|
90
|
+
the NeMo rail and the FastMCP middleware (tools + listing). Note: dual calls always
|
|
91
|
+
evaluate as a batch, so the opt-in ALLOW cache is not consulted.
|
|
92
|
+
- **Dual-principal boundary for adapter-shaped paths (closes
|
|
93
|
+
[#39](https://github.com/jhawlwut/apparitor/issues/39)).** `A2AAuthorizationExecutor`
|
|
94
|
+
and `FastMCPAuthorizationMiddleware` now accept a `boundary_subject` constructor parameter.
|
|
95
|
+
When set, every invocation (A2A) or every `resources/read` / `prompts/get` request (FastMCP)
|
|
96
|
+
is evaluated as a two-request batch (the resolved caller leg AND the boundary leg) using
|
|
97
|
+
the same AND/all-allow-or-block semantics as `DualPrincipalMapper`. For FastMCP,
|
|
98
|
+
`boundary_subject` covers the resource/prompt adapter paths only; use
|
|
99
|
+
`mapper=DualPrincipalMapper(config)` for `tools/call` and listing. A full deployment sets
|
|
100
|
+
both. The same fail-closed guards apply: `"workload"` boundary rejected at construction,
|
|
101
|
+
collapse (boundary == caller) refused before any PDP call, cache warning emitted when
|
|
102
|
+
`cache_enabled=True`. Shared helper `build_boundary_leg` in `apparitor.mapping` (exported
|
|
103
|
+
from the package) powers both adapters.
|
|
104
|
+
- `ToolCallMapper.map()` may now return a **sequence** of requests that must all be
|
|
105
|
+
allowed (backward compatible; `None`/empty sequence abstains, since an empty group can
|
|
106
|
+
never read as an allow, on either the aggregate or the per-item path).
|
|
107
|
+
- **A2A agent-executor enforcement point (`apparitor.a2a`, optional `[a2a]` extra).**
|
|
108
|
+
`A2AAuthorizationExecutor` wraps a deployment's `AgentExecutor` and authorizes every
|
|
109
|
+
agent-to-agent invocation before it runs (action `agent.invoke`; resource
|
|
110
|
+
`{type: "a2a_agent", id: <agent_label>}`, or `a2a_skill` with a `"<agent>/<skill>"` key
|
|
111
|
+
via the `skill_resolver` hook; segments validated, skill kept verbatim). The subject is
|
|
112
|
+
the server's **authenticated peer** (`Subject(type="agent", id=<user_name>)` by
|
|
113
|
+
default), falling back to a subject injected per request via
|
|
114
|
+
`ServerCallContext.state["subject"]` and then the opt-in static `agent_id`. An
|
|
115
|
+
unauthenticated caller is refused, and ambient contextvars are deliberately ignored
|
|
116
|
+
(the SDK's detached producer task snapshots them, so they go stale across turns). The request's A2A `tenant` is forwarded in the
|
|
117
|
+
AuthZEN context for multi-tenant policies (a caller-supplied claim for policies to
|
|
118
|
+
cross-check, not proof). Verdicts map fail-closed: only a clean
|
|
119
|
+
`ALLOW` reaches the wrapped executor; everything else raises a deliberately generic A2A
|
|
120
|
+
error (the rich reason stays in the operator log). `cancel` passes through ungated in
|
|
121
|
+
v1 (documented). Built on `AuthorizationEngine.evaluate_requests`, no core changes.
|
|
122
|
+
- **Complete MCP enforcement scope for the FastMCP middleware** (closes
|
|
123
|
+
[#32](https://github.com/jhawlwut/apparitor/issues/32),
|
|
124
|
+
[#33](https://github.com/jhawlwut/apparitor/issues/33),
|
|
125
|
+
[#34](https://github.com/jhawlwut/apparitor/issues/34)):
|
|
126
|
+
- `resources/read` and `prompts/get` are now **gated by default** (actions
|
|
127
|
+
`resource.read` / `prompt.get`; resource ids: the URI verbatim for resources with the
|
|
128
|
+
server label as a property, `"<server>/<prompt>"` for prompts), with the same subject
|
|
129
|
+
chain, generic refusals (`ResourceError`/`PromptError`) and fail-closed verdict
|
|
130
|
+
mapping as tool calls; `gate_resources=False` / `gate_prompts=False` opt a hook out.
|
|
131
|
+
**Breaking (pre-alpha):** deployments without resource/prompt policies will see those
|
|
132
|
+
requests denied; write policies or opt out.
|
|
133
|
+
- Opt-in `filter_listings=True` hides tools from `tools/list` whose `tools/call` the
|
|
134
|
+
subject would be denied. One batch PDP round trip, advisory only (`tools/call`
|
|
135
|
+
remains the enforcement invariant), and fail-closed (no subject or any fault hides
|
|
136
|
+
everything).
|
|
137
|
+
- Opt-in `allow_workload_subject=True` authorizes verified client-credentials tokens
|
|
138
|
+
(no `sub` claim) as the distinct `Subject(type="workload", id=<client_id>)`, never
|
|
139
|
+
coerced into a user subject; off by default, such tokens keep refusing. The
|
|
140
|
+
`"workload"` subject type is reserved (the constructor rejects it for claim-derived
|
|
141
|
+
and static subjects). Only `tools/list` is filtered; `resources/list` and
|
|
142
|
+
`prompts/list` still advertise names/URIs even though reads/gets are gated.
|
|
143
|
+
- `AuthorizationEngine.evaluate_requests()` (pre-mapped requests, e.g. adapter-shaped
|
|
144
|
+
resource/prompt tuples) and `AuthorizationEngine.evaluate_each()` (positional per-item
|
|
145
|
+
verdicts over one batch round trip, for visibility filtering, fail-closed per item).
|
|
146
|
+
- **Three-PEP portability demo (`examples/three-peps/`).** The same vendored Cedar policy
|
|
147
|
+
(`examples/cedar/policies.cedar`, deny-override on `destructive == true`) enforced
|
|
148
|
+
identically at the LlamaFirewall scanner, the NeMo Guardrails rail, and the FastMCP
|
|
149
|
+
middleware over the in-process Cedar backend: no Docker, no network. Self-asserting
|
|
150
|
+
(exits non-zero on any verdict mismatch) and gated in CI by the `three-pep-demo` job,
|
|
151
|
+
which installs all three enforcement-point extras and requires every lane to run.
|
|
152
|
+
- **FastMCP server-middleware enforcement point (`apparitor.fastmcp`, optional `[fastmcp]`
|
|
153
|
+
extra).** `FastMCPAuthorizationMiddleware` authorizes every MCP `tools/call` server-side,
|
|
154
|
+
before the tool executes, over the same `AuthorizationEngine` as the LlamaFirewall scanner
|
|
155
|
+
and the NeMo rail. The subject comes from the **validated** OAuth access token
|
|
156
|
+
(`claims["sub"]`, configurable) and outranks host-asserted subjects; a token without a
|
|
157
|
+
usable claim refuses, and the static `agent_id` fallback is gated behind an explicit
|
|
158
|
+
`allow_static_subject=True` opt-in (local/stdio only). Verdicts map fail-closed onto MCP:
|
|
159
|
+
only a clean `ALLOW` executes; `BLOCK` / `HUMAN_REVIEW` / mapper abstention / errors raise
|
|
160
|
+
a deliberately generic `ToolError` (the rich reason stays in the operator log). Supports
|
|
161
|
+
fastmcp 2.14 and 3.x (both exercised in CI).
|
|
162
|
+
- `AuthorizationEngine.evaluate_normalized()`: a public seam for enforcement points that
|
|
163
|
+
receive tool calls in structured form (no provider-shape adapter detection).
|
|
164
|
+
- `MCPResourceMapper` can now resolve its server label per call from
|
|
165
|
+
`request_context[MCP_SERVER_LABEL_KEY]`; the tool segment gets the same case/whitespace
|
|
166
|
+
normalisation as the default mapper, and `mcp_resource_id` rejects embedded `/` (an
|
|
167
|
+
ambiguous policy key), fail-closed.
|
|
168
|
+
- `DecisionCache` is bounded: new `cache_max_entries` config (default 10 000, FIFO
|
|
169
|
+
eviction) so per-token subject cardinality cannot grow the ALLOW cache without limit.
|
|
170
|
+
- **Docker-free OpenFGA integration backend (linux/amd64).** Set `APPARITOR_OPENFGA_NATIVE=1` to run the
|
|
171
|
+
OpenFGA E2E against a pinned, **SHA-256-verified** OpenFGA release binary launched directly
|
|
172
|
+
(only `github.com` egress needed) instead of a container, so the real-PDP test works where
|
|
173
|
+
the Docker registry is unreachable. Same vendored model + tuples and assertions; a
|
|
174
|
+
`workflow_dispatch` `integration-native` CI job exercises it.
|
|
175
|
+
- **Working scan pipeline (M1).** `AuthZENScanner.scan()` authorizes an agent's tool
|
|
176
|
+
calls end-to-end: extract → map → evaluate → decide.
|
|
177
|
+
- LlamaFirewall-free `AuthorizationEngine` holding all logic, so the pipeline is fully
|
|
178
|
+
unit-testable without the ML stack. The scanner is a thin adapter that converts the
|
|
179
|
+
verdict to a `ScanResult`.
|
|
180
|
+
- `AuthZENClient`: async httpx transport with explicit timeouts, a request budget,
|
|
181
|
+
bounded retries (429/5xx/transport only) with jittered backoff, an SSRF/TLS guard on
|
|
182
|
+
`pdp_url`, bring-your-own-client support, and httpx→typed-error mapping.
|
|
183
|
+
- `DefaultToolCallMapper` / `MCPResourceMapper`: request-scoped subject resolution
|
|
184
|
+
(never from model output), argument redaction/size-caps, MCP server-scoped resource ids.
|
|
185
|
+
- Opt-in, ALLOW-only TTL `DecisionCache` with a full-tuple SHA-256 key.
|
|
186
|
+
- Batch evaluation with all-allow-or-block aggregation; `review_predicate` escalation
|
|
187
|
+
(never downgrade); fail-closed `on_error` (deny / human_review).
|
|
188
|
+
- AuthZEN 1.0 pydantic models; provider-aware tool-call adapters (OpenAI / Anthropic /
|
|
189
|
+
LangChain); `ScannerConfig` with secure defaults; exception hierarchy.
|
|
190
|
+
- A dependency-free mock AuthZEN PDP for demos/tests.
|
|
191
|
+
- Test suite: 90+ unit tests, 98% line+branch coverage on the LlamaFirewall-free
|
|
192
|
+
modules (90% gate enforced), including the security invariants.
|
|
193
|
+
- **Real PDP examples (M3).** Runnable OpenFGA example (native, experimental AuthZEN API;
|
|
194
|
+
vendored model + relationship tuples) and Cedar example (policy-as-code behind a local
|
|
195
|
+
AuthZEN → Cedar gateway running the official Cedar CLI), each with a `smoke.sh` and a
|
|
196
|
+
Docker-gated `testcontainers` integration test that skips when no daemon is present.
|
|
197
|
+
- `integration` optional-dependency group (`testcontainers`) and a `workflow_dispatch`
|
|
198
|
+
CI job that runs the integration suite.
|
|
199
|
+
- **Observability (M2).** A dependency-free metrics sink (`MetricsSink` protocol with a
|
|
200
|
+
default `InMemoryMetrics` and a `NoopMetrics` opt-out): a decision-latency histogram and
|
|
201
|
+
a cache-hit/miss counter, surfaced on the engine and scanner. Structured audit logs now
|
|
202
|
+
carry the verdict, status, subject (decision principal), correlation id, and an argument
|
|
203
|
+
*fingerprint*; raw arguments and tokens are never logged (arguments are fingerprinted).
|
|
204
|
+
- **AuthZEN 1.0 wire-conformance suite** (`tests/conformance/`): vendored canonical
|
|
205
|
+
request/response payloads driven through the models and client to prove wire
|
|
206
|
+
compatibility (request shapes, response decisions, batch aggregation, and malformed
|
|
207
|
+
responses failing closed).
|
|
208
|
+
|
|
209
|
+
### Changed
|
|
210
|
+
|
|
211
|
+
- **Audit-log message prefixes normalized `authzen` → `apparitor`** to match the logger
|
|
212
|
+
name and project name. Field names and grammar are unchanged. **Update log parsers**
|
|
213
|
+
anchoring on the old `authzen decision`, `authzen batch`, or `authzen per-item`
|
|
214
|
+
prefixes: replace with `apparitor decision`, `apparitor batch`,
|
|
215
|
+
`apparitor per-item`.
|
|
216
|
+
- The structured decision log's resource-id field is now `resources=` (was `tools=`).
|
|
217
|
+
It can carry resource URIs and prompt keys, not just tool names. Update log parsers.
|
|
218
|
+
- The structured decision log now records **every distinct principal** as `subjects=`
|
|
219
|
+
(was `subject=` with only the first leg). Under dual-principal evaluation the audit
|
|
220
|
+
trail must name both the user and the agent, and `resources=` / `fingerprints=`
|
|
221
|
+
carry one entry per evaluation request, so dual legs appear twice. Update log parsers.
|
|
222
|
+
- `DefaultToolCallMapper.map()`'s return annotation is widened to the full
|
|
223
|
+
`ToolCallMapper` union; a typed subclass that delegates to `super().map()` must
|
|
224
|
+
accept the widened return (runtime behaviour of the default mapper is unchanged).
|
|
225
|
+
- **Renamed to `apparitor` and repositioned.** The project is now a vendor-neutral
|
|
226
|
+
authorization layer that aggregates policy engines (AuthZEN, Cedar, OpenFGA, Rego) across
|
|
227
|
+
agentic firewalls (LlamaFirewall today; NeMo Guardrails planned), rather than an
|
|
228
|
+
AuthZEN-scanner-for-LlamaFirewall only. The Python import package and PyPI distribution
|
|
229
|
+
are now **`apparitor`** (was `authzen_llamafirewall` / `authzen-llamafirewall-scanner`):
|
|
230
|
+
`from apparitor import AuthZENScanner`. Breaking import change, acceptable pre-alpha, no
|
|
231
|
+
published release affected. Public API names (`AuthZENScanner`, `AuthorizationEngine`, …)
|
|
232
|
+
are unchanged.
|
|
233
|
+
- **Spec fix:** the batch options field is now `evaluations_semantic` (plural), matching
|
|
234
|
+
AuthZEN 1.0; it was previously serialised as `evaluation_semantic`. Renames
|
|
235
|
+
`EvaluationsOptions.evaluation_semantic` → `evaluations_semantic` (pre-alpha, breaking).
|
|
236
|
+
- `DefaultToolCallMapper._resource` takes the request context (so `MCPResourceMapper` can
|
|
237
|
+
resolve a per-call server label). A custom mapper subclass overriding the protected
|
|
238
|
+
`_resource` must adopt the new signature; the public `ToolCallMapper.map` contract is
|
|
239
|
+
unchanged.
|
|
240
|
+
|
|
241
|
+
## [0.0.1a0]
|
|
242
|
+
- Initial pre-alpha scaffold.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest! This is an Apache-2.0, public-standards-only project. Please keep
|
|
4
|
+
contributions free of proprietary or confidential material.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
python -m venv .venv && source .venv/bin/activate
|
|
10
|
+
pip install -e ".[dev]" # AuthZEN client/models + tooling
|
|
11
|
+
pip install -e ".[dev,llamafirewall]" # add the scanner path (pulls the LlamaFirewall ML stack)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The `[llamafirewall]` extra is heavy (it brings PromptGuard's ML dependencies). The
|
|
15
|
+
AuthZEN-free modules (`models`, `client`, `adapters`, `mapping`, `cache`, `config`,
|
|
16
|
+
`errors`) develop and test fine **without** it.
|
|
17
|
+
|
|
18
|
+
## Checks
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
ruff check . # lint
|
|
22
|
+
ruff format . # format
|
|
23
|
+
mypy src/ # types (strict)
|
|
24
|
+
pytest # unit suite (integration excluded by default)
|
|
25
|
+
pytest -m integration # integration (needs Docker; auto-skips when absent)
|
|
26
|
+
python -m build && twine check dist/* # packaging
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
CI runs lint, types, the unit suite on Python 3.10 to 3.13, and a packaging check. The
|
|
30
|
+
coverage gate (≥90% line+branch on the LlamaFirewall-free modules) turns on with the
|
|
31
|
+
behavioural suite.
|
|
32
|
+
|
|
33
|
+
## Branch naming
|
|
34
|
+
|
|
35
|
+
Format: **`<type>/<short-kebab-summary>`**, optionally with an issue number:
|
|
36
|
+
`<type>/<issue>-<summary>`. Lowercase, hyphen-separated, 3 to 5 words.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
feat/authzen-client
|
|
40
|
+
fix/cache-ttl-clamp
|
|
41
|
+
docs/setup-guide
|
|
42
|
+
feat/42-batch-evaluation
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`<type>` matches the commit types below (`feat`, `fix`, `docs`, `refactor`, `test`,
|
|
46
|
+
`chore`, `ci`, `perf`). Automated agent sessions may push to tool-managed branches like
|
|
47
|
+
`claude/<slug>-<id>`. The trailing id is intentional collision-avoidance, not a naming
|
|
48
|
+
choice. Human-driven branches should use the convention above.
|
|
49
|
+
|
|
50
|
+
## Commit messages: [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
<type>(<optional scope>): <imperative summary> # ≤72 chars, lowercase, no trailing period
|
|
54
|
+
|
|
55
|
+
<body: explain *why*, not what; wrapped at 72 cols>
|
|
56
|
+
|
|
57
|
+
<footers: e.g. "Closes #123", "BREAKING CHANGE: ...">
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- **Types:** `feat` (new feature), `fix` (bug fix), `docs`, `refactor`, `perf`, `test`,
|
|
61
|
+
`build`, `ci`, `chore`, `revert`.
|
|
62
|
+
- **Scope** (optional) names the area, e.g. `feat(client):`, `fix(cache):`.
|
|
63
|
+
- **Breaking changes:** append `!` (`feat(config)!: ...`) and/or add a
|
|
64
|
+
`BREAKING CHANGE:` footer.
|
|
65
|
+
- Imperative mood ("add", not "added"/"adds"). The subject says *what*; the body says
|
|
66
|
+
*why*.
|
|
67
|
+
|
|
68
|
+
## Pull requests
|
|
69
|
+
|
|
70
|
+
- One logical change per PR; keep them small and reviewable. Open as **draft** early.
|
|
71
|
+
- PR **title** uses the Conventional Commits format (it usually becomes the squash-merge
|
|
72
|
+
subject).
|
|
73
|
+
- PR **body** covers: context/why, what changed, how it was tested, and linked issues
|
|
74
|
+
(`Closes #N`).
|
|
75
|
+
- CI must be green. Run the full check suite (see above) before pushing.
|
|
76
|
+
- Add or update tests for behavioural changes; update docs in the same PR.
|
|
77
|
+
|
|
78
|
+
## AI-assisted contributions
|
|
79
|
+
|
|
80
|
+
AI coding assistants are **welcome** here. Many of this project's own changes are
|
|
81
|
+
agent-assisted. The bar is the same as for any contribution, and a few expectations keep it
|
|
82
|
+
useful rather than noisy:
|
|
83
|
+
|
|
84
|
+
- **You are the author of record.** By submitting, you certify you understand, have
|
|
85
|
+
reviewed, and have tested the change: you can explain and stand behind every line,
|
|
86
|
+
whoever (or whatever) drafted it. Signing off (`git commit -s`, see below) makes that
|
|
87
|
+
explicit.
|
|
88
|
+
- **Same quality bar.** The full check suite must pass, behavioural changes need tests, and
|
|
89
|
+
the [anti-slop guidance in `AGENTS.md`](AGENTS.md) applies: smallest change that solves
|
|
90
|
+
the problem, no drive-by churn, comments that explain *why*. Unreviewed or bulk
|
|
91
|
+
machine-generated PRs (mass "fix-up" sweeps, output you haven't read) will be closed.
|
|
92
|
+
- **Disclosure is encouraged, not required.** If an assistant did meaningful work, note it
|
|
93
|
+
with a provenance trailer so reviewers know where to look: `Assisted-by:` when a human
|
|
94
|
+
authored with help, `Generated-by:` when a tool produced essentially all of a change
|
|
95
|
+
(e.g. `Assisted-by: <tool/model>`). The trailer names a *tool*, never an author: an
|
|
96
|
+
assistant is never listed as `Signed-off-by:` or `Co-authored-by:`. The DCO sign-off and
|
|
97
|
+
authorship are yours alone, and the change is attributed to you. (Don't name the assistant
|
|
98
|
+
anywhere else in the commit or PR text.)
|
|
99
|
+
- **Agent-instruction files are security-sensitive.** Changes to `AGENTS.md`, `CLAUDE.md`,
|
|
100
|
+
`.claude/**`, and the CI/release workflows are reviewed with extra scrutiny. Coding agents
|
|
101
|
+
treat instruction files as trusted context and a compromised workflow could leak secrets,
|
|
102
|
+
so both are an injection / supply-chain surface. See [`SECURITY.md`](SECURITY.md).
|
|
103
|
+
|
|
104
|
+
Working through an agent? [`AGENTS.md`](AGENTS.md) is the canonical guide (tool-specific
|
|
105
|
+
files point to it), and reusable workflows live in [`.claude/skills/`](.claude/skills/).
|
|
106
|
+
|
|
107
|
+
## Licensing & sign-off
|
|
108
|
+
|
|
109
|
+
By contributing you agree your work is licensed under Apache-2.0. DCO-style sign-off is
|
|
110
|
+
welcome: `git commit -s`.
|
|
111
|
+
|
|
112
|
+
## Design
|
|
113
|
+
|
|
114
|
+
Read [`docs/requirements.md`](docs/requirements.md) before proposing behavioural changes.
|
|
115
|
+
Several decisions (no fail-open, subject never from message content, ALLOW-only caching)
|
|
116
|
+
are deliberate security invariants, not oversights.
|
apparitor-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
apparitor-0.1.1/NOTICE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
apparitor
|
|
2
|
+
Copyright 2026 The apparitor authors
|
|
3
|
+
|
|
4
|
+
This product is licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this product except in compliance with the License. You may obtain
|
|
6
|
+
a copy of the License in the LICENSE file or at:
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|