<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Lykrin Security Research</title><subtitle>Source code review and security research for self-hosted software.</subtitle><link href="https://lykrin.github.io/"/><link rel="self" href="https://lykrin.github.io/feed.xml"/><id>https://lykrin.github.io/</id><updated>2026-09-30T00:00:00Z</updated><author><name>Lykrin</name><uri>https://github.com/Lykrin</uri></author>
<entry><title>SSH adapters and remote shell boundaries</title><link href="https://lykrin.github.io/research/vpn-remote-command-boundaries.html"/><id>https://lykrin.github.io/research/vpn-remote-command-boundaries.html</id><updated>2026-09-30T00:00:00Z</updated><category term="app"/><summary>Python → SSH → container → sh -c: avoiding a local shell does not stop the remote shells parsing. Found unquoted inner-shell operands and a validator that accepts a trailing newline; proposes per-argument quoting and full-string matching.</summary></entry>
<entry><title>BDO Companion: pipe authorization, UIAccess, and signing</title><link href="https://lykrin.github.io/research/bdo-companion-0-7-3-security.html"/><id>https://lykrin.github.io/research/bdo-companion-0-7-3-security.html</id><updated>2026-09-30T00:00:00Z</updated><category term="desktop"/><summary>BDO Companion 0.7.3 (Windows): static review of the overlay&#x27;s named-pipe security, its UIAccess manifest request, and embedded signing material versus verified trust. Nothing was executed.</summary></entry>
<entry><title>Glance configuration controls a privileged network client</title><link href="https://lykrin.github.io/research/glance-request-destination-authority.html"/><id>https://lykrin.github.io/research/glance-request-destination-authority.html</id><updated>2026-09-30T00:00:00Z</updated><category term="app"/><summary>Glance v0.8.6 SSRF: response-supplied URLs and same-host redirects let widget requests reach other services and routes. A loopback Go probe shows both; fixed identifiers and route policy close them.</summary></entry>
<entry><title>Where Glance API strings acquire markup authority</title><link href="https://lykrin.github.io/research/glance-active-content-boundary.html"/><id>https://lykrin.github.io/research/glance-active-content-boundary.html</id><updated>2026-09-30T00:00:00Z</updated><category term="app"/><summary>Glance v0.8.6 custom API widgets: nine experiments show where trusted-HTML and trusted-URL conversions turn API strings into active markup. Template diffs remove the unnecessary trust assertions.</summary></entry>
<entry><title>Closing an intake&#x27;s outbound trust-boundary gap</title><link href="https://lykrin.github.io/research/support-intake-egress-isolation.html"/><id>https://lykrin.github.io/research/support-intake-egress-isolation.html</id><updated>2026-09-26T00:00:00Z</updated><category term="infra"/><summary>nftables egress: a sandboxed intake guest with locked-down ingress could still open TCP connections to protected internal hosts. A default-drop output policy blocked them; health checks and proxy traffic kept working.</summary></entry>
<entry><title>Glance: TLS, redirects, and the browser boundary</title><link href="https://lykrin.github.io/research/dashboard-credential-transport.html"/><id>https://lykrin.github.io/research/dashboard-credential-transport.html</id><updated>2026-09-26T00:00:00Z</updated><category term="infra"/><summary>Glance v0.8.6: eight widget fetches had TLS certificate verification disabled. Removed all eight, then traced API credentials through redirects and templates with a loopback Go probe comparing Authorization and x-api-key handling.</summary></entry>
<entry><title>Authentik discovery is not a client&#x27;s grant allowlist</title><link href="https://lykrin.github.io/research/authentik-grant-policy.html"/><id>https://lykrin.github.io/research/authentik-grant-policy.html</id><updated>2026-09-26T00:00:00Z</updated><category term="identity"/><summary>Authentik 2026.8.3: OIDC discovery advertises a fixed grant list, but enforcement reads each provider&#x27;s stored config. A disabled flow can still be listed, and a supported one can be missing.</summary></entry>
<entry><title>A backup contract failed safely while its health display stayed green</title><link href="https://lykrin.github.io/research/backup-contract-observability.html"/><id>https://lykrin.github.io/research/backup-contract-observability.html</id><updated>2026-09-26T00:00:00Z</updated><category term="infra"/><summary>Inventory drift made a backup watchdog exit before checking capacity and sync freshness, while health stayed green. The encrypted copies existed; the detection path did not. Reconciliation restored monitoring.</summary></entry>
<entry><title>Encrypted backups need a key path that survives the outage</title><link href="https://lykrin.github.io/research/encrypted-recovery-bootstrap.html"/><id>https://lykrin.github.io/research/encrypted-recovery-bootstrap.html</id><updated>2026-09-26T00:00:00Z</updated><category term="infra"/><summary>Proxmox Backup Server client-side encryption: the documented protected key replica could not be retrieved. Recreated it, proved it matched the live key, and decrypted a real restore whose checksum matched the source.</summary></entry>
<entry><title>Native identity, local fallback, and narrow API exceptions</title><link href="https://lykrin.github.io/research/native-identity-and-api-exceptions.html"/><id>https://lykrin.github.io/research/native-identity-and-api-exceptions.html</id><updated>2026-09-26T00:00:00Z</updated><category term="identity"/><summary>Paperless-ngx and FreshRSS: native OIDC, local fallback login and API credentials are separate entry points. Disabling automatic social signup does not forbid social signup; API exceptions keep application authentication.</summary></entry>
<entry><title>Forgejo enrollment: OpenID is not OpenID Connect</title><link href="https://lykrin.github.io/research/forgejo-external-provisioning-policy.html"/><id>https://lykrin.github.io/research/forgejo-external-provisioning-policy.html</id><updated>2026-09-26T00:00:00Z</updated><category term="identity"/><summary>Forgejo v15.0.9: ENABLE_OPENID_SIGNUP governs legacy OpenID, not OIDC. Whether an external identity can create an account depends on separate OAuth2 settings, traced handler by handler.</summary></entry>
<entry><title>Hyprlock: separate authentication from reader cleanup</title><link href="https://lykrin.github.io/research/hyprlock-authentication-lifecycle.html"/><id>https://lykrin.github.io/research/hyprlock-authentication-lifecycle.html</id><updated>2026-09-20T00:00:00Z</updated><category term="desktop"/><summary>Hyprlock v0.9.6: blocking fprintd calls stalled the Wayland event loop, and a fingerprint unlock could log a failed PAM attempt. A local patch moves device work off the event loop without loosening authentication.</summary></entry>
<entry><title>A gateway restart without a general root-control API</title><link href="https://lykrin.github.io/research/authenticated-ingress-and-fixed-control.html"/><id>https://lykrin.github.io/research/authenticated-ingress-and-fixed-control.html</id><updated>2026-09-06T00:00:00Z</updated><category term="app"/><summary>Rust/Axum: a restart button without mounting the Docker socket. Proxy identity is trusted only from the real peer address, and a root helper exposes just status and restart for one fixed target, behind a lock and cooldown.</summary></entry>
<entry><title>A self-merge deleted the record it should preserve</title><link href="https://lykrin.github.io/research/atomic-merge-integrity.html"/><id>https://lykrin.github.io/research/atomic-merge-integrity.html</id><updated>2026-09-05T00:00:00Z</updated><category term="app"/><summary>SQLite: merging a record into itself deleted it, and a late failure left half-copied associations. Identical IDs are now rejected at the API and store, and the whole merge runs in one transaction.</summary></entry>
<entry><title>Closing the direct-backend route around a login gateway</title><link href="https://lykrin.github.io/research/proxy-backend-firewall-contracts.html"/><id>https://lykrin.github.io/research/proxy-backend-firewall-contracts.html</id><updated>2026-08-31T00:00:00Z</updated><category term="infra"/><summary>Forward auth only protects traffic that passes through the proxy. Source-specific nftables permits and drops close the direct route to backend listeners while keeping named health checks.</summary></entry>
<entry><title>Turning MQTT credentials into per-role topic boundaries</title><link href="https://lykrin.github.io/research/mqtt-topic-authorization.html"/><id>https://lykrin.github.io/research/mqtt-topic-authorization.html</id><updated>2026-08-31T00:00:00Z</updated><category term="infra"/><summary>Shared MQTT broker: authentication alone did not limit which topics a client could use. A per-role ACL confines each client to its namespace; wildcard and integration exceptions are called out.</summary></entry>
<entry><title>Authorize the VPN target before invoking its backend</title><link href="https://lykrin.github.io/research/vpn-control-plane-authorization.html"/><id>https://lykrin.github.io/research/vpn-control-plane-authorization.html</id><updated>2026-07-05T00:00:00Z</updated><category term="identity"/><summary>Flask VPN control plane: once backend names entered mutation URLs, authentication alone would let an isolated-plane user POST against the trusted plane. Per-plane membership checks now run before any privileged add or revoke.</summary></entry>
<entry><title>VPN revocation and rollback state</title><link href="https://lykrin.github.io/research/vpn-revoke-rollback-state.html"/><id>https://lykrin.github.io/research/vpn-revoke-rollback-state.html</id><updated>2026-07-05T00:00:00Z</updated><category term="infra"/><summary>WireGuard peer revocation touches four stores. Reconstructs a July fix and its ordering test, and shows how rolling back after a bookkeeping failure can restore a peer that was already revoked.</summary></entry>
<entry><title>Closing a failed transaction before the next caller uses it</title><link href="https://lykrin.github.io/research/rollback-after-partial-snapshot-write.html"/><id>https://lykrin.github.io/research/rollback-after-partial-snapshot-write.html</id><updated>2026-07-02T00:00:00Z</updated><category term="app"/><summary>SQLite: a failed snapshot replacement left the shared connection mid-transaction, so the next caller&#x27;s writes joined it. Explicit rollback before re-raising restores the old snapshot and a clean connection.</summary></entry>
<entry><title>Preserving manual ownership during an automatic rebuild</title><link href="https://lykrin.github.io/research/preserving-manual-lineage.html"/><id>https://lykrin.github.io/research/preserving-manual-lineage.html</id><updated>2026-06-21T00:00:00Z</updated><category term="app"/><summary>An automatic rebuild kept user-edited groups but deleted their lineage edges. Cleanup now removes only edges it owns at both endpoints, and the regression test can no longer pass on empty output.</summary></entry>
<entry><title>Remove database-path authority from browser settings</title><link href="https://lykrin.github.io/research/database-configuration-boundaries.html"/><id>https://lykrin.github.io/research/database-configuration-boundaries.html</id><updated>2026-06-14T00:00:00Z</updated><category term="app"/><summary>FastAPI settings endpoint accepted db_path, so any API caller could choose the database location for the next start. Removed the field, rejected unknown fields, and moved the demo database out of a predictable shared temp path.</summary></entry>
<entry><title>Browser-origin checks across HTTP and WebSocket entry points</title><link href="https://lykrin.github.io/research/browser-origin-and-websocket-boundaries.html"/><id>https://lykrin.github.io/research/browser-origin-and-websocket-boundaries.html</id><updated>2026-06-12T00:00:00Z</updated><category term="app"/><summary>CSRF and cross-site WebSocket hijacking on a local web app: Origin-versus-Host checks on every mutating route and the WebSocket handshake, with the remaining scheme and missing-Origin gaps documented.</summary></entry>
</feed>
