Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
commit
af6077b9c3
455 changed files with 78366 additions and 0 deletions
153
test/tarakan_web/controllers/api/finding_signals_api_test.exs
Normal file
153
test/tarakan_web/controllers/api/finding_signals_api_test.exs
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
defmodule TarakanWeb.API.FindingSignalsApiTest do
|
||||
@moduledoc """
|
||||
The three worker submission endpoints that had no HTTP surface until now:
|
||||
calibrated severity, code embeddings, and cluster detectors.
|
||||
"""
|
||||
use TarakanWeb.ConnCase, async: true
|
||||
|
||||
import Ecto.Query, only: [from: 2]
|
||||
|
||||
alias Tarakan.Accounts.ApiCredentials
|
||||
alias Tarakan.Repo
|
||||
alias Tarakan.Scans.CanonicalFinding
|
||||
|
||||
setup %{conn: conn} do
|
||||
submitter = github_account_fixture()
|
||||
repository = listed_github_repository_fixture(submitter)
|
||||
|
||||
findings =
|
||||
Jason.encode!(%{
|
||||
"tarakan_scan_format" => 1,
|
||||
"findings" => [
|
||||
%{
|
||||
"file" => "lib/vulnerable.ex",
|
||||
"severity" => "critical",
|
||||
"title" => "Command injection #{System.unique_integer([:positive])}",
|
||||
"description" => "User input reaches System.cmd without escaping."
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
scan_fixture(repository, submitter, %{"findings_json" => findings})
|
||||
finding = Repo.one!(from f in CanonicalFinding, where: f.repository_id == ^repository.id)
|
||||
|
||||
reviewer = reviewer_account_fixture()
|
||||
|
||||
{:ok, token, _cred} =
|
||||
ApiCredentials.create(reviewer, %{
|
||||
name: "signals",
|
||||
scopes: ["reviews:read", "reviews:verify"]
|
||||
})
|
||||
|
||||
%{
|
||||
conn: put_req_header(conn, "authorization", "Bearer #{token}"),
|
||||
anon: build_conn(),
|
||||
repository: repository,
|
||||
finding: finding
|
||||
}
|
||||
end
|
||||
|
||||
defp signal_path(repository, finding, suffix) do
|
||||
"/api/github.com/#{repository.owner}/#{repository.name}/findings/#{finding.public_id}/#{suffix}"
|
||||
end
|
||||
|
||||
describe "POST .../severity" do
|
||||
test "stores a rescore beside the submitter's claim", ctx do
|
||||
conn =
|
||||
post(ctx.conn, signal_path(ctx.repository, ctx.finding, "severity"), %{
|
||||
"severity" => "low",
|
||||
"rubric" => "rubric-v1"
|
||||
})
|
||||
|
||||
assert %{"calibrated_severity" => "low"} = json_response(conn, 200)
|
||||
assert Repo.get!(CanonicalFinding, ctx.finding.id).severity == "critical"
|
||||
end
|
||||
|
||||
test "rejects a severity outside the scale", ctx do
|
||||
conn =
|
||||
post(ctx.conn, signal_path(ctx.repository, ctx.finding, "severity"), %{
|
||||
"severity" => "catastrophic",
|
||||
"rubric" => "rubric-v1"
|
||||
})
|
||||
|
||||
assert %{"errors" => %{"calibrated_severity" => _}} = json_response(conn, 422)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST .../embedding" do
|
||||
test "stores the vector and assigns a cluster", ctx do
|
||||
conn =
|
||||
post(ctx.conn, signal_path(ctx.repository, ctx.finding, "embedding"), %{
|
||||
"embedding" => [0.1, 0.9, 0.2],
|
||||
"embedding_model" => "test-embed-v1"
|
||||
})
|
||||
|
||||
assert %{"code_pattern_key" => key} = json_response(conn, 200)
|
||||
assert key =~ ~r/^code:/
|
||||
end
|
||||
|
||||
test "rejects an all-zero vector, which would match everything", ctx do
|
||||
conn =
|
||||
post(ctx.conn, signal_path(ctx.repository, ctx.finding, "embedding"), %{
|
||||
"embedding" => [0.0, 0.0, 0.0],
|
||||
"embedding_model" => "test-embed-v1"
|
||||
})
|
||||
|
||||
assert %{"errors" => %{"embedding" => _}} = json_response(conn, 422)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/patterns/:key/rule" do
|
||||
@rule """
|
||||
rules:
|
||||
- id: tarakan-cmd-injection
|
||||
message: user input reaches System.cmd
|
||||
severity: ERROR
|
||||
languages: [elixir]
|
||||
patterns:
|
||||
- pattern: System.cmd($C, $A)
|
||||
"""
|
||||
|
||||
test "accepts a validated detector and marks it servable", ctx do
|
||||
conn =
|
||||
post(ctx.conn, "/api/patterns/code:abc/rule", %{
|
||||
"rule_yaml" => @rule,
|
||||
"language" => "elixir",
|
||||
"checked_count" => 3,
|
||||
"matched_count" => 2,
|
||||
"matched_finding_ids" => [Ecto.UUID.generate(), Ecto.UUID.generate()]
|
||||
})
|
||||
|
||||
assert %{"servable" => true, "matched_count" => 2} = json_response(conn, 201)
|
||||
end
|
||||
|
||||
test "a detector that matched nothing is stored but not servable", ctx do
|
||||
conn =
|
||||
post(ctx.conn, "/api/patterns/code:abc/rule", %{
|
||||
"rule_yaml" => @rule,
|
||||
"checked_count" => 3,
|
||||
"matched_count" => 0,
|
||||
"matched_finding_ids" => []
|
||||
})
|
||||
|
||||
assert %{"servable" => false, "validated_at" => nil} = json_response(conn, 201)
|
||||
end
|
||||
|
||||
test "rejects a match count with no receipts", ctx do
|
||||
conn =
|
||||
post(ctx.conn, "/api/patterns/code:abc/rule", %{
|
||||
"rule_yaml" => @rule,
|
||||
"checked_count" => 3,
|
||||
"matched_count" => 2,
|
||||
"matched_finding_ids" => []
|
||||
})
|
||||
|
||||
assert %{"errors" => %{"matched_finding_ids" => _}} = json_response(conn, 422)
|
||||
end
|
||||
|
||||
test "anonymous submissions are refused", ctx do
|
||||
conn = post(ctx.anon, "/api/patterns/code:abc/rule", %{"rule_yaml" => @rule})
|
||||
assert json_response(conn, 401)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue