# Copyright (C) 2020 by the Georgia Tech Research Institute (GTRI)
# This software may be modified and distributed under the terms of
# the BSD 3-Clause license. See the LICENSE file for details.
defmodule ScannerModule do
@moduledoc """
Scanner scans.
"""
def dependencies(path) do
cwd = File.cwd!()
File.cd!(path)
deps =
File.read!("./mix.lock")
|> Hex.Lockfile.parse!(true)
|> Hex.Encoder.lockfile_json()
File.cd!(cwd)
deps
end
def scan(path) do
cwd = File.cwd!()
File.cd!(path)
start_time = DateTime.utc_now()
project_types_identified = ProjectIdent.get_project_types_identified(path)
mix? = Map.has_key?(project_types_identified, :mix)
node? = Map.has_key?(project_types_identified, :node)
pypi? = Map.has_key?(project_types_identified, :python)
{hex_reports_list, hex_deps_count} = Hex.Scanner.scan(mix?, project_types_identified)
{json_reports_list, yarn_reports_list, npm_deps_count} =
Npm.Scanner.scan(node?, project_types_identified)
{pypi_reports_list, pypi_deps_count} = Pypi.Scanner.scan(pypi?, project_types_identified)
reports = [
hex: hex_reports_list,
node_json: json_reports_list,
node_yarn: yarn_reports_list,
pypi: pypi_reports_list
]
result =
get_report(
start_time,
hex_deps_count + npm_deps_count + pypi_deps_count,
reports,
project_types_identified
)
File.cd!(cwd)
Poison.encode!(result, pretty: true)
end
def get_report(
start_time,
deps_count,
[hex: hex_report, node_json: json_report, node_yarn: yarn_report, pypi: pypi_report] =
reports,
project_types
) do
files =
Enum.map(reports, fn {k, v} -> if !Enum.empty?(v), do: k end)
|> Enum.reject(fn k -> k == nil end)
cond do
# If both package-lock.json and yarn.lock are present, create 2 separate reports
Enum.member?(files, :node_json) && Enum.member?(files, :node_yarn) ->
# json
result_json_list = hex_report ++ json_report
result_yarn_list = hex_report ++ yarn_report
result_json = %{
:state => :complete,
:metadata => %{
repo_count: Enum.count(result_json_list),
dependency_count: deps_count
},
:report => %{
:uuid => UUID.uuid1(),
:repos => result_json_list
}
}
result_yarn = %{
:state => :complete,
:metadata => %{
repo_count: Enum.count(result_yarn_list),
dependency_count: deps_count
},
:report => %{
:uuid => UUID.uuid1(),
:repos => result_yarn_list
}
}
result_json = AnalyzerModule.determine_risk_counts(result_json)
result_yarn = AnalyzerModule.determine_risk_counts(result_yarn)
end_time = DateTime.utc_now()
result = %{
:scan_node_json => result_json,
:scan_node_yarn => result_yarn,
:metadata => %{
:times => %{
start_time: DateTime.to_iso8601(start_time),
end_time: DateTime.to_iso8601(end_time),
duration: DateTime.diff(end_time, start_time)
},
:files => project_types
}
}
result |> Map.put(:scan_node_yarn, result_yarn)
Enum.empty?(project_types) ->
%{:error => "No dependency manifest files were found"}
true ->
result_list = hex_report ++ json_report ++ yarn_report ++ pypi_report
result = %{
:state => :complete,
:metadata => %{
repo_count: Enum.count(result_list),
dependency_count: deps_count
},
:report => %{
:uuid => UUID.uuid1(),
:repos => result_list
},
:files => files
}
result = AnalyzerModule.determine_risk_counts(result)
end_time = DateTime.utc_now()
times = %{
start_time: DateTime.to_iso8601(start_time),
end_time: DateTime.to_iso8601(end_time),
duration: DateTime.diff(end_time, start_time)
}
metadata = Map.put_new(result[:metadata], :times, times)
result |> Map.put(:metadata, metadata)
end
end
end