DEV Community

Discussion on: A frozen field with one witness is still a claim

Collapse
 
williamchiu profile image
weiche chiu •

The issuance record is the witness I was missing, and the join is the right shape for it. Before replying I went to check where a jti would go, and found something worse than the limit I published.

On the HTTP path there is no token to take a jti from. authenticated_actor comes from an X-AINE-Actor header that nothing verifies, so the caller writes both fields. A caller who lies sends the same lie in the header and in the payload, and the row shows no mismatch. The post said that field comes from the authentication layer. On this path there isn't one, and I've added a correction to the post.

Your static-key limit applies here in a stronger form. A static key at least proves the caller holds the key. A header proves nothing, so every HTTP row today is single-witness.

Your point about readers holds too, with one detail. Every route that writes these fields rejects a request without the header, so on HTTP authenticated_actor is never null. A daily null count over that traffic would read 0 and look healthy.

None of this is fixed yet. I opened an issue for it (github.com/williamlabdev/aine-cont...). The first step labels where each actor value came from. The second adds the daily mismatch and null counts with a named owner. Your join comes last, because it needs real token verification before there is a jti to store.

Collapse
 
_firelinks profile image
Mike Dabydeen •

I'd move approvals ahead of the labelling step in issue #8, because the same header decides who may approve, not only who gets recorded. _context() also reads X-AINE-Roles from the caller. decide_approval checks that role against required_roles, and an approval passes once the count of distinct actor_id values reaches required_approvals. So one caller can post two approve decisions under two X-AINE-Actor names, both sending X-AINE-Roles: approver, and clear required_approvals: 2. I also couldn't find a check that the deciding actor differs from requested_by, so a single required approval can be the requester's own.

The 127.0.0.1 default bind keeps this local today, which is probably why it hasn't mattered yet. It stops being local the first time someone puts the server behind a proxy so a team can share it.

Until token verification lands, I'd record decisions whose actor came from the header but not count them toward required_approvals. The test: create an approval with required_approvals 2, post two approve decisions over HTTP with different X-AINE-Actor values and the approver role, and expect it not to reach approved. A requester-cannot-approve check can ship next to it, since that one needs no token.

Thread Thread
 
williamchiu profile image
weiche chiu • • Edited

You were right to put approvals first, and it was worse than the role. decide_approval took roles from X-AINE-Roles, so a caller could name itself approver. The quorum counts distinct actor ids, and those come from X-AINE-Actor, so one caller could fill a quorum of two by sending two names.

Both are closed in PR #10 (github.com/williamlabdev/aine-cont...). Approval decisions now refuse any actor whose source is missing or "header", and record nothing. A consumer that verifies identity sets source (for example token) and keeps working. The cost is that the reference server can no longer decide approvals on its own; it needs a verifying layer in front.

One thing I left open: decisions recorded before the fix don't store their source, so they still count. Thanks for pushing on the order.