docs(adr): add ADR 0068 for privacy allowlist on poll input drivers#3428
docs(adr): add ADR 0068 for privacy allowlist on poll input drivers#3428waynesun09 wants to merge 3 commits into
Conversation
ADR 0063 introduces jira-poll, the first dispatch input driver where the event source (an internal Jira project) and the dispatch target (potentially a public repo) do not share a trust boundary. The Jira poll adapter maps issue fields into NormalizedEvent unfiltered -- comment bodies, labels, and other content flow directly into agent-visible FULLSEND_WORK_ITEM_* env vars with no sanitization. Add a configurable privacy allowlist gate applied inside jira-poll, before NormalizedEvent construction: default-deny field allowlist, template-projected comment bodies, PII scanning as defense in depth, and a provenance hash for audit. Update the Jira poll adapter to reflect the gated mapping as the new default, cross-reference the threat model's existing output-side mitigations with this input-side complement, and note the decision in architecture.md. No jira-poll code exists yet (#2263 tracks implementation), so this lands as design input before the driver ships rather than a retrofit. Signed-off-by: Wayne Sun <gsun@redhat.com>
|
🤖 Review · |
PR Summary by Qododocs(adr): add ADR 0068 privacy allowlist gate for poll input drivers
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
Site previewPreview: https://8c7daca4-site.fullsend-ai.workers.dev Commit: |
Code Review by Qodo
1.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Two issues flagged by automated review: - normalized-event.schema.json's state object had additionalProperties: false with no privacy_gate property defined, so any event carrying state.privacy_gate.source_hash (as the adapter doc specified) would fail schema validation. Add privacy_gate as an optional object under state. - ADR 0068's default-deny allowed_fields design didn't account for state.labels being a required NormalizedEvent field -- a config that excludes labels from allowed_fields would produce a schema-invalid event. Resolve explicitly: state.labels is always emitted, set to an empty array when labels isn't allowlisted, documented in both the ADR and the Jira poll adapter. Signed-off-by: Wayne Sun <gsun@redhat.com>
|
Fixed both bugs flagged by review (commit a4c9033):
The third finding (ADR line count) was marked low-relevance/optional by the reviewer itself, citing prior precedent (ADR 0055 merged at 190 lines) — left as-is. |
|
🤖 Finished Review · ✅ Success · Started 10:19 PM UTC · Completed 10:28 PM UTC |
Review — ApproveRe-review after one new commit (b40f6b0) that addresses substantive design gaps identified by a four-agent review squad. The ADR and supporting documentation are now internally consistent and technically sound. Changes since prior review (a4c9033 → b40f6b0):
Prior findings disposition:
Correctness: ✅ All cross-file references verified consistent: default Security: ✅ No secrets, injection patterns, or permission changes. Fail-closed design confirmed (omitted/empty ADR conventions: ✅ Status Accepted on new file (correct per AGENTS.md), Schema backward compatibility: ✅ Adding optional PR title: ✅ Findings[low · docs-currency] Labels: PR adds a new ADR and modifies normative dispatch documentation and schema Previous runReview — ApproveWell-structured ADR that addresses a real privacy gap in the jira-poll input driver design. The default-deny allowlist, template-projected comments, PII scanning, and provenance hash form a coherent defense-in-depth approach. All cross-cutting documentation updates (architecture.md, security-threat-model.md, jira-poll-adapter.md, normalized-event.schema.json) are present and consistent. ADR conventions: ✅ Status Accepted on new file (correct per AGENTS.md), architecture.md updated, security-threat-model.md updated, annotation comment matches corpus pattern. Schema backward compatibility: ✅ Adding optional Security design: ✅ Default-deny with safe fallback when Findings[low · correctness] [low · correctness] [low · docs-currency] |
Four-agent review (2x Claude, Gemini, Codex) surfaced substantive
design gaps beyond the earlier automated pass:
- comment_template slots were not constrained to allowed_fields,
letting the template mechanism re-introduce excluded content --
now a normative MUST, with config-time rejection.
- Substituted template values were not sanitized, allowing a crafted
Jira field to inject content indistinguishable from template
structure -- now a normative MUST.
- allowed_fields defaults (summary, issue_type, priority) don't
correspond to any direct NormalizedEvent schema field; only
state.labels does. Clarified allowed_fields governs Jira content
read for comment_template interpolation, not NormalizedEvent
output fields directly, and fixed the Context's incorrect claim
that issue title already flows into NormalizedEvent today.
- entity.url/entity.key are required-but-excludable under a literal
default-deny reading, same class as the already-fixed labels bug --
and they're the highest-signal internal-identity fields the ADR
exists to protect (Jira hostname, ticket key). Extended the
always-emitted carve-out and explicitly acknowledged this as an
accepted gap with rationale (both fields are load-bearing for
ADR 0063's poll cancel / lock-refresh resolution), rather than
leaving it unaddressed.
- Provenance hash claimed to let "sanitization be verified... without
retaining the original content" -- self-contradictory for a
one-way hash with nothing to recompute against. Reframed as a
correlation/tamper-evidence fingerprint, and added an RFC 8785
canonicalization requirement for cross-implementation
reproducibility.
- PII scan claims corrected: no existing fullsend component does PII
scanning today (SecretRedactor is secrets-only, verified against
internal/security/redactor.go); regex scanning cannot catch the
customer names/rationale/ticket-reference leakage the ADR is
motivated by, and that residual risk is now stated explicitly.
Specified the match action (redact in place).
- Schema fix: added required: ["source_hash"] inside privacy_gate so
an empty {} object can't validate.
- Minor: fixed a stale #2263 reference (closed design issue, not an
implementation epic), broadened "non-forge-native" scoping to the
actual trust-boundary condition, added conformance-test guidance
for the eventual jira-poll implementation, and clarified the
privacy_gate-present-but-allowed_fields-omitted default.
Mirrored all changes into jira-poll-adapter.md for cross-file
consistency.
Signed-off-by: Wayne Sun <gsun@redhat.com>
Review Squad Report — #3428Agents dispatched: 4 (claude-coder, claude-researcher, gemini-code-review, cursor-code-review) Critical (2)
High (4)
Medium (3)
Low (4)
Info (3)
False Positives RemovedNone — all reported findings were verified as real against the actual file contents before inclusion. Fixes applied (commit b40f6b0)
SummaryThe first review round (Qodo) caught schema-validity bugs; this round caught design-level security gaps in the mechanism itself — most notably that the privacy gate's own |
|
🤖 Finished Review · ✅ Success · Started 10:31 PM UTC · Completed 10:43 PM UTC |
Summary
Adds ADR 0068, proposing a configurable privacy allowlist gate on
jira-poll(ADR 0063) applied beforeNormalizedEventconstruction — sincejira-pollis the first dispatch input driver where the event source (an internal Jira project) and the dispatch target (potentially a public repo) do not share a trust boundary, and no sanitization exists in the current mapping.Related Issue
Relevant to the
fullsend pollimplementation epic (#2263) — this ADR is intended to land before that implementation starts, since nojira-pollcode exists yet.Changes
docs/ADRs/0068-privacy-allowlist-for-poll-input-drivers.md: default-deny field allowlist, template-projected comment bodies, PII scanning as defense in depth, and a provenance hash for audit — configured via a newprivacy_gateblock underpoll.input_drivers.docs/normative/normalized-event/v1/jira-poll-adapter.mdto document theprivacy_gateschema extension and reflect the gated mapping as the new default (previously unfiltered).docs/architecture.mdnoting the decision under the dispatch section.docs/problems/security-threat-model.md's existing "Indirect information disclosure" section, as the input-side complement to the existing output-sideSecretRedactor/ADR 0022 mitigation.Testing
make lintpasses (stage changes first, then run)Checklist
!for breaking changes)Assisted-by: Claude