Packages

Custom Credo checks for common Elixir/Phoenix anti-patterns and CWE Top 25 security vulnerabilities. Provides 40 comprehensive checks covering code quality, error handling, performance, change risk, telemetry, and security (SQL injection, XSS, path traversal, CSRF, SSRF, hardcoded credentials, etc).

Current section

Files

Jump to
oeditus_credo lib credo check warning hardcoded_value.ex
Raw

lib/credo/check/warning/hardcoded_value.ex

defmodule OeditusCredo.Check.Warning.HardcodedValue do
use Credo.Check,
base_priority: :normal,
category: :warning,
explanations: [
check: """
Hardcoded URLs, IP addresses, and secrets should be moved to configuration.
Hardcoding values makes the code less flexible and can expose sensitive information.
Bad:
api_url = "https://api.example.com"
db_host = "192.168.1.100"
Good:
api_url = Application.get_env(:my_app, :api_url)
db_host = System.get_env("DB_HOST")
""",
params: [
exclude_test_files: "Set to false to check test files"
]
]
@url_pattern ~r/https?:\/\/[^\s"']+/
@ip_pattern ~r/\b(?:\d{1,3}\.){3}\d{1,3}\b/
@doc false
@impl true
def run(%SourceFile{} = source_file, params) do
issue_meta = IssueMeta.for(source_file, params)
exclude_test = Params.get(params, :exclude_test_files, __MODULE__)
if exclude_test and test_file?(source_file.filename) do
[]
else
source_file
|> Credo.Code.prewalk(&traverse(&1, &2, issue_meta))
end
end
@doc false
@impl true
def param_defaults do
[exclude_test_files: true]
end
defp traverse({:<<>>, meta, [string]} = ast, issues, issue_meta) when is_binary(string) do
issues =
cond do
Regex.match?(@url_pattern, string) and not localhost?(string) ->
[issue_for(issue_meta, meta[:line], "hardcoded URL") | issues]
Regex.match?(@ip_pattern, string) and not local_ip?(string) ->
[issue_for(issue_meta, meta[:line], "hardcoded IP address") | issues]
true ->
issues
end
{ast, issues}
end
defp traverse(ast, issues, _issue_meta) do
{ast, issues}
end
defp test_file?(filename) do
String.ends_with?(filename, "_test.exs") or String.contains?(filename, "/test/")
end
defp localhost?(url) do
String.contains?(url, "localhost") or String.contains?(url, "127.0.0.1")
end
defp local_ip?(ip) do
String.starts_with?(ip, "127.") or String.starts_with?(ip, "192.168.") or
String.starts_with?(ip, "10.") or ip == "0.0.0.0"
end
defp issue_for(issue_meta, line_no, type) do
format_issue(
issue_meta,
message: "Avoid #{type} in code, use configuration instead",
trigger: type,
line_no: line_no || 1
)
end
end