Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/hypatia/cli.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,7 @@ defmodule Hypatia.CLI do
reason: f.reason,
action: to_string(f.action)
}
|> Map.merge(Map.take(f, [:line, :category, :recipe_id, :fix_script, :target]))
|> Map.merge(Map.take(f, [:line, :category, :recipe_id, :fix_script, :target, :advisory]))
end)

results ++ normalized
Expand Down
16 changes: 12 additions & 4 deletions lib/rules/structural_drift.ex
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,12 @@ defmodule Hypatia.Rules.StructuralDrift do
SD004: Detect descriptile files outside their canonical location.
Canonical location is .machine_readable/descriptiles/<file>.a2ml.
The old .machine_readable/6a2/<file>.a2ml directory is retired.
Severity: critical.
Action: move to .machine_readable/descriptiles/.
Severity: medium. This is layout drift, not a security finding, so it
must not block a merge: a repository still on the old layout is not
unsafe. The finding is `advisory: true`, so the CLI never counts it
towards exit 1 at any threshold (as WF026, WF027). The current canon keeps the descriptiles as clauses of the repo
deed, and the A2ML-to-deed conversion is kyX's (standards D312, D313).
Action: move to .machine_readable/descriptiles/ (unchanged for fixers).
"""
def sd004_misplaced_a2ml(repo_path) do
@a2ml_state_files
Expand All @@ -161,8 +165,12 @@ defmodule Hypatia.Rules.StructuralDrift do
%{
rule: "SD004",
file: Path.relative_to(path, repo_path),
severity: :critical,
reason: "Descriptile in retired location -- must be in .machine_readable/descriptiles/",
severity: :medium,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
advisory: true,
reason:
"Descriptile in retired location (layout drift, not a security issue); " <>
"the current canon keeps descriptiles as clauses of the repo deed, " <>
"converted by kyX (standards D312, D313)",
action: :move,
target: ".machine_readable/descriptiles/#{a2ml_file}"
}
Expand Down
17 changes: 17 additions & 0 deletions test/cli_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,23 @@ defmodule Hypatia.CLITest do
assert {[], []} = Hypatia.CLI.report_sets(wf027, %{@base | advisory: false})
assert {^wf027, []} = Hypatia.CLI.report_sets(wf027, %{@base | advisory: true})
end

test "SD004 survives the structural_drift normalizer and never blocks" do
dir = Path.join(System.tmp_dir!(), "hyp-sd004-#{:erlang.unique_integer([:positive])}")
File.mkdir_p!(Path.join(dir, ".machine_readable/6a2"))
on_exit(fn -> File.rm_rf!(dir) end)
File.write!(Path.join(dir, ".machine_readable/6a2/STATE.a2ml"), "[metadata]\n")

sd004 =
dir
|> Hypatia.CLI.collect_findings([:structural_drift])
|> Enum.filter(&(&1.type == "SD004"))

assert [%{advisory: true, severity: "medium"}] = sd004

# Shown at the default threshold, but not in the blocking set.
assert {^sd004, []} = Hypatia.CLI.report_sets(sd004, @base)
end
end

# Regression coverage for the placeholder-message defect.
Expand Down
5 changes: 5 additions & 0 deletions test/structural_drift_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ defmodule Hypatia.Rules.StructuralDriftTest do
assert finding.file == ".machine_readable/6a2/STATE.a2ml"
assert finding.target == ".machine_readable/descriptiles/STATE.a2ml"
assert finding.reason =~ "retired location"
# Layout drift must not read as a blocking security finding.
assert finding.severity == :medium
assert finding.advisory == true
assert finding.reason =~ "not a security issue"
assert finding.reason =~ "deed"
end

test "accepts a descriptile in canonical descriptiles/", %{repo: repo} do
Expand Down
Loading