Found in review of #370.
POST /api/v1/node/libvirt (lnvps_api/src/api/marketplace.rs:733-776) is authenticated with NodeAuth, which proves node identity and token version (lnvps_api_common/src/node_auth.rs:23-70) and says nothing about status.
Every other node-driven allocation does check: allocate_node_tunnel refuses a node that is not approved, with a test for it (a_node_that_is_not_approved_gets_no_data_plane). This one does not, so any registered node — pending, unreviewed, listing fee unpaid — can call store_libvirt_cert, which creates directories and rewrites files under the API host's pki_dir on every call. Registration is free and open (NIP-98, any key).
Fix: gate on the same approval state the tunnel path uses, and add the matching negative test.
Two smaller things in the same handler:
store_libvirt_cert does blocking fs IO inside an async handler.
- PEM validation (
:753-758) is prefix/suffix string matching only. A body of several concatenated PEM blocks passes, and every block becomes a trust anchor for that node. There is no X.509 parse, no basicConstraints: CA check, no SAN-vs-tunnel-address check, no expiry check and no length cap, despite the doc comment at :145-149 asserting the certificate is "self-signed and CA-capable". The impact is confined to that node's own directory — a node choosing what it serves gains nothing by lying — but the code claims properties nothing verifies.
Found in review of #370.
POST /api/v1/node/libvirt(lnvps_api/src/api/marketplace.rs:733-776) is authenticated withNodeAuth, which proves node identity and token version (lnvps_api_common/src/node_auth.rs:23-70) and says nothing aboutstatus.Every other node-driven allocation does check:
allocate_node_tunnelrefuses a node that is not approved, with a test for it (a_node_that_is_not_approved_gets_no_data_plane). This one does not, so any registered node — pending, unreviewed, listing fee unpaid — can callstore_libvirt_cert, which creates directories and rewrites files under the API host'spki_diron every call. Registration is free and open (NIP-98, any key).Fix: gate on the same approval state the tunnel path uses, and add the matching negative test.
Two smaller things in the same handler:
store_libvirt_certdoes blockingfsIO inside an async handler.:753-758) is prefix/suffix string matching only. A body of several concatenated PEM blocks passes, and every block becomes a trust anchor for that node. There is no X.509 parse, nobasicConstraints: CAcheck, no SAN-vs-tunnel-address check, no expiry check and no length cap, despite the doc comment at:145-149asserting the certificate is "self-signed and CA-capable". The impact is confined to that node's own directory — a node choosing what it serves gains nothing by lying — but the code claims properties nothing verifies.