Packages
metastatic
0.4.1
0.26.0
0.25.0
0.24.1
0.24.0
0.23.0
0.22.2
0.22.1
0.22.0
0.21.3
0.21.2
0.21.1
0.21.0
0.20.3
0.20.2
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Cross-language code meta-model library using unified MetaAST representation. Parse, transform, and translate code across Python, Elixir, Ruby, Erlang, Haskell, and more via a shared three-tuple AST format.
Current section
Files
Jump to
Current section
Files
lib/metastatic/analysis/security/result.ex
defmodule Metastatic.Analysis.Security.Result do
@moduledoc """
Result structure for security vulnerability detection.
Contains information about detected security vulnerabilities,
their severity, and remediation recommendations.
## Fields
- `:has_vulnerabilities?` - Boolean indicating if vulnerabilities were found
- `:vulnerabilities` - List of detected vulnerability details
- `:summary` - Human-readable summary of findings
- `:total_vulnerabilities` - Count of detected vulnerabilities
- `:by_severity` - Map of counts by severity
- `:by_category` - Map of counts by vulnerability category
## Vulnerability Categories
- `:injection` - SQL injection, command injection, XSS
- `:unsafe_deserialization` - pickle.loads, eval, exec
- `:hardcoded_secret` - Passwords, API keys in code
- `:weak_crypto` - MD5, SHA1, weak random
- `:path_traversal` - Unchecked file paths
- `:insecure_protocol` - HTTP instead of HTTPS
## Examples
iex> result = Metastatic.Analysis.Security.Result.new([])
iex> result.has_vulnerabilities?
false
iex> vulns = [%{category: :injection, severity: :critical, description: "test"}]
iex> result = Metastatic.Analysis.Security.Result.new(vulns)
iex> result.has_vulnerabilities?
true
"""
@type vulnerability :: %{
category: category(),
severity: severity(),
description: String.t(),
recommendation: String.t(),
cwe: integer() | nil,
context: term()
}
@type category ::
:injection
| :unsafe_deserialization
| :hardcoded_secret
| :weak_crypto
| :path_traversal
| :insecure_protocol
@type severity :: :critical | :high | :medium | :low
@type t :: %__MODULE__{
has_vulnerabilities?: boolean(),
vulnerabilities: [vulnerability()],
summary: String.t(),
total_vulnerabilities: non_neg_integer(),
by_severity: %{severity() => non_neg_integer()},
by_category: %{category() => non_neg_integer()}
}
defstruct has_vulnerabilities?: false,
vulnerabilities: [],
summary: "No security vulnerabilities detected",
total_vulnerabilities: 0,
by_severity: %{},
by_category: %{}
@doc """
Creates a new result from a list of vulnerabilities.
## Examples
iex> Metastatic.Analysis.Security.Result.new([])
%Metastatic.Analysis.Security.Result{has_vulnerabilities?: false, summary: "No security vulnerabilities detected"}
iex> vulns = [%{category: :injection, severity: :critical, description: "test", recommendation: "fix", cwe: nil, context: nil}]
iex> result = Metastatic.Analysis.Security.Result.new(vulns)
iex> result.has_vulnerabilities?
true
"""
@spec new([vulnerability()]) :: t()
def new([]), do: %__MODULE__{}
def new([_ | _] = vulnerabilities) do
by_severity = count_by_severity(vulnerabilities)
by_category = count_by_category(vulnerabilities)
%__MODULE__{
has_vulnerabilities?: true,
vulnerabilities: vulnerabilities,
summary: build_summary(vulnerabilities, by_severity),
total_vulnerabilities: length(vulnerabilities),
by_severity: by_severity,
by_category: by_category
}
end
@doc """
Creates a result with no vulnerabilities.
## Examples
iex> result = Metastatic.Analysis.Security.Result.no_vulnerabilities()
iex> result.has_vulnerabilities?
false
"""
@spec no_vulnerabilities() :: t()
def no_vulnerabilities do
new([])
end
@doc """
Converts result to JSON-compatible map.
## Examples
iex> result = Metastatic.Analysis.Security.Result.new([])
iex> map = Metastatic.Analysis.Security.Result.to_map(result)
iex> is_map(map)
true
"""
@spec to_map(t()) :: map()
def to_map(%__MODULE__{} = result) do
%{
has_vulnerabilities: result.has_vulnerabilities?,
summary: result.summary,
total_vulnerabilities: result.total_vulnerabilities,
by_severity: result.by_severity,
by_category: result.by_category,
vulnerabilities: result.vulnerabilities
}
end
# Private helpers
defp count_by_severity(vulns) do
Enum.reduce(vulns, %{}, fn %{severity: sev}, acc ->
Map.update(acc, sev, 1, &(&1 + 1))
end)
end
defp count_by_category(vulns) do
Enum.reduce(vulns, %{}, fn %{category: cat}, acc ->
Map.update(acc, cat, 1, &(&1 + 1))
end)
end
defp build_summary(vulns, by_severity) do
total = length(vulns)
severity_summary =
by_severity
|> Enum.sort_by(fn {sev, _} -> severity_order(sev) end, :desc)
|> Enum.map_join(", ", fn {sev, count} ->
"#{count} #{sev}"
end)
"Found #{total} security vulnerability(ies): #{severity_summary}"
end
defp severity_order(:critical), do: 4
defp severity_order(:high), do: 3
defp severity_order(:medium), do: 2
defp severity_order(:low), do: 1
end