Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
420 lines
14 KiB
Elixir
420 lines
14 KiB
Elixir
defmodule TarakanWeb.FindingLiveTest do
|
|
use TarakanWeb.ConnCase
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
setup do
|
|
submitter = github_account_fixture()
|
|
repository = listed_github_repository_fixture(submitter)
|
|
%{repository: repository, submitter: submitter}
|
|
end
|
|
|
|
test "renders a publicly disclosed finding", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan =
|
|
repository
|
|
|> scan_fixture(submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|> publish_scan("public")
|
|
|
|
[finding] = scan.findings
|
|
|
|
{:ok, view, html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(view, "#finding-title", finding.title)
|
|
assert has_element?(view, "#finding-severity", "high")
|
|
assert has_element?(view, "#finding-description", "String-built SQL")
|
|
assert has_element?(view, "#finding-canonical-memory", "detected in 1 run")
|
|
assert has_element?(view, "#finding-source-link[href='/findings/#{finding.public_id}/code']")
|
|
assert has_element?(view, "#finding-verified-badge")
|
|
assert has_element?(view, "#finding-disclosure-badge")
|
|
assert has_element?(view, "#finding-copy-link")
|
|
assert has_element?(view, "#finding-cite", "/findings/#{finding.public_id}")
|
|
assert has_element?(view, "#finding-verdict-counts", "2 confirmed · 0 disputed")
|
|
assert html =~ scan.commit_sha
|
|
assert html =~ "Public"
|
|
assert has_element?(view, "#finding-record-link[href='/github.com/openai/codex/security']")
|
|
end
|
|
|
|
test "shows the raw model report exactly as submitted", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
assert scan.raw_document == findings_json_fixture(1)
|
|
|
|
{:ok, view, html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(view, "#finding-raw-report")
|
|
assert html =~ "tarakan_scan_format"
|
|
end
|
|
|
|
test "a qualified contributor records a verdict from the finding page", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
reviewer =
|
|
account_fixture()
|
|
|> Tarakan.Accounts.Account.authorization_changeset(%{
|
|
state: "active",
|
|
platform_role: "member",
|
|
trust_tier: "reviewer"
|
|
})
|
|
|> Tarakan.Repo.update!()
|
|
|
|
conn = log_in_account(conn, reviewer)
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(view, "#finding-verdict-counts", "0 confirmed · 0 disputed")
|
|
|
|
view
|
|
|> form("#finding-verdict-form", %{
|
|
"notes" => "Reproduced against the pinned commit with the documented payload."
|
|
})
|
|
|> render_submit(%{"verdict" => "confirmed"})
|
|
|
|
assert has_element?(view, "#finding-verdict-counts", "1 confirmed · 0 disputed")
|
|
assert has_element?(view, "#finding-checks", "@#{reviewer.handle}")
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#finding-checks",
|
|
"Reproduced against the pinned commit with the documented payload."
|
|
)
|
|
|
|
assert has_element?(view, "#finding-checks", "counts toward quorum")
|
|
refute has_element?(view, "#finding-verdict-form")
|
|
|
|
{:ok, public_view, _html} =
|
|
live(Phoenix.ConnTest.build_conn(), ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(public_view, "#finding-checks", "@#{reviewer.handle}")
|
|
assert has_element?(public_view, "#finding-checks", "documented payload")
|
|
end
|
|
|
|
test "the submitter cannot verify their own finding", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
conn = log_in_account(conn, submitter)
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
refute has_element?(view, "#finding-verdict-form")
|
|
end
|
|
|
|
test "the dead render carries the meta description and canonical URL", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan =
|
|
repository
|
|
|> scan_fixture(submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|> publish_scan("public")
|
|
|
|
[finding] = scan.findings
|
|
|
|
html = conn |> get(~p"/findings/#{finding.public_id}") |> html_response(200)
|
|
|
|
assert html =~ ~s(rel="canonical")
|
|
assert html =~ "/findings/#{finding.public_id}"
|
|
assert html =~ "High in openai/codex"
|
|
assert get_resp_header(get(conn, ~p"/findings/#{finding.public_id}"), "x-robots-tag") == []
|
|
end
|
|
|
|
test "fresh submissions resolve for anonymous visitors immediately", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(view, "#finding-title", finding.title)
|
|
refute has_element?(view, "#finding-verified-badge")
|
|
end
|
|
|
|
test "restricted findings do not resolve for anonymous visitors", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
{:ok, _restricted} =
|
|
Tarakan.Scans.update_visibility(
|
|
Tarakan.Accounts.Scope.for_account(moderator_account_fixture()),
|
|
scan,
|
|
"restricted",
|
|
%{
|
|
"moderation_reason" => "takedown_review",
|
|
"moderation_notes" =>
|
|
"Deliberately restricted in this test to exercise the takedown boundary."
|
|
}
|
|
)
|
|
|
|
assert_error_sent 404, fn -> get(conn, ~p"/findings/#{finding.public_id}") end
|
|
end
|
|
|
|
test "public-summary findings do not resolve for anonymous visitors", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
publish_scan(scan, "public_summary")
|
|
|
|
assert_error_sent 404, fn -> get(conn, ~p"/findings/#{finding.public_id}") end
|
|
end
|
|
|
|
test "the submitter can still open their own restricted finding", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
{:ok, _restricted} =
|
|
Tarakan.Scans.update_visibility(
|
|
Tarakan.Accounts.Scope.for_account(moderator_account_fixture()),
|
|
scan,
|
|
"restricted",
|
|
%{
|
|
"moderation_reason" => "takedown_review",
|
|
"moderation_notes" =>
|
|
"Deliberately restricted in this test to exercise submitter access."
|
|
}
|
|
)
|
|
|
|
conn = log_in_account(conn, submitter)
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(view, "#finding-title", finding.title)
|
|
end
|
|
|
|
test "the lifecycle timeline lists first-seen and check events", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
reviewer = reviewer_account_fixture()
|
|
conn = log_in_account(conn, reviewer)
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(view, "#finding-timeline-first-seen", "First seen")
|
|
refute has_element?(view, "li[id^='finding-timeline-check-']")
|
|
refute has_element?(view, "#finding-timeline-verified")
|
|
refute has_element?(view, "#finding-timeline-vendor-notified")
|
|
|
|
view
|
|
|> form("#finding-verdict-form", %{
|
|
"notes" => "Reproduced against the pinned commit with the documented payload."
|
|
})
|
|
|> render_submit(%{"verdict" => "confirmed"})
|
|
|
|
assert has_element?(view, "#finding-timeline li[id^='finding-timeline-check-']")
|
|
assert has_element?(view, "#finding-timeline", "@#{reviewer.handle}")
|
|
assert has_element?(view, "#finding-timeline", "confirmed")
|
|
end
|
|
|
|
test "the lifecycle timeline shows verified and vendor events when set", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan =
|
|
repository
|
|
|> scan_fixture(submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
|> publish_scan("public")
|
|
|
|
[finding] = scan.findings
|
|
|
|
conn = log_in_account(conn, reviewer_account_fixture())
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(view, "#finding-timeline-first-seen")
|
|
assert has_element?(view, "#finding-timeline li[id^='finding-timeline-check-']")
|
|
assert has_element?(view, "#finding-timeline-verified", "Verified")
|
|
refute has_element?(view, "#finding-timeline-vendor-notified")
|
|
|
|
view
|
|
|> form("#finding-vendor-form", %{
|
|
"vendor_notification" => %{"notified_on" => "2026-01-15"}
|
|
})
|
|
|> render_submit()
|
|
|
|
assert has_element?(view, "#finding-timeline-vendor-notified", "Vendor notified")
|
|
|
|
canonical =
|
|
Tarakan.Repo.get_by!(Tarakan.Scans.CanonicalFinding, repository_id: repository.id)
|
|
|
|
assert canonical.vendor_notified_at == ~U[2026-01-15 00:00:00.000000Z]
|
|
|
|
view
|
|
|> form("#finding-vendor-form", %{"vendor_notification" => %{"notified_on" => ""}})
|
|
|> render_submit()
|
|
|
|
refute has_element?(view, "#finding-timeline-vendor-notified")
|
|
|
|
assert Tarakan.Repo.get!(Tarakan.Scans.CanonicalFinding, canonical.id).vendor_notified_at ==
|
|
nil
|
|
end
|
|
|
|
test "CWE and CVE badges link out and disclosure metadata renders", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan =
|
|
scan_fixture(repository, submitter, %{
|
|
"findings_json" => findings_json_with_disclosure_fixture()
|
|
})
|
|
|
|
[finding] = scan.findings
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#finding-cwe-badge[href='https://cwe.mitre.org/data/definitions/89.html']",
|
|
"CWE-89"
|
|
)
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#finding-cve-badge[href='https://nvd.nist.gov/vuln/detail/CVE-2026-12345']",
|
|
"CVE-2026-12345"
|
|
)
|
|
|
|
assert has_element?(view, "#finding-reproduction", "Run the payload")
|
|
assert has_element?(view, "#finding-affected-versions", ">= 1.0, < 2.0")
|
|
end
|
|
|
|
test "CWE and CVE badges and disclosure metadata are absent when unset", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
refute has_element?(view, "#finding-cwe-badge")
|
|
refute has_element?(view, "#finding-cve-badge")
|
|
refute has_element?(view, "#finding-reproduction")
|
|
refute has_element?(view, "#finding-affected-versions")
|
|
end
|
|
|
|
test "the vendor notification form is hidden from ordinary members", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
{:ok, anonymous_view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
refute has_element?(anonymous_view, "#finding-vendor-form")
|
|
|
|
conn = log_in_account(conn, submitter)
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
refute has_element?(view, "#finding-vendor-form")
|
|
end
|
|
|
|
test "an unauthorized vendor notification event is rejected", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
submitter: submitter
|
|
} do
|
|
scan = scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})
|
|
[finding] = scan.findings
|
|
|
|
conn = log_in_account(conn, submitter)
|
|
{:ok, view, _html} = live(conn, ~p"/findings/#{finding.public_id}")
|
|
|
|
render_hook(view, "record_vendor_notification", %{
|
|
"vendor_notification" => %{"notified_on" => "2026-01-15"}
|
|
})
|
|
|
|
assert has_element?(view, "#flash-group", "not authorized")
|
|
|
|
canonical =
|
|
Tarakan.Repo.get_by!(Tarakan.Scans.CanonicalFinding, repository_id: repository.id)
|
|
|
|
assert canonical.vendor_notified_at == nil
|
|
end
|
|
|
|
defp findings_json_with_disclosure_fixture do
|
|
Jason.encode!(%{
|
|
"tarakan_scan_format" => 1,
|
|
"findings" => [
|
|
%{
|
|
"file" => "lib/example/module_1.ex",
|
|
"line_start" => 10,
|
|
"line_end" => 15,
|
|
"severity" => "high",
|
|
"title" => "Unsanitized input reaches interpolated query (1)",
|
|
"description" => "String-built SQL executed with request parameters.",
|
|
"reproduction" => "1. Check out the pinned commit\n2. Run the payload",
|
|
"affected_versions" => ">= 1.0, < 2.0",
|
|
"cwe" => "CWE-89",
|
|
"cve" => "CVE-2026-12345"
|
|
}
|
|
]
|
|
})
|
|
end
|
|
|
|
defp publish_scan(scan, visibility) 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
|
|
end
|