Skip to content

models: hoist the duplicated egress access dedup out of ttyrec and scp#28

Merged
golgeek merged 1 commit into
mainfrom
dedup-access-picker
Jun 12, 2026
Merged

models: hoist the duplicated egress access dedup out of ttyrec and scp#28
golgeek merged 1 commit into
mainfrom
dedup-access-picker

Conversation

@golgeek

@golgeek golgeek commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Summary

getUniqueAccessFromAvailableAccesses existed twice — verbatim-identical dedup loops in cmd/ttyrec.go and cmd/scp.go — differing only in what happens on an ambiguous target. The shared logic now lives once as models.UniqueAccessesForHost, tested; the commands keep only their policy tails. No behavior change.

Previous behavior

Both commands carried their own copy of the loop that collapses accesses with the same host/user/port and completes wide-prefix grants (stored with an empty host) with the host the user typed.

Why it was a problem

The shared logic had no tests, and a fix to one copy could silently miss the other — the classic drift risk of duplicated security-adjacent code on the egress path.

What changed

  • New models.UniqueAccessesForHost(accesses, host): order-preserving dedup plus in-place wide-prefix host completion, with the rationale documented (the completion participates in dedup, so a prefix grant collapses against an explicit grant for the same target).
  • ttyrec and scp keep thin wrappers holding only their policy: single match → use it; several → ttyrec prompts the user interactively, scp refuses with its historical message (its wrapper has no terminal to prompt on).

How it's tested

Table tests pin the hoisted function: duplicate collapse with order preserved, distinctness by user and port, wide-prefix completion with the typed host, a completed prefix deduplicating against an explicit grant, and empty input. go build ./..., go test ./..., golangci-lint run green; demo-stack e2e passes against this branch (host connections and SCP exercise both wrappers end to end).

Test plan

  • go test ./internal/models/ -run TestUniqueAccessesForHost -v
  • tests/e2e-local.sh

Previously, getUniqueAccessFromAvailableAccesses existed twice — once
in cmd/ttyrec.go and once in cmd/scp.go — with a verbatim-identical
deduplication loop (collapse accesses with the same host/user/port,
complete wide-prefix grants with the host the user typed) and only the
final ambiguity policy differing: ttyrec prompts the user
interactively, scp refuses because its wrapper has no terminal to
prompt on. This was problematic because the shared logic had no tests
and any fix to one copy could silently miss the other — the classic
drift risk of duplicated security-adjacent code.

This patch hoists the shared part into models.UniqueAccessesForHost
(order-preserving dedup plus in-place wide-prefix host completion,
documented including why the completion participates in dedup), and
reduces both command methods to thin policy wrappers: single match →
use it; several → ttyrec prompts, scp errors with its historical
message. No behavior change.

Table tests pin the hoisted function: duplicate collapse with order
preserved, distinctness by user and port, wide-prefix completion with
the typed host, a completed prefix deduplicating against an explicit
grant, and the empty input. go build ./..., go test ./... and
golangci-lint run are green, and the demo-stack e2e suite passes
against this branch (the host-connection and SCP paths exercise both
wrappers end to end).
@golgeek golgeek merged commit 76672ec into main Jun 12, 2026
5 checks passed
@golgeek golgeek deleted the dedup-access-picker branch June 12, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant