Repository navigation
fix: count each temporary token link once and correct the repeated-Authorization warning - #811
Conversation
The generated token's hard-link check compares the link count with the distinct temporary link names the scan found, plus one, rather than the number of scan results. A listing that repeats an entry can no longer make a foreign hard link look like a second temporary link. The bounded-rescan test now asserts the exact number of scans, and the docstring no longer says a concurrent start may be adding its link.
When the first Authorization value is empty, x-api-key decides, so the repeated-Authorization warning no longer claims the gateway used the first value. It says the gateway ignored the later Authorization values, which holds whichever header decided. The warning test now also covers the 403 refusal when no admin token is configured, and missing, unreadable and conflicting x-api-key requests, each checking that the refusal neither logs the warning nor spends it.
Independent adversarial review, run 1 (agent-generated)This change touches token authentication in the server and the gateway, so it follows the authentication and authorization review gate. Review record
Reviewer prompt, verbatimThe agent was started with this prompt: The brief file, verbatim: Reviewer report, uneditedAgent-generated adversarial review Reviewed head and range
Abuse cases considered
Test evidence
Findings
Probe areas that held (no findings)
Blocking findings: 0 |
|
The non-blocking findings 1 to 5 from the review above are fixed in #818, which gets its own independent adversarial review before it merges. |
* fix(gateway): warn when only a later Authorization value carries a token A request whose first Authorization value is empty or blank, whose later value carries a token, and that also sends x-api-key authenticated with x-api-key without the repeated-Authorization warning, and so fell outside the refusal planned for 0.13.0. Count that shape as a repeat the gateway ignores, so it logs the warning once. Which header authenticates is unchanged. The middleware tests now send an x-api-key that differs from every Authorization value and check the masked token id, so a passing request shows which header decided. * fix(server): recount the token's links after removing temporary links A temporary link renamed during the directory scan, or added before and removed after the listing, could balance the link count and let a token with another hard link be accepted. After removing the temporary links the token must now have one link; the balanced count decides only when a read-only directory keeps every temporary link in place. Each temporary name is removed only if it still holds the token. On macOS a token directory with mode 0700 and an access control list passed the mode check, although the list can let other accounts change the directory. Such a directory is now refused with a chmod -N hint. * docs: describe which Authorization value the gateway reads The first Authorization value decides when it carries a token; otherwise x-api-key decides, and later Authorization values are ignored. The documents said a repeated Authorization header uses its first value, or that Authorization is used whenever both headers are sent. * docs: say both headers must carry a token for Authorization to win in the coding-agent example * fix(server): check the token file's access control list and refuse only allow entries The macOS access control list check now runs on the open directory and on the open token file, through acl_get_fd_np, so a token file that carries an allow entry (inherited from its directory, or added to it) is refused even after the directory is cleared. Only entries that are not deny entries are refused: deny entries, such as the one macOS puts on the home folder, take access away. An entry whose kind cannot be read, and a list that cannot be read, are still refused. The refusal names the path to fix, and for the directory it suggests removing the allow entries or using a directory of its own instead of clearing every entry. New tests pin the read-only exception of the link check: a temporary link that a writable directory cannot remove (EPERM), a read-only directory where one link fails with another error, and a link added elsewhere while a read-only directory is cleaned up. * docs: say when x-api-key decides and which first Authorization values are refused x-api-key decides only when the first Authorization value is empty or blank. An unreadable first value, or Bearer without a token, is refused even with a valid x-api-key. * docs: say a bare Bearer value counts as the token Bearer The gateway reads Authorization: Bearer with no token as the raw token Bearer, as it always has, so x-api-key does not decide for it and the request is refused unless Bearer itself is a configured token. The documents said such a value is refused, which is not exact. A test now pins that neither an unreadable first value nor a bare Bearer lets x-api-key decide. * docs: say the repeated-Authorization warning is logged only for authenticated requests The gateway logs the warning, once per process, only for a request that passes authentication; a refused request gets 401 without it. SECURITY.md, the Helm README, the gateway README, the architecture guide and the 0.13.0 table now say so, as the COMPATIBILITY.md item already did.
Summary
Follow-ups to #760 from its independent review (#760 (comment)). All four findings were non-blocking.
1. Each temporary token link counts once (
packages/sie_server/src/sie_server/core/server_auth.py,_drop_temporary_links)auth-token, a foreign link and one staged link look like the token plus two temporary links, so the token was accepted with the foreign link in place.len({entry.name for entry in linked}) + 1.2. Test precision (
packages/sie_server/tests/core/test_server_auth.py)test_a_link_count_that_keeps_changing_is_refused_after_a_few_scansassertslen(scans) == server_auth._LINK_SCAN_ATTEMPTSinstead of a range._drop_temporary_linksdocstring no longer says a concurrent start may be adding its link. A start never adds a link to a token that a reader already has open.3. Warning text (
packages/sie_gateway/src/middleware/auth.rs)Authorizationwarning said "The gateway used the first value". When the firstAuthorizationvalue is empty,x-api-keydecides, so that was wrong.authorization_values_disagreedocstring says the same.4. Warning-test cases (
middleware_warns_only_for_a_repeat_that_passes_authentication)x-api-keyrequests. Each sendsAuthorizationvalues that disagree.x-api-keycases pass with an empty firstAuthorizationvalue, the case in item 3.New test
TestPersistedToken::test_a_temporary_link_listed_twice_by_the_scan_counts_once, with the scan yielding each entry twice:[with-foreign-link]: refused, and the foreign and staged links stay.[temporary-only]: accepted, and the staged link is removed.Mutation checks
Each mutation was applied on its own and caught:
len(linked) + 1: both new parameters fail ([with-foreign-link]with DID NOT RAISE,[temporary-only]with a refusal);_LINK_SCAN_ATTEMPTS + 1or_LINK_SCAN_ATTEMPTS - 1times: the scan-count test fails;Validation
mise run lintandmise run typecheck: clean.mise run test -- packages/sie_server/tests/core/test_server_auth.py: 153 passed.mise run test -- packages/sie_server/tests/test_cli_auth.py: 58 passed.mise run gateway-fmt -- --check,mise run gateway-checkandmise run gateway-clippy: clean.mise run gateway-test, default andcloud-storagefeatures, with NATS: all passed.Review
Items 1 and 3 change authentication code, so this gets an independent adversarial review before merge.
Summary by CodeRabbit
Authorizationheaders now receive a warning that accurately describes how later values are handled.