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