leaky_environment.ex (2001B)
1 defmodule Credo.Check.Warning.LeakyEnvironment do 2 use Credo.Check, 3 base_priority: :high, 4 tags: [:controversial], 5 category: :warning, 6 explanations: [ 7 check: """ 8 OS child processes inherit the environment of their parent process. This 9 includes sensitive configuration parameters, such as credentials. To 10 minimize the risk of such values leaking, clear or overwrite them when 11 spawning executables. 12 13 The functions `System.cmd/2` and `System.cmd/3` allow environment variables be cleared by 14 setting their value to `nil`: 15 16 System.cmd("env", [], env: %{"DB_PASSWORD" => nil}) 17 18 """ 19 ] 20 21 @doc false 22 @impl true 23 def run(%SourceFile{} = source_file, params \\ []) do 24 issue_meta = IssueMeta.for(source_file, params) 25 26 Credo.Code.prewalk(source_file, &traverse(&1, &2, issue_meta)) 27 end 28 29 defp traverse({{:., _loc, call}, meta, args} = ast, issues, issue_meta) do 30 case get_forbidden_call(call, args) do 31 nil -> 32 {ast, issues} 33 34 bad -> 35 {ast, issues_for_call(bad, meta, issue_meta, issues)} 36 end 37 end 38 39 defp traverse(ast, issues, _issue_meta) do 40 {ast, issues} 41 end 42 43 defp get_forbidden_call([{:__aliases__, _, [:System]}, :cmd], [_, _]) do 44 "System.cmd/2" 45 end 46 47 defp get_forbidden_call([{:__aliases__, _, [:System]}, :cmd], [_, _, opts]) 48 when is_list(opts) do 49 if Keyword.has_key?(opts, :env) do 50 nil 51 else 52 "System.cmd/3" 53 end 54 end 55 56 defp get_forbidden_call([:erlang, :open_port], [_, opts]) 57 when is_list(opts) do 58 if Keyword.has_key?(opts, :env) do 59 nil 60 else 61 ":erlang.open_port/2" 62 end 63 end 64 65 defp get_forbidden_call(_, _) do 66 nil 67 end 68 69 defp issues_for_call(call, meta, issue_meta, issues) do 70 options = [ 71 message: "When using #{call}, clear or overwrite sensitive environment variables", 72 trigger: call, 73 line_no: meta[:line] 74 ] 75 76 [format_issue(issue_meta, options) | issues] 77 end 78 end