Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,5 @@ jobs:
# Wiring all of that up is tracked separately.
mix test.turn
mix test.nat
# Snapcraft recipe contract (unpinned libc6 / USN rebuilds); no anvil.
DIODE_MINIMAL_TEST=1 mix test --no-start test/snap/snapcraft_test.exs
2 changes: 2 additions & 0 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ parts:
# WireGuardNat (MASQUERADE/FORWARD) calls iptables + `ip route`; firewall-control plug required at runtime.
- iptables
- iproute2
# Unpinned: Snapcraft stages the Ubuntu archive libc used at build time.
# Rebuilds pick up USNs (e.g. USN-8737-2 / libc6 2.39-0ubuntu8.9); do not pin.
- libc6
- libncurses6
- libsigsegv2
Expand Down
58 changes: 58 additions & 0 deletions test/snap/snapcraft_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Diode Server
# Copyright 2021-2024 Diode
# Licensed under the Diode License, Version 1.1

# Snap Store USN scans (e.g. USN-8737-2) are resolved by rebuilding when
# libc6 is staged from the Ubuntu archive without a version pin.
defmodule Snap.SnapcraftTest do
use ExUnit.Case, async: true

@snapcraft Path.expand("../../snap/snapcraft.yaml", __DIR__)

setup_all do
{:ok, yaml: File.read!(@snapcraft)}
end

test "uses core24 so staged debs come from Ubuntu 24.04 (noble)", %{yaml: yaml} do
assert yaml =~ ~r/^base:\s*core24\s*$/m
end

test "does not pin apt sources that would freeze libc6 away from USNs", %{yaml: yaml} do
refute yaml =~ ~r/^package-repositories:/m
end

test "libc6 is listed unpinned in build-packages and stage-packages", %{yaml: yaml} do
for section <- ["build-packages", "stage-packages"] do
packages = listed_packages(yaml, section)
libc6 = Enum.filter(packages, &String.starts_with?(&1, "libc6"))

assert libc6 == ["libc6"],
"#{section} must list unpinned libc6 so a snap rebuild pulls archive security updates (USN-8737-2); got: #{inspect(libc6)}"
end
end

defp listed_packages(yaml, section) do
lines = String.split(yaml, "\n")

start =
Enum.find_index(lines, fn line ->
String.match?(line, ~r/^\s{2,4}#{Regex.escape(section)}:\s*$/)
end)

assert is_integer(start), "missing #{section} in snap/snapcraft.yaml"

lines
|> Enum.drop(start + 1)
|> Enum.take_while(&list_continuation?/1)
|> Enum.flat_map(fn line ->
case Regex.run(~r/^\s+-\s+(\S+)\s*$/, line) do
[_, pkg] -> [pkg]
_ -> []
end
end)
end

defp list_continuation?(line) do
String.match?(line, ~r/^\s*(?:#|$|- )/)
end
end
Loading