Repository navigation
docs: establish canonical detector and assurance architecture #912
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
seonghobae
merged 28 commits into
develop
from
docs/product-detection-architecture-2026-08-09
Aug 12, 2026
Merged
Changes from 18 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
43aa786
docs: add canonical AppGuardrail documentation map
seonghobae aadff91
docs: add canonical AppGuardrail PRD
seonghobae ab79dd2
docs: add canonical AppGuardrail TRD
seonghobae 7d5997e
docs: add canonical AppGuardrail architecture
seonghobae 438d2dd
docs: add AppGuardrail UML views
seonghobae 90119ea
docs: add AppGuardrail data model ERD
seonghobae 9979bab
docs: add AppGuardrail threat model
seonghobae e6edbf7
docs: add detector validation strategy
seonghobae 5e99c2a
docs: add AppGuardrail operability guide
seonghobae fad9793
docs: add detector and issue traceability
seonghobae f09678c
docs: add AppGuardrail ADR index
seonghobae e1effca
docs: record detector truth ADR
seonghobae 973fb55
docs: separate prevention and detection ADR
seonghobae 877c5f1
docs: record external engine provenance ADR
seonghobae 73a67ed
docs: record tenant and egress boundaries ADR
seonghobae faed46d
docs: record remediation authority ADR
seonghobae b9b37e6
docs: record automation authority ADR
seonghobae 1ccfb44
test: enforce canonical product and detector docs
seonghobae 031062c
docs: bind detector and workflow evidence provenance
seonghobae d045100
docs: make webhook delivery semantics fail-closed
seonghobae bff713c
docs: correct detector maturity and webhook delivery flow
seonghobae c302af5
test: harden canonical detector documentation contracts
seonghobae bfccf5d
docs: restore structural detector truth contract
seonghobae 3afb015
docs: bind canonical evidence trust
seonghobae 96a16f3
docs: pin webhook connection policy
seonghobae 99ecd9a
test: lock evidence and maturity contracts
seonghobae 382cae1
docs: reconcile SSRF assurance after PR #910 integration
seonghobae 4c634dd
test: bind maturity assertions to each SSRF claim
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| # AppGuardrail Architecture | ||
|
|
||
| **Status:** Accepted as-built/target architecture with maturity labels | ||
| **Last reviewed:** 2026-08-09 | ||
|
|
||
| ## Architectural goal | ||
|
|
||
| AppGuardrail converts application/security evidence into deterministic findings, reviewable remediation, continuous policy gates, and longitudinal assurance without conflating optional external scanners, issue metadata, or historical coordination with executable detection truth. | ||
|
|
||
| ## Component view | ||
|
|
||
| ```mermaid | ||
| flowchart LR | ||
| TARGET[Untrusted target repository/app] | ||
| DISC[Discovery/normalization] | ||
| BUILTIN[Built-in detector engine] | ||
| EXT[Optional external engines] | ||
| FIND[Normalized findings] | ||
| GATE[Deploy gate] | ||
| FIX[Safe fix / fix-pack] | ||
| SARIF[SARIF / reports / SBOM] | ||
| CP[Control plane] | ||
| DASH[Dashboard / buyer evidence] | ||
| ISSUE[Issue-to-detection audit] | ||
|
|
||
| TARGET --> DISC | ||
| DISC --> BUILTIN | ||
| TARGET --> EXT | ||
| BUILTIN --> FIND | ||
| EXT --> FIND | ||
| FIND --> GATE | ||
| FIND --> FIX | ||
| FIND --> SARIF | ||
| FIND --> CP | ||
| CP --> DASH | ||
| ISSUE --> BUILTIN | ||
| ISSUE --> EXT | ||
| ``` | ||
|
|
||
| ## Detector authority | ||
|
|
||
| The detector that observes evidence is authoritative for its finding. `scanner/rules/*.yml` is not automatically executable in full: supported `pattern-regex` entries can be evaluated by the lightweight engine, while Semgrep-style structural `pattern:` fixtures remain non-executable by the built-in matcher unless explicitly routed to a working structural engine. | ||
|
|
||
| External engines retain their own engine/rule/version provenance. AppGuardrail normalizes their output but does not claim their analysis was performed internally. | ||
|
|
||
| ## Issue-to-detection boundary | ||
|
|
||
| ```mermaid | ||
| flowchart LR | ||
| HIST[Independent issue/claim inventory] | ||
| REG[Detection obligation registry] | ||
| ADAPT[Detector-family adapter] | ||
| DET[Actual detector] | ||
| EV[Closed evidence fixture or authenticated workflow result] | ||
| RES[pass/fail/inconclusive obligation result] | ||
|
|
||
| HIST --> REG | ||
| REG --> ADAPT | ||
| EV --> ADAPT | ||
| ADAPT --> DET | ||
| DET --> RES | ||
| ``` | ||
|
|
||
| A registry maps requirement identity to executable detector family; it cannot assert the detector answer. PR #911 is active-PR implementation of this contract. | ||
|
|
||
| ## SSRF architecture | ||
|
|
||
| ```mermaid | ||
| flowchart LR | ||
| INPUT[User-controlled URL] | ||
| VALID[Destination validation] | ||
| STORE[(Stored webhook/callback config)] | ||
| EXEC[Outbound executor] | ||
| DNS[DNS/IP/redirect checks] | ||
| NET[Network request] | ||
|
|
||
| INPUT --> VALID | ||
| VALID --> STORE | ||
| STORE --> EXEC | ||
| EXEC --> DNS | ||
| DNS --> NET | ||
| ``` | ||
|
|
||
| Stored SSRF prevention and scanner detection are separate. PR #910 adds a prevention-side write-path check; a scanner rule must independently detect vulnerable validation/store/execute patterns before AppGuardrail claims automatic detection coverage. | ||
|
|
||
| ## Control-plane boundary | ||
|
|
||
| Current standalone control plane is stdlib HTTP + SQLite, with tenant API-key roles and scan/history/drift/webhook configuration. Persistent organization identity is resolved from authenticated key context, not untrusted payload strings. Enterprise replacement of SQLite is behind stable repository service functions and requires migrations/authz/recovery evidence. | ||
|
|
||
| ## Remediation authority | ||
|
|
||
| Autofix can perform only narrowly proven semantics-preserving transformations. Other fixes are guidance for a user/agent and become accepted only after rescanning/reverification. Model-generated remediation is never a substitute for scanner evidence. | ||
|
|
||
| ## Automation authority | ||
|
|
||
| ```mermaid | ||
| flowchart LR | ||
| DEV[Autonomous developer] | ||
| VERIFY[Tests/security exact-head evidence] | ||
| REVIEW[Independent review agents/humans] | ||
| MERGE[Protected merge] | ||
| RELEASE[Release environment] | ||
|
|
||
| DEV --> VERIFY | ||
| VERIFY --> REVIEW | ||
| REVIEW --> MERGE | ||
| MERGE --> RELEASE | ||
| ``` | ||
|
|
||
| The development model does not own qualifying approval, protected merge, release, or reviewer credentials. Scheduler blocks are RCA inputs; one blocked PR does not idle unrelated safe work. | ||
|
|
||
| ## Deployment modes | ||
|
|
||
| 1. **CLI/library:** one-shot local scan/report/SBOM/fix. | ||
| 2. **CI monitor:** installed GitHub workflow generating findings/SARIF and optional control-plane push. | ||
| 3. **Control plane:** standalone multi-tenant scan history/drift/dashboard/webhook service. | ||
| 4. **Organization evidence:** read-only aggregation of repository/PR/action evidence for acquisition/security diligence. | ||
|
|
||
| These modes share normalized contracts but can operate separately. | ||
|
|
||
| ## Change control | ||
|
|
||
| A new detector engine, persistent schema, tenant authority, arbitrary autofix class, outbound target policy, issue-audit semantics, or automation credential boundary requires ADR and synchronized technical/security/test documentation. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # AppGuardrail Documentation Map | ||
|
|
||
| AppGuardrail has extensive rule, scanner, report, release, issue, and scheduler documentation. This index establishes the cross-cutting product and architecture graph so buyers and maintainers do not have to reconstruct the product from README, workflows, issue bodies, and feature-specific notes. | ||
|
|
||
| | Area | Canonical document | | ||
| |---|---| | ||
| | Product requirements | [`docs/PRD.md`](docs/PRD.md) | | ||
| | Technical requirements | [`docs/TRD.md`](docs/TRD.md) | | ||
| | Architecture | [`ARCHITECTURE.md`](ARCHITECTURE.md) | | ||
| | UML/runtime/detection flows | [`docs/UML.md`](docs/UML.md) | | ||
| | Logical/physical data model | [`docs/ERD.md`](docs/ERD.md) | | ||
| | Threat model | [`docs/THREAT_MODEL.md`](docs/THREAT_MODEL.md) | | ||
| | Test and detector-validation strategy | [`docs/TEST_STRATEGY.md`](docs/TEST_STRATEGY.md) | | ||
| | Operability/recovery/release | [`docs/OPERABILITY.md`](docs/OPERABILITY.md) | | ||
| | Detection/issue/evidence traceability | [`docs/TRACEABILITY.md`](docs/TRACEABILITY.md) | | ||
| | Architecture decisions | [`docs/adr/README.md`](docs/adr/README.md) | | ||
| | Security reporting | [`SECURITY.md`](SECURITY.md) | | ||
| | Release automation | [`docs/release-automation.md`](docs/release-automation.md) | | ||
| | Productization roadmap | [`docs/product/2026-07-02-2b-krw-sale-readiness-plan.md`](docs/product/2026-07-02-2b-krw-sale-readiness-plan.md) | | ||
| | Agent development rules | [`AGENTS.md`](AGENTS.md) | | ||
| | Agent context | [`CLAUDE.md`](CLAUDE.md) | | ||
| | Product overview | [`README.md`](README.md) | | ||
| | Change history | [`CHANGELOG.md`](CHANGELOG.md) | | ||
|
|
||
| ## Maturity vocabulary | ||
|
|
||
| - **implemented-main** — source and tests exist on protected `develop`. | ||
| - **active-PR** — implementation/evidence exists only on an open pull request. | ||
| - **planned** — accepted product target without executable detector/control yet. | ||
| - **external-engine** — capability delegated to an optional scanner such as Semgrep/Trivy/Bandit/ZAP rather than AppGuardrail's lightweight built-in matcher. | ||
| - **evidence-only** — information visible in reports/history but not yet executable as an AppGuardrail detector. | ||
|
|
||
| Critical current distinction: issue #911's no-exclusions issue-to-detector registry and executable obligation coverage are **active-PR**, not protected-branch behavior. Stored-webhook SSRF prevention in PR #910 is also not equivalent to AppGuardrail automatically detecting the unsafe stored-SSRF coding pattern; prevention and scanner detection are separate requirements. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # AppGuardrail Logical and Persistence ERD | ||
|
|
||
| **Status:** Accepted cross-cutting data model; active-PR detector-obligation entities are labelled. | ||
| **Last reviewed:** 2026-08-09 | ||
|
|
||
| Current control-plane persistence is SQLite behind repository service functions. The scanner itself is primarily filesystem/in-memory and emits normalized finding envelopes. This ERD distinguishes current persistent scan history from active-PR/planned detection-obligation evidence. | ||
|
|
||
| ## Current control-plane model | ||
|
|
||
| ```mermaid | ||
| erDiagram | ||
| ORGANIZATION_RECORD ||--o{ API_KEY_RECORD : authorizes | ||
| ORGANIZATION_RECORD ||--o{ SCAN_RECORD : owns | ||
| SCAN_RECORD ||--o{ FINDING_RECORD : contains | ||
| ORGANIZATION_RECORD ||--o| WEBHOOK_CONFIG : configures | ||
| SCAN_RECORD ||--o{ DRIFT_RECORD : compares | ||
|
|
||
| ORGANIZATION_RECORD { | ||
| string organization_id PK | ||
| string organization_name | ||
| datetime created_at | ||
| } | ||
|
|
||
| API_KEY_RECORD { | ||
| string api_key_id PK | ||
| string organization_id FK | ||
| string key_digest | ||
| string role_code | ||
| datetime created_at | ||
| datetime revoked_at | ||
| } | ||
|
|
||
| SCAN_RECORD { | ||
| string scan_id PK | ||
| string organization_id FK | ||
| string repository_name | ||
| string commit_sha | ||
| integer blocking_finding_count | ||
| string schema_version | ||
| datetime created_at | ||
| } | ||
|
|
||
| FINDING_RECORD { | ||
| string finding_id PK | ||
| string scan_id FK | ||
| string rule_id | ||
| string engine_code | ||
| string severity_code | ||
| string category_code | ||
| string file_path | ||
| integer line_number | ||
| string bounded_metadata_json | ||
| } | ||
|
|
||
| DRIFT_RECORD { | ||
| string drift_record_id PK | ||
| string scan_id FK | ||
| string previous_scan_id | ||
| integer new_blocker_count | ||
| string calculation_version | ||
| } | ||
|
|
||
| WEBHOOK_CONFIG { | ||
| string webhook_config_id PK | ||
| string organization_id FK | ||
| string normalized_destination | ||
| string destination_policy_version | ||
| datetime updated_at | ||
| } | ||
| ``` | ||
|
|
||
| Exact physical SQLite table/column names may differ; migrations/source remain authoritative. Persistent object naming should converge on descriptive two-or-more-word `snake_case` when schema changes occur. | ||
|
|
||
| ## Detection obligation model — PR #911 active target | ||
|
|
||
| ```mermaid | ||
| erDiagram | ||
| ISSUE_CLAIM ||--o{ DETECTION_OBLIGATION : maps_to | ||
| DETECTOR_FAMILY ||--o{ DETECTION_OBLIGATION : satisfies | ||
| DETECTION_OBLIGATION ||--o{ DETECTOR_EVIDENCE_CASE : evaluated_by | ||
| DETECTOR_EVIDENCE_CASE ||--o{ OBLIGATION_RESULT : produces | ||
| WORKFLOW_EVIDENCE ||--o{ DETECTOR_EVIDENCE_CASE : authenticates | ||
|
|
||
| ISSUE_CLAIM { | ||
| integer issue_number | ||
| string claim_identifier | ||
| string issue_state_code | ||
| string source_digest | ||
| } | ||
|
|
||
| DETECTOR_FAMILY { | ||
| string detector_family_id | ||
| string execution_owner_code | ||
| string detector_version | ||
| string capability_status_code | ||
| } | ||
|
|
||
| DETECTION_OBLIGATION { | ||
| string obligation_id | ||
| integer issue_number | ||
| string claim_identifier | ||
| string detector_family_id | ||
| string detectability_code | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
|
|
||
| DETECTOR_EVIDENCE_CASE { | ||
| string evidence_case_id | ||
| string obligation_id | ||
| string evidence_type_code | ||
| string evidence_digest | ||
| string provenance_status_code | ||
| } | ||
|
|
||
| OBLIGATION_RESULT { | ||
| string obligation_result_id | ||
| string evidence_case_id | ||
| string result_code | ||
| string detector_rule_id | ||
| string finding_digest | ||
| } | ||
|
|
||
| WORKFLOW_EVIDENCE { | ||
| string workflow_evidence_id | ||
| string repository_full_name | ||
| string workflow_name | ||
| string job_name | ||
| string head_sha | ||
| integer run_id | ||
| integer run_attempt | ||
| string conclusion_code | ||
| string evidence_digest | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| ``` | ||
|
|
||
| This second model is an **active-PR logical contract**, not a protected-develop persisted schema. PR #911 may use committed JSON/registry/fixtures rather than these as database tables. | ||
|
|
||
| ## Identity and tenancy invariants | ||
|
|
||
| - API key identity resolves organization authority; repository/org strings inside scan payloads cannot elevate access. | ||
| - Finding IDs, scan IDs, issue numbers, rule IDs, and GitHub run IDs are evidence identities, not authorization identities. | ||
| - Webhook destination strings are protected network destinations and must pass policy before persistence/execution. | ||
| - Raw secrets discovered in target code are not copied into durable findings; retain rule/location/fingerprint or bounded redacted evidence instead. | ||
|
|
||
| ## Finding provenance | ||
|
|
||
| ```mermaid | ||
| flowchart LR | ||
| SRC[Target source/config] | ||
| ENG[Built-in or external engine] | ||
| FIND[Normalized finding] | ||
| SCAN[Scan envelope] | ||
| SARIF[SARIF/report/control plane] | ||
|
|
||
| SRC --> ENG | ||
| ENG --> FIND | ||
| FIND --> SCAN | ||
| SCAN --> SARIF | ||
| ``` | ||
|
|
||
| A finding retains engine/rule/provenance across transformations. AppGuardrail must not erase the distinction between built-in and external-engine evidence. | ||
|
|
||
| ## Schema evolution rule | ||
|
|
||
| A future managed PostgreSQL control plane requires explicit migration/rollback, tenant authorization/RLS where used, idempotency/concurrency, webhook/egress security, backup/recovery, retention/deletion, and cross-tenant tests. Conceptual issue-obligation entities become persistent only through such a reviewed migration, not merely by being drawn here. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.