Skip to content
Open
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
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,41 @@ jobs:
- run: npm ci
- run: npm run build:ui
- run: npm test

review-consent:
name: Review consent transactions
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx tsc --noEmit
- name: Start disposable MongoDB replica set
run: |
docker run -d --name review-consent-test --network host --memory=512m --cpus=1 --tmpfs /data/db:rw,noexec,nosuid,size=256m mongo:7.0@sha256:0e145625e78b94224d16222ff2609c4621ff6e2c390300e4e6bf698305596792 mongod --bind_ip 127.0.0.1 --port 28743 --replSet review-consent-test --oplogSize 16
for attempt in $(seq 1 40); do
if docker exec review-consent-test mongosh --quiet --port 28743 --eval 'db.adminCommand({ping:1})' > /dev/null 2>&1; then break; fi
sleep 1
done
docker exec review-consent-test mongosh --quiet --port 28743 --eval 'rs.initiate({_id:"review-consent-test",members:[{_id:0,host:"127.0.0.1:28743"}]})'
for attempt in $(seq 1 40); do
if docker exec review-consent-test mongosh --quiet --port 28743 --eval 'if (!db.hello().isWritablePrimary) quit(1)' > /dev/null 2>&1; then exit 0; fi
sleep 1
done
exit 1
- name: Verify consent authority, concurrency and replay cost
env:
TEST_REVIEW_CONSENT_MONGO: mongodb://127.0.0.1:28743/?replicaSet=review-consent-test
TEST_REVIEW_CONSENT_PERF_REPORT: review-consent-perf.json
run: npx mocha test/review-owner-consent.test.js
- uses: actions/upload-artifact@v4
with:
name: review-consent-performance
path: review-consent-perf.json
- name: Remove disposable database
if: always()
run: docker rm -f review-consent-test || true
23 changes: 23 additions & 0 deletions docs/review-owner-consent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Review owner consent backend

`createReviewOwnerConsent` prepares a signed policy preview from the authenticated review intent consumer and records explicit access/retention acceptance in MongoDB. It is an unwired backend. Browser routes, CSRF protection, UI, completion-code exchange and activation remain unfinished. Capabilities continue to advertise no available integration features.

The caller must provide an account ID and session ID derived from a current authenticated browser session. Never populate this principal from request JSON or treat a service credential as an owner's identity. The factory also needs the configured intent consumer, a dedicated random 32-byte signing key held outside Git, and a connected MongoDB replica set. Configure bounded server-selection and socket timeouts on that connection. Tests use MongoDB 7 with a three-second server-selection timeout and ten-second socket timeout.

Preview checks the current account and repository before consuming the review intent, then checks them again afterward. The network request holds no MongoDB transaction or authority lock. The account must be active or have the legacy unset active status; the repository must be ready and unexpired. Ownership uses the current account ObjectId. Coauthors must match the account's current numeric GitHub ID. Legacy username-only coauthors and unrelated administrators cannot provide consent.

The signed preview binds the saved intent and exact policy to the account, hashed session ID and repository ObjectId. Changing the account, session, repository or policy invalidates confirmation. The signing context is specific to this protocol; key rotation invalidates outstanding previews. Confirmation requires both access and retention acceptance and an explicit 32-hex request ID. Preview and replay expire with the intent or retention policy.

Confirmation writes one immutable `review_owner_consents` record per client/intent. It records the account, repository, authority role, exact policy, request ID, confirmation time and a hash of the signed preview. It stores no raw session ID, intent token, service credential or preview ticket. Explicit retries return the same receipt only after current authority checks; another request ID or repository conflicts. No binding is created by accepting a policy.

Transactions touch the current account and repository before writing or reading the receipt. Ownership transfer, coauthor removal and account disablement therefore serialize with confirmation. The private `reviewConsentRevision` fields are excluded from ordinary Mongoose reads. Four transactions may be active per factory. Database commands and commits have limits; transactions are not automatically retried. A conflict or uncertain commit returns a fixed `unavailable` error, and the caller must retain the same ticket and request ID for explicit recovery.

The final binding flow must still revalidate the initiating review session and saved intent scope with the review service. This backend proves current upstream owner/coauthor consent, not continuing review-side author authority, immutable snapshots, restricted file access or preservation. Receipt retention/export and the HTTP adapter remain separate work; this change enables no provider or public route.

## Validation

The opt-in suite runs against a disposable MongoDB replica set. It covers literal acceptance, immutable replay, stable-ID coauthors, administrator/username rejection, ticket tampering, account/session binding, disabled accounts, ownership changes, removed coauthors, archived/expired repositories, permission changes during upstream I/O, concurrent confirmations, expiry, second-repository conflicts and authority-field privacy. A held ownership-change transaction forces a real write conflict; the explicit retry is denied and no receipt is created.

Run `TEST_REVIEW_CONSENT_MONGO='mongodb://127.0.0.1:28743/?replicaSet=review-consent-test' npx mocha test/review-owner-consent.test.js`. Set `TEST_REVIEW_CONSENT_PERF_REPORT=/tmp/review-consent-perf.json` to include 100 durable receipt replays. The CI job starts and removes its own pinned MongoDB image and preserves the performance report. Local timings are synthetic and are not a production latency guarantee.

Local validation passes 12 targeted cases with the benchmark enabled, 723 full-suite cases with 51 opt-in/environment-dependent cases pending, the full TypeScript check and targeted lint. The final 100-replay run measured mean 12.85 ms, median 12.82 ms and p95 14.89 ms against a one-CPU, 512 MiB disposable database. An earlier performance attempt encountered a database conflict while the privacy fixture automatically built indexes; the final fixture disables automatic schema/index creation and creates its collections explicitly.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { repositoryAccessSchema } from "../repository-access.schema";
import { Schema } from "mongoose";

const AnonymizedRepositorySchema = new Schema({
reviewConsentRevision: { type: Number, select: false },
repoId: {
type: String,
index: { unique: true, collation: { locale: "en", strength: 2 } },
Expand Down
1 change: 1 addition & 0 deletions src/core/model/users/users.schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Schema } from "mongoose";

const UserSchema = new Schema({
reviewConsentRevision: { type: Number, select: false },
accessTokens: {
github: { type: String, select: false },
},
Expand Down
Loading
Loading