Initial commit on Forgejo
All checks were successful
CI and deploy / Test (push) Successful in 4m52s
CI and deploy / Deploy production (push) Successful in 23s

Fresh repository history for elektrine/tarakan hosted at
https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
Maxfield Luke 2026-07-29 04:43:40 -04:00
commit af6077b9c3
455 changed files with 78366 additions and 0 deletions

View file

@ -0,0 +1,85 @@
defmodule TarakanWeb.Plugs.ApiRateLimitTest do
use Tarakan.DataCase, async: true
import Plug.Conn
import Plug.Test
alias Tarakan.Accounts.Scope
alias TarakanWeb.Plugs.ApiRateLimit
defp call_ip(conn, opts) do
ApiRateLimit.call(conn, ApiRateLimit.init([mode: :ip] ++ opts))
end
defp call_actor(conn, scope, opts) do
conn
|> assign(:current_scope, scope)
|> ApiRateLimit.call(ApiRateLimit.init([mode: :actor] ++ opts))
end
defp credential_scope(account, token_id) do
Scope.for_account(account, token_id: token_id, authentication_method: :api_credential)
end
test "requests carrying a Bearer token count against the IP bucket" do
opts = [request_limit: 2, mutation_limit: 2, window_seconds: 60]
for _ <- 1..2 do
conn =
conn(:get, "/api/repositories")
|> put_req_header("authorization", "Bearer garbage-token")
|> call_ip(opts)
refute conn.halted
end
conn =
conn(:get, "/api/repositories")
|> put_req_header("authorization", "Bearer garbage-token")
|> call_ip(opts)
assert conn.halted
assert conn.status == 429
assert Jason.decode!(conn.resp_body)["error"] == "rate limit exceeded"
end
test "IPv6 clients in the same /64 share one IP bucket" do
opts = [request_limit: 1, mutation_limit: 1, window_seconds: 60]
first =
conn(:get, "/api/repositories")
|> Map.put(:remote_ip, {0x2001, 0x0DB8, 0, 0, 0, 0, 0, 1})
|> call_ip(opts)
refute first.halted
second =
conn(:get, "/api/repositories")
|> Map.put(:remote_ip, {0x2001, 0x0DB8, 0, 0, 0xAAAA, 0xBBBB, 0xCCCC, 0xDDDD})
|> call_ip(opts)
assert second.halted
assert second.status == 429
end
test "plain requests still count against the IP bucket" do
opts = [request_limit: 1, mutation_limit: 1, window_seconds: 60]
refute conn(:get, "/api/repositories") |> call_ip(opts) |> Map.fetch!(:halted)
assert conn(:get, "/api/repositories") |> call_ip(opts) |> Map.fetch!(:halted)
end
test "authenticated actors get the multiplied actor budget" do
opts = [request_limit: 2, mutation_limit: 2, window_seconds: 60]
scope = credential_scope(account_fixture(), 11_001)
# 2 × @actor_multiplier (4) = 8 requests fit; the 9th is rejected.
for _ <- 1..8 do
refute conn(:get, "/api/jobs") |> call_actor(scope, opts) |> Map.fetch!(:halted)
end
limited = conn(:get, "/api/jobs") |> call_actor(scope, opts)
assert limited.halted
assert limited.status == 429
end
end

View file

@ -0,0 +1,114 @@
defmodule TarakanWeb.Plugs.ClientIpTest do
use ExUnit.Case, async: false
import Plug.Conn
import Plug.Test
alias Tarakan.TrustedProxies
alias TarakanWeb.Plugs.ClientIp
setup do
previous = Application.get_env(:tarakan, :trusted_proxies, [])
on_exit(fn ->
Application.put_env(:tarakan, :trusted_proxies, previous)
end)
:ok
end
test "does not trust X-Forwarded-For without configured proxies" do
Application.put_env(:tarakan, :trusted_proxies, [])
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {10, 0, 0, 5})
|> put_req_header("x-forwarded-for", "203.0.113.9")
|> ClientIp.call([])
assert conn.remote_ip == {10, 0, 0, 5}
end
test "rewrites remote_ip when the peer is a trusted proxy" do
Application.put_env(:tarakan, :trusted_proxies, TrustedProxies.parse("10.0.0.0/8"))
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {10, 0, 0, 5})
|> put_req_header("x-forwarded-for", "203.0.113.9, 10.0.0.5")
|> ClientIp.call([])
assert conn.remote_ip == {203, 0, 113, 9}
end
test "ignores forwarded headers from untrusted peers" do
Application.put_env(:tarakan, :trusted_proxies, TrustedProxies.parse("10.0.0.0/8"))
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {198, 51, 100, 1})
|> put_req_header("x-forwarded-for", "203.0.113.9")
|> ClientIp.call([])
assert conn.remote_ip == {198, 51, 100, 1}
end
test "picks the rightmost untrusted hop, not a client-spoofed leftmost entry" do
Application.put_env(:tarakan, :trusted_proxies, TrustedProxies.parse("10.0.0.0/8"))
# Attacker sends a forged leftmost value; the trusted proxy appends the real
# peer it observed. remote_ip must be the real hop, never the forged one.
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {10, 0, 0, 5})
|> put_req_header("x-forwarded-for", "203.0.113.9, 198.51.100.7")
|> ClientIp.call([])
assert conn.remote_ip == {198, 51, 100, 7}
end
test "strips multiple trailing trusted proxies down to the client" do
Application.put_env(:tarakan, :trusted_proxies, TrustedProxies.parse("10.0.0.0/8"))
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {10, 0, 0, 9})
|> put_req_header("x-forwarded-for", "203.0.113.9, 10.0.0.5, 10.0.0.9")
|> ClientIp.call([])
assert conn.remote_ip == {203, 0, 113, 9}
end
test "matches an IPv4 trusted proxy against an IPv6-mapped IPv4 peer" do
# On a dual-stack (`::`) listener, an IPv4 proxy arrives as ::ffff:10.0.0.5.
Application.put_env(:tarakan, :trusted_proxies, TrustedProxies.parse("10.0.0.0/8"))
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {0, 0, 0, 0, 0, 0xFFFF, 0x0A00, 0x0005})
|> put_req_header("x-forwarded-for", "203.0.113.9")
|> ClientIp.call([])
assert conn.remote_ip == {203, 0, 113, 9}
end
test "normalizes an IPv6-mapped remote_ip to plain IPv4 for rate-limit keys" do
conn = %Plug.Conn{remote_ip: {0, 0, 0, 0, 0, 0xFFFF, 0xCB00, 0x7109}}
assert ClientIp.remote_ip_string(conn) == "203.0.113.9"
end
test "remote_ip_bucket keeps the full IPv4 address" do
conn = %Plug.Conn{remote_ip: {203, 0, 113, 9}}
assert ClientIp.remote_ip_bucket(conn) == {:v4, 203, 0, 113, 9}
end
test "remote_ip_bucket truncates IPv6 to the /64 prefix" do
conn = %Plug.Conn{remote_ip: {0x2001, 0x0DB8, 0x0001, 0x0002, 1, 2, 3, 4}}
assert ClientIp.remote_ip_bucket(conn) == {:v6, 0x2001, 0x0DB8, 0x0001, 0x0002}
end
test "remote_ip_bucket folds IPv4-mapped IPv6 into the IPv4 bucket" do
conn = %Plug.Conn{remote_ip: {0, 0, 0, 0, 0, 0xFFFF, 0xCB00, 0x7109}}
assert ClientIp.remote_ip_bucket(conn) == {:v4, 203, 0, 113, 9}
end
end

View file

@ -0,0 +1,68 @@
defmodule TarakanWeb.Plugs.CodeBrowserRateLimitTest do
use Tarakan.DataCase, async: true
import Plug.Conn
import Plug.Test
alias TarakanWeb.Plugs.CodeBrowserRateLimit
test "allows requests under the per-IP budget" do
opts = CodeBrowserRateLimit.init(limit: 2, window_seconds: 60)
for path <- [
"/findings/55d5c681-240a-4e3a-a1f8-45933a30c4ef/code",
"/github.com/openai/codex"
] do
conn = conn(:get, path) |> CodeBrowserRateLimit.call(opts)
refute conn.halted
end
end
test "answers 429 once the per-IP budget is exceeded" do
opts = CodeBrowserRateLimit.init(limit: 2, window_seconds: 60)
for _ <- 1..2 do
conn = conn(:get, "/github.com/openai/codex") |> CodeBrowserRateLimit.call(opts)
refute conn.halted
end
conn = conn(:get, "/github.com/openai/codex") |> CodeBrowserRateLimit.call(opts)
assert conn.halted
assert conn.status == 429
assert [retry_after] = get_resp_header(conn, "retry-after")
assert String.to_integer(retry_after) > 0
end
test "IPv6 clients in the same /64 share one bucket" do
opts = CodeBrowserRateLimit.init(limit: 1, window_seconds: 60)
first =
conn(:get, "/github.com/openai/codex")
|> Map.put(:remote_ip, {0x2001, 0x0DB8, 0, 0, 0, 0, 0, 1})
|> CodeBrowserRateLimit.call(opts)
refute first.halted
second =
conn(:get, "/github.com/openai/codex")
|> Map.put(:remote_ip, {0x2001, 0x0DB8, 0, 0, 0xFFFF, 0xEEEE, 0xDDDD, 0xCCCC})
|> CodeBrowserRateLimit.call(opts)
assert second.halted
assert second.status == 429
end
test "admins and moderators are exempt from the budget" do
opts = CodeBrowserRateLimit.init(limit: 1, window_seconds: 60)
for role <- ["admin", "moderator"], _ <- 1..3 do
conn =
conn(:get, "/github.com/openai/codex")
|> assign(:current_scope, %{platform_role: role})
|> CodeBrowserRateLimit.call(opts)
refute conn.halted
end
end
end

View file

@ -0,0 +1,75 @@
defmodule TarakanWeb.Plugs.ForwardedProtoTest do
use ExUnit.Case, async: false
import Plug.Conn
import Plug.Test
alias Tarakan.TrustedProxies
alias TarakanWeb.Plugs.ForwardedProto
setup do
previous = Application.get_env(:tarakan, :trusted_proxies, [])
on_exit(fn ->
Application.put_env(:tarakan, :trusted_proxies, previous)
end)
:ok
end
test "rewrites the scheme when the peer is a trusted proxy" do
Application.put_env(:tarakan, :trusted_proxies, TrustedProxies.parse("127.0.0.1,::1"))
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {127, 0, 0, 1})
|> put_req_header("x-forwarded-proto", "https")
|> ForwardedProto.call([])
assert conn.scheme == :https
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {0, 0, 0, 0, 0, 0, 0, 1})
|> Map.put(:scheme, :https)
|> put_req_header("x-forwarded-proto", "http")
|> ForwardedProto.call([])
assert conn.scheme == :http
end
test "ignores X-Forwarded-Proto from untrusted peers" do
Application.put_env(:tarakan, :trusted_proxies, TrustedProxies.parse("10.0.0.0/8"))
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {198, 51, 100, 1})
|> put_req_header("x-forwarded-proto", "https")
|> ForwardedProto.call([])
assert conn.scheme == :http
end
test "ignores X-Forwarded-Proto without configured proxies" do
Application.put_env(:tarakan, :trusted_proxies, [])
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {127, 0, 0, 1})
|> put_req_header("x-forwarded-proto", "https")
|> ForwardedProto.call([])
assert conn.scheme == :http
end
test "leaves the scheme untouched without the header" do
Application.put_env(:tarakan, :trusted_proxies, TrustedProxies.parse("127.0.0.1"))
conn =
conn(:get, "/")
|> Map.put(:remote_ip, {127, 0, 0, 1})
|> ForwardedProto.call([])
assert conn.scheme == :http
end
end