Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
1008 lines
34 KiB
Elixir
1008 lines
34 KiB
Elixir
defmodule TarakanWeb.RepositoryLiveTest do
|
|
use TarakanWeb.ConnCase
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
alias Tarakan.Repositories
|
|
alias Tarakan.Repositories.Repository
|
|
alias Tarakan.Work
|
|
|
|
test "renders the public repository registry", %{conn: conn} do
|
|
{:ok, view, html} = live(conn, ~p"/")
|
|
|
|
assert has_element?(view, "#repository-auth-gate")
|
|
assert has_element?(view, "#account-login-button")
|
|
refute has_element?(view, "#activity-wire")
|
|
assert has_element?(view, "#registry-shoutbox")
|
|
assert has_element?(view, "#home-infestations")
|
|
|
|
# The homepage title is what a search result and a browser tab show, so it
|
|
# says what the site is rather than carrying a slogan.
|
|
assert html =~ "<title" and html =~ "Public security record · Tarakan"
|
|
refute html =~ "Beautiful machines"
|
|
|
|
assert html =~ ~s(property="og:image")
|
|
assert html =~ ~s(/images/og-image.png)
|
|
assert html =~ ~s(name="twitter:image")
|
|
end
|
|
|
|
describe "record signals on the home page" do
|
|
test "the band is absent until the record has produced a signal", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
# Four columns of "none yet" is filler, not evidence. Nothing to show
|
|
# means nothing is shown.
|
|
refute has_element?(view, "#record-signals")
|
|
end
|
|
|
|
test "the field is absent until repositories actually share a pattern", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
# Nothing to draw is not a reason to draw nothing-shaped decoration.
|
|
refute has_element?(view, "#infestation-field")
|
|
end
|
|
|
|
test "the field carries the real graph once patterns span repositories", %{conn: conn} do
|
|
submitter = github_account_fixture()
|
|
other = github_account_fixture()
|
|
third = github_account_fixture()
|
|
|
|
repo_a = listed_github_repository_fixture(submitter)
|
|
{:ok, repo_b} = Repositories.register_github_repository("acme/widget", other)
|
|
repo_b = listed_repository_fixture(repo_b)
|
|
{:ok, repo_c} = Repositories.register_github_repository("acme/gadget", third)
|
|
repo_c = listed_repository_fixture(repo_c)
|
|
|
|
title = "Shared field pattern #{System.unique_integer([:positive])}"
|
|
|
|
findings = fn path ->
|
|
Jason.encode!(%{
|
|
"tarakan_scan_format" => 1,
|
|
"findings" => [
|
|
%{
|
|
"file" => path,
|
|
"severity" => "high",
|
|
"title" => title,
|
|
"description" => "Spans several repositories."
|
|
}
|
|
]
|
|
})
|
|
end
|
|
|
|
scan_fixture(repo_a, submitter, %{"findings_json" => findings.("lib/a.ex")})
|
|
scan_fixture(repo_b, other, %{"findings_json" => findings.("src/b.rs")})
|
|
scan_fixture(repo_c, third, %{"findings_json" => findings.("pkg/c.go")})
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
assert has_element?(view, "#infestation-field")
|
|
|
|
graph =
|
|
view
|
|
|> element("#infestation-field")
|
|
|> render()
|
|
|> then(&Regex.run(~r/data-graph="([^"]*)"/, &1))
|
|
|> Enum.at(1)
|
|
|> String.replace(""", "\"")
|
|
|> Jason.decode!()
|
|
|
|
# Three repositories sharing one pattern is a triangle: 3 nodes, 3 edges.
|
|
assert length(graph["nodes"]) == 3
|
|
assert length(graph["edges"]) == 3
|
|
assert Enum.all?(graph["edges"], &(length(&1) == 2))
|
|
end
|
|
|
|
test "the capabilities are explained where the prose lives", %{conn: conn} do
|
|
{:ok, _view, html} = live(conn, ~p"/")
|
|
|
|
# Explanation belongs in How it works, not stacked onto the data strip.
|
|
assert html =~ "Then: carry"
|
|
assert html =~ "Then: regress"
|
|
assert html =~ "Then: rank"
|
|
assert html =~ "Then: score"
|
|
end
|
|
|
|
test "only signals with data get a column", %{conn: conn} do
|
|
account = github_account_fixture()
|
|
repository = github_repository_fixture(account)
|
|
|
|
scored =
|
|
scan_fixture(repository, account, %{
|
|
"model" => "lone-agent",
|
|
"findings_json" => findings_json_fixture(5)
|
|
})
|
|
|
|
for occurrence <- scored.findings do
|
|
Tarakan.Repo.get!(Tarakan.Scans.CanonicalFinding, occurrence.canonical_finding_id)
|
|
|> Ecto.Changeset.change(status: "verified")
|
|
|> Tarakan.Repo.update!()
|
|
end
|
|
|
|
{:ok, view, html} = live(conn, ~p"/")
|
|
|
|
# Model precision has data; nothing has been carried or regressed, so
|
|
# those columns are absent rather than padded.
|
|
assert has_element?(view, "#record-signals")
|
|
assert html =~ "Model precision"
|
|
refute html =~ "Fixes carried"
|
|
refute html =~ "Regressed"
|
|
end
|
|
|
|
test "model precision ranks by accuracy, not volume", %{conn: conn} do
|
|
account = github_account_fixture()
|
|
repository = github_repository_fixture(account)
|
|
|
|
accurate =
|
|
scan_fixture(repository, account, %{
|
|
"model" => "accurate-agent",
|
|
"findings_json" => findings_json_fixture(5)
|
|
})
|
|
|
|
for occurrence <- accurate.findings do
|
|
Tarakan.Repo.get!(Tarakan.Scans.CanonicalFinding, occurrence.canonical_finding_id)
|
|
|> Ecto.Changeset.change(status: "verified")
|
|
|> Tarakan.Repo.update!()
|
|
end
|
|
|
|
{:ok, view, html} = live(conn, ~p"/")
|
|
|
|
assert has_element?(view, "#record-signals")
|
|
assert html =~ "accurate-agent"
|
|
assert html =~ "100.0%"
|
|
end
|
|
|
|
test "a regressed finding appears with its repository and commit", %{conn: conn} do
|
|
submitter = github_account_fixture()
|
|
repository = listed_github_repository_fixture(submitter)
|
|
|
|
scan =
|
|
scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|
|
[occurrence] = scan.findings
|
|
|
|
canonical =
|
|
Tarakan.Repo.get!(Tarakan.Scans.CanonicalFinding, occurrence.canonical_finding_id)
|
|
|> Ecto.Changeset.change(%{
|
|
status: "fixed",
|
|
fixed_at: DateTime.utc_now(),
|
|
fixed_commit_sha: scan.commit_sha
|
|
})
|
|
|> Tarakan.Repo.update!()
|
|
|
|
# The same finding reported again at a different commit is a regression.
|
|
later =
|
|
scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|
|
{:ok, view, html} = live(conn, ~p"/")
|
|
|
|
assert has_element?(view, "#record-signals")
|
|
assert html =~ canonical.title
|
|
assert html =~ "Regressed"
|
|
assert html =~ String.slice(later.commit_sha, 0, 7)
|
|
end
|
|
|
|
test "a repository that closes findings appears in the responsiveness list", %{conn: conn} do
|
|
repository = listed_github_repository_fixture()
|
|
now = DateTime.utc_now()
|
|
|
|
for _index <- 1..3 do
|
|
%Tarakan.Scans.CanonicalFinding{}
|
|
|> Ecto.Changeset.change(%{
|
|
repository_id: repository.id,
|
|
fingerprint: "fp-#{System.unique_integer([:positive])}",
|
|
file_path: "lib/app.ex",
|
|
line_start: 1,
|
|
severity: "high",
|
|
title: "Closed finding #{System.unique_integer([:positive])}",
|
|
description: "Body.",
|
|
status: "fixed",
|
|
first_seen_commit_sha: random_commit_sha(),
|
|
last_seen_commit_sha: random_commit_sha(),
|
|
fixed_commit_sha: random_commit_sha(),
|
|
verified_at: DateTime.add(now, -2 * 86_400, :second),
|
|
fixed_at: now,
|
|
inserted_at: now,
|
|
updated_at: now
|
|
})
|
|
|> Tarakan.Repo.insert!()
|
|
end
|
|
|
|
{:ok, view, html} = live(conn, ~p"/")
|
|
|
|
assert has_element?(view, "#record-signals")
|
|
assert html =~ "Fastest to fix"
|
|
assert html =~ "#{repository.owner}/#{repository.name}"
|
|
assert html =~ "2d"
|
|
end
|
|
end
|
|
|
|
test "home infestations lists multi-repo patterns", %{conn: conn} do
|
|
submitter = github_account_fixture()
|
|
other = github_account_fixture()
|
|
third = github_account_fixture()
|
|
repo_a = listed_github_repository_fixture(submitter)
|
|
{:ok, repo_b} = Repositories.register_github_repository("acme/widget", other)
|
|
repo_b = listed_repository_fixture(repo_b)
|
|
{:ok, repo_c} = Repositories.register_github_repository("acme/gadget", third)
|
|
repo_c = listed_repository_fixture(repo_c)
|
|
|
|
title = "Home infestation #{System.unique_integer([:positive])}"
|
|
|
|
findings = fn path ->
|
|
Jason.encode!(%{
|
|
"tarakan_scan_format" => 1,
|
|
"findings" => [
|
|
%{
|
|
"file" => path,
|
|
"severity" => "high",
|
|
"title" => title,
|
|
"description" => "Homepage infestation fixture."
|
|
}
|
|
]
|
|
})
|
|
end
|
|
|
|
scan_fixture(repo_a, submitter, %{"findings_json" => findings.("lib/a.ex")})
|
|
scan_fixture(repo_b, other, %{"findings_json" => findings.("src/b.rs")})
|
|
scan_fixture(repo_c, third, %{"findings_json" => findings.("pkg/c.go")})
|
|
|
|
pattern = Tarakan.FindingMemory.pattern_key(title)
|
|
|
|
{:ok, view, html} = live(conn, ~p"/")
|
|
|
|
assert has_element?(view, "#home-infestations-list")
|
|
assert has_element?(view, "#home-infestation-map")
|
|
assert has_element?(view, "#home-infestation-map-hub-#{pattern}")
|
|
assert html =~ title
|
|
end
|
|
|
|
test "trending infestations section renders a window switcher that re-ranks", %{conn: conn} do
|
|
submitter = github_account_fixture()
|
|
other = github_account_fixture()
|
|
third = github_account_fixture()
|
|
repo_a = listed_github_repository_fixture(submitter)
|
|
{:ok, repo_b} = Repositories.register_github_repository("acme/widget", other)
|
|
repo_b = listed_repository_fixture(repo_b)
|
|
{:ok, repo_c} = Repositories.register_github_repository("acme/gadget", third)
|
|
repo_c = listed_repository_fixture(repo_c)
|
|
|
|
title = "Trending infestation #{System.unique_integer([:positive])}"
|
|
|
|
findings = fn path ->
|
|
Jason.encode!(%{
|
|
"tarakan_scan_format" => 1,
|
|
"findings" => [
|
|
%{
|
|
"file" => path,
|
|
"severity" => "high",
|
|
"title" => title,
|
|
"description" => "Homepage infestation fixture."
|
|
}
|
|
]
|
|
})
|
|
end
|
|
|
|
scan_fixture(repo_a, submitter, %{"findings_json" => findings.("lib/a.ex")})
|
|
scan_fixture(repo_b, other, %{"findings_json" => findings.("src/b.rs")})
|
|
scan_fixture(repo_c, third, %{"findings_json" => findings.("pkg/c.go")})
|
|
|
|
pattern = Tarakan.FindingMemory.pattern_key(title)
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
assert has_element?(view, "#home-infestations", "Trending infestations")
|
|
assert has_element?(view, "#home-infestations a[href='/infestations']")
|
|
|
|
# The default 7d window lists the fresh pattern; switching windows keeps it
|
|
# listed (the fixture is inside every window).
|
|
assert has_element?(
|
|
view,
|
|
"#home-infestations-window button[phx-value-days='7'][aria-pressed='true']"
|
|
)
|
|
|
|
view |> element("#home-infestations-window button[phx-value-days='90']") |> render_click()
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#home-infestations-window button[phx-value-days='90'][aria-pressed='true']"
|
|
)
|
|
|
|
assert has_element?(view, "#home-infestation-map-hub-#{pattern}")
|
|
end
|
|
|
|
test "scan queue excludes quarantined repositories", %{conn: conn} do
|
|
repository = github_repository_fixture()
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
assert has_element?(view, "#scan-queue-#{repository.id}", "★")
|
|
|
|
quarantined =
|
|
repository
|
|
|> Repository.listing_changeset(%{listing_status: "quarantined"})
|
|
|> Tarakan.Repo.update!()
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
refute has_element?(view, "#scan-queue-#{quarantined.id}")
|
|
end
|
|
|
|
test "lists registrations in the public registry immediately", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
account = github_account_fixture()
|
|
|
|
{:ok, repository} =
|
|
Repositories.register_github_repository("github.com/OpenAI/Codex", account)
|
|
|
|
_ = :sys.get_state(view.pid)
|
|
|
|
assert repository.listing_status == "listed"
|
|
assert has_element?(view, "#scan-queue-#{repository.id}")
|
|
assert has_element?(view, "#repository-count", "1")
|
|
end
|
|
|
|
test "resolved repository containment evicts connected public repository and task views", %{
|
|
conn: conn
|
|
} do
|
|
creator = github_account_fixture()
|
|
repository = listed_github_repository_fixture(creator)
|
|
task = review_task_fixture(repository, creator)
|
|
{:ok, repository_view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
{:ok, task_view, _html} = live(conn, ~p"/jobs/#{task.id}")
|
|
|
|
reporter = account_fixture()
|
|
moderator = moderator_account_fixture()
|
|
|
|
assert {:ok, case_record} =
|
|
Tarakan.Moderation.report(
|
|
Tarakan.Accounts.Scope.for_account(reporter),
|
|
%{
|
|
"subject_type" => "repository",
|
|
"subject_id" => repository.id,
|
|
"reason" => "unsafe_disclosure",
|
|
"description" =>
|
|
"The public repository record contains disclosure material that needs review."
|
|
}
|
|
)
|
|
|
|
assert {:ok, assigned} =
|
|
Tarakan.Moderation.assign(
|
|
Tarakan.Accounts.Scope.for_account(moderator),
|
|
case_record
|
|
)
|
|
|
|
assert {:ok, _resolved} =
|
|
Tarakan.Moderation.resolve(
|
|
Tarakan.Accounts.Scope.for_account(moderator),
|
|
assigned,
|
|
"resolved",
|
|
"The report was verified and the public record must remain contained."
|
|
)
|
|
|
|
assert_redirect(repository_view, ~p"/")
|
|
assert_redirect(task_view, ~p"/github.com/openai/codex")
|
|
|
|
contained = Tarakan.Repo.get!(Repository, repository.id)
|
|
assert contained.participation_mode == "paused"
|
|
assert contained.listing_status == "quarantined"
|
|
end
|
|
|
|
describe "registry search" do
|
|
test "finds a listed repository by partial name and links to its record", %{conn: conn} do
|
|
repository = github_repository_fixture()
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
view
|
|
|> form("#repository-search-form", %{"q" => "ope"})
|
|
|> render_change()
|
|
|
|
assert has_element?(view, "#search-result-#{repository.id}", "openai/codex")
|
|
|
|
assert view |> element("#search-result-#{repository.id}") |> render() =~
|
|
~s(href="/github.com/openai/codex")
|
|
end
|
|
|
|
test "matches the combined owner/name form", %{conn: conn} do
|
|
repository = github_repository_fixture()
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
view
|
|
|> form("#repository-search-form", %{"q" => "openai/co"})
|
|
|> render_change()
|
|
|
|
assert has_element?(view, "#search-result-#{repository.id}", "openai/codex")
|
|
end
|
|
|
|
test "never lists quarantined repositories", %{conn: conn} do
|
|
repository =
|
|
github_repository_fixture()
|
|
|> Repository.listing_changeset(%{listing_status: "quarantined"})
|
|
|> Tarakan.Repo.update!()
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
view
|
|
|> form("#repository-search-form", %{"q" => "codex"})
|
|
|> render_change()
|
|
|
|
refute has_element?(view, "#search-result-#{repository.id}")
|
|
assert has_element?(view, "#repository-search-results", "No repositories match")
|
|
end
|
|
|
|
test "renders no results panel for a blank query", %{conn: conn} do
|
|
github_repository_fixture()
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
refute has_element?(view, "#repository-search-results")
|
|
|
|
view
|
|
|> form("#repository-search-form", %{"q" => " "})
|
|
|> render_change()
|
|
|
|
refute has_element?(view, "#repository-search-results")
|
|
end
|
|
|
|
test "treats ilike metacharacters as literals", %{conn: conn} do
|
|
repository = github_repository_fixture()
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
view
|
|
|> form("#repository-search-form", %{"q" => "%"})
|
|
|> render_change()
|
|
|
|
refute has_element?(view, "#search-result-#{repository.id}")
|
|
assert has_element?(view, "#repository-search-results", "No repositories match “%”")
|
|
end
|
|
end
|
|
|
|
describe "security record" do
|
|
setup %{conn: conn} do
|
|
submitter = github_account_fixture()
|
|
repository = listed_github_repository_fixture(submitter)
|
|
%{conn: conn, submitter: submitter, repository: repository}
|
|
end
|
|
|
|
test "malformed vote parameters are rejected without crashing", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|
|
conn = log_in_account(conn, account_fixture())
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
html =
|
|
render_click(view, "vote", %{
|
|
"type" => "canonical_finding",
|
|
"id" => "1 OR 1=1",
|
|
"vote" => "1"
|
|
})
|
|
|
|
assert html =~ "could not be recorded"
|
|
|
|
html =
|
|
render_click(view, "vote", %{
|
|
"type" => "canonical_finding",
|
|
"id" => "12",
|
|
"vote" => "1; DROP"
|
|
})
|
|
|
|
assert html =~ "could not be recorded"
|
|
end
|
|
|
|
test "logged-out visitors see fresh submissions but not moderator-restricted scans", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|
|
restricted =
|
|
scan_fixture(repository, account_fixture(), %{"findings_json" => findings_json_fixture(1)})
|
|
|
|
{:ok, restricted} =
|
|
Tarakan.Scans.update_visibility(
|
|
Tarakan.Accounts.Scope.for_account(moderator_account_fixture()),
|
|
restricted,
|
|
"restricted",
|
|
%{
|
|
"moderation_reason" => "evidence_reviewed",
|
|
"moderation_notes" =>
|
|
"The disclosure needs moderator review and is taken down in the meantime."
|
|
}
|
|
)
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#scans-#{scan.id}", "Awaiting independent check")
|
|
assert has_element?(view, "#scans-#{scan.id}", "1 finding")
|
|
assert has_element?(view, "#scans-#{scan.id}", "lib/example/module_1.ex")
|
|
refute has_element?(view, "#scans-#{restricted.id}")
|
|
refute has_element?(view, "#scan-api-pointer")
|
|
refute has_element?(view, "#scan-submission-form")
|
|
refute has_element?(view, "#scan-#{scan.id}-verdict-form")
|
|
end
|
|
|
|
test "authorization changes evict stale LiveView snapshots", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|
|
{:ok, view, _html} =
|
|
live(log_in_account(conn, submitter), ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#scans-#{scan.id}")
|
|
|
|
admin =
|
|
account_fixture()
|
|
|> Tarakan.Accounts.Account.authorization_changeset(%{
|
|
state: "active",
|
|
platform_role: "admin"
|
|
})
|
|
|> Tarakan.Repo.update!()
|
|
|
|
assert {:ok, _suspended} =
|
|
Tarakan.Accounts.update_authorization(
|
|
Tarakan.Accounts.Scope.for_account(admin),
|
|
submitter,
|
|
%{state: "suspended"}
|
|
)
|
|
|
|
assert_redirect(view, ~p"/")
|
|
end
|
|
|
|
test "renders clean and findings scans with the repository state", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
findings_scan =
|
|
repository
|
|
|> scan_fixture(submitter, %{"findings_json" => findings_json_fixture(2)})
|
|
|> publish_scan("public")
|
|
|
|
repository
|
|
|> scan_fixture(account_fixture(), %{"findings_json" => nil})
|
|
|> publish_scan("public")
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#scans article", "No findings reported")
|
|
assert has_element?(view, "#scans article", "2 findings")
|
|
assert has_element?(view, "#scans article", "lib/example/module_1.ex:10-15")
|
|
assert has_element?(view, "#repository-status", "2 open")
|
|
assert has_element?(view, "#scan-count", "2")
|
|
assert has_element?(view, "#open-findings-count", "2")
|
|
assert has_element?(view, "#canonical-findings", "2 unique open")
|
|
|
|
assert has_element?(view, "#canonical-findings article + article")
|
|
|
|
for finding <- findings_scan.findings do
|
|
assert has_element?(
|
|
view,
|
|
"#finding-source-#{finding.id}[href='/findings/#{finding.public_id}/code']"
|
|
)
|
|
end
|
|
end
|
|
|
|
test "assimilates repeated report findings into one public issue", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
commit_sha = random_commit_sha()
|
|
document = findings_json_fixture(1)
|
|
|
|
scan_fixture(repository, submitter, %{
|
|
"commit_sha" => commit_sha,
|
|
"findings_json" => document
|
|
})
|
|
|
|
scan_fixture(repository, account_fixture(), %{
|
|
"commit_sha" => commit_sha,
|
|
"findings_json" => document
|
|
})
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#scan-count", "2")
|
|
assert has_element?(view, "#open-findings-count", "1")
|
|
assert has_element?(view, "#canonical-findings", "detected in 2 runs")
|
|
assert has_element?(view, "#canonical-findings", "2 submitters")
|
|
|
|
assert has_element?(view, "#canonical-findings article[id^='canonical-']")
|
|
refute has_element?(view, "#canonical-findings article + article")
|
|
end
|
|
|
|
test "public summaries do not disclose finding paths or source links", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan =
|
|
repository
|
|
|> scan_fixture(submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|> publish_scan("public_summary")
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
html = render(view)
|
|
|
|
assert has_element?(view, "#scans-#{scan.id}", "Detailed evidence is restricted")
|
|
refute html =~ "lib/example/module_1.ex"
|
|
refute html =~ "/findings/"
|
|
refute has_element?(view, "[id^='finding-source-']")
|
|
end
|
|
|
|
test "shows human provenance, review type, and narrative evidence", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan =
|
|
repository
|
|
|> scan_fixture(submitter, %{
|
|
"provenance" => "human",
|
|
"review_kind" => "privacy_review",
|
|
"model" => nil,
|
|
"prompt_version" => nil,
|
|
"notes" => "Manually mapped deletion behavior across analytics exports."
|
|
})
|
|
|> publish_scan("public")
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#scans-#{scan.id}")
|
|
assert has_element?(view, "#scans-#{scan.id}", "Written by a human")
|
|
assert has_element?(view, "#scans-#{scan.id}", "Privacy review")
|
|
assert has_element?(view, "#scans-#{scan.id}", "Manually mapped deletion behavior")
|
|
assert has_element?(view, "#scans-#{scan.id}", "Reviewed")
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#scan-#{scan.id}-provenance-attestation[title='Submitter claim, not independently verified']"
|
|
)
|
|
end
|
|
|
|
test "submitters see no verdict form on their own scan", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter)
|
|
|
|
conn = log_in_account(conn, submitter)
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#scans-#{scan.id}")
|
|
refute has_element?(view, "#scan-#{scan.id}-verdict-form")
|
|
end
|
|
|
|
test "records an independent check on one canonical finding", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[occurrence] = scan.findings
|
|
canonical_id = occurrence.canonical_finding.public_id
|
|
|
|
conn = log_in_account(conn, reviewer_account_fixture())
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
view
|
|
|> form("#canonical-#{canonical_id}-check-form")
|
|
|> render_submit(%{
|
|
"verdict" => "confirmed",
|
|
"notes" => "Independently reproduced this one finding at the pinned commit."
|
|
})
|
|
|
|
assert has_element?(view, "#canonical-#{canonical_id}", "1 confirmed")
|
|
refute has_element?(view, "#canonical-#{canonical_id}-check-form")
|
|
end
|
|
|
|
test "a moderator explicitly accepts and discloses verified evidence", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan =
|
|
scan_fixture(repository, submitter) |> confirmation_fixture(reviewer_account_fixture())
|
|
|
|
scan = confirmation_fixture(scan, reviewer_account_fixture())
|
|
|
|
conn = log_in_account(conn, moderator_account_fixture())
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
view
|
|
|> form("#scan-#{scan.id}-moderation-form",
|
|
moderation: %{
|
|
"moderation_notes" =>
|
|
"Two independent reviewers supplied reproducible evidence for the pinned commit."
|
|
}
|
|
)
|
|
|> render_submit(%{"decision" => "accept"})
|
|
|
|
view
|
|
|> form("#scan-#{scan.id}-moderation-form",
|
|
moderation: %{
|
|
"moderation_notes" =>
|
|
"The public summary omits file paths, reproduction details, and verifier notes."
|
|
}
|
|
)
|
|
|> render_submit(%{"decision" => "publish_summary"})
|
|
|
|
assert has_element?(view, "#scans-#{scan.id}", "Accepted")
|
|
assert has_element?(view, "#scans-#{scan.id}", "Restore full evidence")
|
|
assert has_element?(view, "#repository-status", "Clear")
|
|
assert has_element?(view, "#scan-count", "1")
|
|
end
|
|
|
|
test "scan activity updates the registry stats live", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
{:ok, view, _html} = live(conn, ~p"/")
|
|
|
|
assert has_element?(view, "#unscanned-count", "1")
|
|
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(2)})
|
|
publish_scan(scan)
|
|
|
|
_ = :sys.get_state(view.pid)
|
|
|
|
assert has_element?(view, "#unscanned-count", "0")
|
|
assert has_element?(view, "#verified-findings-count", "2")
|
|
end
|
|
|
|
test "streams scans submitted elsewhere onto the record live", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#scan-count", "0")
|
|
assert has_element?(view, "#repository-status", "No report")
|
|
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|
|
_ = :sys.get_state(view.pid)
|
|
|
|
assert has_element?(view, "#scans-#{scan.id}")
|
|
assert has_element?(view, "#scan-count", "1")
|
|
assert has_element?(view, "#repository-status", "1 open")
|
|
end
|
|
end
|
|
|
|
describe "human review queue" do
|
|
setup %{conn: conn} do
|
|
creator = github_account_fixture()
|
|
repository = listed_github_repository_fixture(creator)
|
|
%{conn: conn, creator: creator, repository: repository}
|
|
end
|
|
|
|
test "the public can see tasks and gets a sign-in gate", %{
|
|
conn: conn,
|
|
creator: creator,
|
|
repository: repository
|
|
} do
|
|
task = review_task_fixture(repository, creator)
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#review-task-auth-gate")
|
|
assert has_element?(view, "#tasks-#{task.id}")
|
|
assert has_element?(view, "#review-task-#{task.id}-link")
|
|
refute has_element?(view, "#review-task-form")
|
|
end
|
|
|
|
test "an authenticated contributor opens a task", %{
|
|
conn: conn,
|
|
creator: creator
|
|
} do
|
|
conn = log_in_account(conn, creator)
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
|
|
assert has_element?(view, "#quick-open-job-button")
|
|
view |> element("#propose-task-toggle") |> render_click()
|
|
|
|
view
|
|
|> form("#review-task-form", review_task: valid_review_task_attributes())
|
|
|> render_submit()
|
|
|
|
assert has_element?(view, "#review-tasks article", "Map the authorization boundary")
|
|
assert has_element?(view, "#review-tasks article", "Human required")
|
|
end
|
|
|
|
test "a moderator can one-click open a default security report job", %{
|
|
conn: conn,
|
|
repository: repository
|
|
} do
|
|
moderator = moderator_account_fixture()
|
|
|
|
{:ok, view, _html} =
|
|
conn
|
|
|> log_in_account(moderator)
|
|
|> live(~p"/github.com/openai/codex/security")
|
|
|
|
view |> element("#quick-open-job-button") |> render_click()
|
|
|
|
assert render(view) =~ "Job opened on the public queue"
|
|
|
|
assert [task] = Work.list_tasks(repository)
|
|
assert task.kind == "code_review"
|
|
assert task.capability == "agent"
|
|
assert task.status == "open"
|
|
end
|
|
|
|
test "customize form can switch the branch tip into the commit field", %{
|
|
conn: conn,
|
|
creator: creator
|
|
} do
|
|
{:ok, view, _html} =
|
|
conn
|
|
|> log_in_account(creator)
|
|
|> live(~p"/github.com/openai/codex/security")
|
|
|
|
view |> element("#propose-task-toggle") |> render_click()
|
|
|
|
assert has_element?(view, "#task-branch-select")
|
|
assert has_element?(view, "#task-branch-select option[value='develop']")
|
|
|
|
view
|
|
|> element("#task-branch-select")
|
|
|> render_change(%{"branch" => "develop"})
|
|
|
|
# develop tip is the stub SHA of all 8s
|
|
assert has_element?(
|
|
view,
|
|
"#review_task_commit_sha[value='8888888888888888888888888888888888888888']"
|
|
)
|
|
end
|
|
|
|
test "a contributor can propose a check for an existing report", %{
|
|
conn: conn,
|
|
creator: creator,
|
|
repository: repository
|
|
} do
|
|
report =
|
|
scan_fixture(repository, account_fixture(), %{
|
|
"findings_json" => findings_json_fixture()
|
|
})
|
|
|
|
{:ok, view, _html} =
|
|
conn
|
|
|> log_in_account(creator)
|
|
|> live(~p"/github.com/openai/codex/security")
|
|
|
|
view |> element("#propose-task-toggle") |> render_click()
|
|
|
|
view
|
|
|> form("#review-task-form", %{
|
|
"review_task" => %{"kind" => "verify_findings"}
|
|
})
|
|
|> render_change()
|
|
|
|
assert has_element?(view, "#review_task_target_review_id option[value='#{report.id}']")
|
|
|
|
view
|
|
|> form("#review-task-form", %{
|
|
"review_task" =>
|
|
valid_review_task_attributes(%{
|
|
"kind" => "verify_findings",
|
|
"target_review_id" => to_string(report.id),
|
|
"title" => "Check the reported authorization bypass"
|
|
})
|
|
})
|
|
|> render_submit()
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#review-tasks article",
|
|
"Check the reported authorization bypass"
|
|
)
|
|
|
|
check_jobs =
|
|
repository
|
|
|> Work.list_tasks()
|
|
|> Enum.filter(&(&1.kind == "verify_findings" and &1.target_review_id == report.id))
|
|
|
|
assert Enum.any?(check_jobs, &(&1.title =~ "Check the reported authorization bypass"))
|
|
assert Enum.any?(check_jobs, &(&1.capability == "agent" and &1.status == "open"))
|
|
end
|
|
end
|
|
|
|
defp publish_scan(scan, visibility \\ "public_summary") do
|
|
scan = confirmation_fixture(scan, reviewer_account_fixture())
|
|
scan = confirmation_fixture(scan, reviewer_account_fixture())
|
|
|
|
scope = Tarakan.Accounts.Scope.for_account(moderator_account_fixture())
|
|
|
|
if visibility == "public" do
|
|
scan.repository
|
|
|> Tarakan.Repositories.Repository.participation_changeset(%{
|
|
participation_mode: "maintainer_verified"
|
|
})
|
|
|> Tarakan.Repo.update!()
|
|
end
|
|
|
|
{:ok, scan} =
|
|
Tarakan.Scans.accept_scan(
|
|
scope,
|
|
scan,
|
|
%{
|
|
"moderation_reason" => "evidence_reviewed",
|
|
"moderation_notes" =>
|
|
"Two independent reviewers supplied reproducible evidence for the pinned commit."
|
|
}
|
|
)
|
|
|
|
{:ok, scan} =
|
|
Tarakan.Scans.update_visibility(
|
|
scope,
|
|
scan,
|
|
visibility,
|
|
%{
|
|
"moderation_reason" => "disclosure_reviewed",
|
|
"moderation_notes" =>
|
|
"Disclosure was separately reviewed for scope, secrets, and personal data.",
|
|
"sensitive_data_reviewed" => "true"
|
|
}
|
|
)
|
|
|
|
scan
|
|
end
|
|
|
|
test "raw reports are provenance-only and canonical findings own public signals", %{conn: conn} do
|
|
submitter = github_account_fixture()
|
|
repository = listed_github_repository_fixture(submitter)
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
refute has_element?(view, "#scan-sort")
|
|
refute has_element?(view, "#scan-#{scan.id}-verdict-form")
|
|
refute has_element?(view, "#vote-finding-#{finding.id}")
|
|
assert has_element?(view, "#vote-canonical_finding-#{finding.canonical_finding.id}")
|
|
end
|
|
|
|
describe "managed disclosure" do
|
|
test "the badge renders near the repository status only when enabled", %{conn: conn} do
|
|
repository = listed_github_repository_fixture()
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
refute has_element?(view, "#repository-managed-disclosure")
|
|
|
|
moderator = Tarakan.Accounts.Scope.for_account(moderator_account_fixture())
|
|
{:ok, _updated} = Repositories.set_managed_disclosure(moderator, repository, true)
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
assert has_element?(view, "#repository-managed-disclosure", "Managed disclosure")
|
|
end
|
|
|
|
test "the toggle is moderator-only and flips the flag", %{conn: conn} do
|
|
listed_github_repository_fixture()
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
refute has_element?(view, "#toggle-managed-disclosure")
|
|
|
|
conn = log_in_account(conn, account_fixture())
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
refute has_element?(view, "#toggle-managed-disclosure")
|
|
|
|
conn = log_in_account(conn, moderator_account_fixture())
|
|
{:ok, view, _html} = live(conn, ~p"/github.com/openai/codex/security")
|
|
assert has_element?(view, "#toggle-managed-disclosure", "Enable")
|
|
|
|
view |> element("#toggle-managed-disclosure") |> render_click()
|
|
|
|
assert has_element?(view, "#repository-managed-disclosure", "Managed disclosure")
|
|
assert has_element?(view, "#toggle-managed-disclosure", "Disable")
|
|
|
|
assert Repositories.get_repository("github.com", "openai", "codex").managed_disclosure ==
|
|
true
|
|
end
|
|
end
|
|
end
|