{"id":32164893,"url":"https://github.com/gtri/lowendinsight","last_synced_at":"2025-10-21T14:55:34.584Z","repository":{"id":42877472,"uuid":"244650692","full_name":"gtri/lowendinsight","owner":"gtri","description":"LowEndInsight is a simple \"bus-factor\" risk analysis library for Open Source Software that is managed within a Git repository. Provide the git URL and the library will respond with a basic Elixir Map structure report.  Critical feedback is always appreciated.","archived":false,"fork":false,"pushed_at":"2023-01-17T04:44:58.000Z","size":755,"stargazers_count":12,"open_issues_count":10,"forks_count":3,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2025-10-21T14:55:23.047Z","etag":null,"topics":["cybersecurity","elixir-library","risk-assessment","risk-management"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gtri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-03T14:00:40.000Z","updated_at":"2024-03-29T14:31:11.000Z","dependencies_parsed_at":"2023-01-31T06:31:07.859Z","dependency_job_id":null,"html_url":"https://github.com/gtri/lowendinsight","commit_stats":{"total_commits":471,"total_committers":10,"mean_commits":47.1,"dds":0.5201698513800425,"last_synced_commit":"1e3fccf92d4c806f5157480af297b02e3d31425d"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/gtri/lowendinsight","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gtri%2Flowendinsight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gtri%2Flowendinsight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gtri%2Flowendinsight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gtri%2Flowendinsight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gtri","download_url":"https://codeload.github.com/gtri/lowendinsight/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gtri%2Flowendinsight/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280281396,"owners_count":26303708,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cybersecurity","elixir-library","risk-assessment","risk-management"],"created_at":"2025-10-21T14:55:32.359Z","updated_at":"2025-10-21T14:55:34.577Z","avatar_url":"https://github.com/gtri.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LowEndInsight\n\n![build status](https://github.com/gtri/lowendinsight/workflows/default_elixir_ci/badge.svg?branch=develop) ![Hex.pm](https://img.shields.io/hexpm/v/lowendinsight) [![Coverage Status](https://coveralls.io/repos/github/gtri/lowendinsight/badge.svg?branch=develop)](https://coveralls.io/github/gtri/lowendinsight?branch=develop)\n\nCAVEATS: \n\nVersion 0.7.0 includes a quick scan for the presence of a SBOM, either a `bom.xml` for CycloneDX or `*.spdx`/`*spdx.rdf` for SPDX.\n\nVersion 0.6.0 includes breaking changes to the analyze function -\u003e upgrading from 0.5.0 to 0.6.0 will require you to pass in a couple\nextra arguments to the analyze function:\n\n```\nAnalyzerModule.analyze([\"https://github.com/gtri/lowendinsight\",\"https://github.com/gtri/lowendinsight-get\"], \"iex\", DateTime.utc_now(), %{types: true})\n```\n\nIn version 0.6.0 the `DateTime.utc_now()` and new `options` field `%{types: true}` are required.\n\n\u003cimg src=\"lei_bus_128.png\" style=\"float: left;margin-right: 10px;margin-top: 10px;\"\u003e LowEndInsight is a simple \"bus-factor\" risk analysis library for Open\nSource Software which is managed within a Git repository.  Provide the\ngit URL and the library will respond with a basic Elixir Map structure report. (There is a desire to make this a struct.)\n\n**If you are at all concerned about risks associated with upstream\ndependency requirements LowEndInsight can provide valuable, and\nactionable information around the likelihood of critical issues being\nresolved, if ever reported.  For example, a repo with a single\ncontributor isn't necessarily bad, but it should be considered with some\nlevel of risk.  Are you or your organization willing to assume ownership\n(fork) of the repository to resolve issues yourself?  Or if there hasn't\nbeen a commit against the source repository, in some significant\namount of time, can you assume that it is inactive, or just stable?**\n\nWhile, in terms of DevSecOps, we are moving towards automation of vulnerability scanning, this doesn't tell the whole picture.  First problem is that not all vulnerabilities are found/known, nor are all reported.  So perhaps some risk reduction should be applied at the dependency inclusion steps.\n\nAgain, the intent of LowEndInsight isn't to say that any upstream Open\nSource dependency is bad, just that the risks should be smartly weighed,\nand a deeper understanding of the implications should be gained during\nthe decision to use.  LowEndInsight provides a simple mechanism for\ninvestigating and applying basic governance (based on a definition of\nthe tolerance level, which you can easily override) and responds with a useful report for integrating into your existing DevSecOps automation.  Or, you can easily use LowEndInsight as an ad-hoc reporting tool, running it manually as part of an [ADR](https://github.com/joelparkerhenderson/architecture_decision_record).\n\n## Key Metrics\n* **Functional Contributors** - we've found that most projects receive a majority of contributions from one or two contributors.  This highlights a misconception about a project's number of contributors and some notion of health or maturity.  We report both the total number of contributors, and our notion of \"functional contributors\" making it easy to identify the risk.\n\n* **Commit Currency** - we've found that many projects are active, while many are also dormant or stale.  This isn't saying anthing of the quality of the project, but actually highlights potential supply-chain issues, e.g., is the project staying current with upstream dependencies updates\n\n* **SBOM presence** - we've found that adoption of a standard software-bill-of-materials (SBOM) manifest is lagging, especially in smaller projects.  Again, the presence or lack thereof does not indicate quality or maturity, however, it does highlight the need to address provenence and risk management.\n\n* **Recent Commit Change** - we've found that project volatility could indicate project instability, but more likely highlights an active project - though it is very difficult to discern this based on amounts of change, and thus should require more due diligence applied when looking at the stability of a dependency.\n  \n```\n✗ mix lei.analyze https://github.com/facebook/react | jq\n{\n  \"state\": \"complete\",\n  \"report\": {\n    \"uuid\": \"4d1e2b08-7b68-11ea-9ca1-88e9fe666193\",\n    \"repos\": [\n      {\n        \"header\": {\n          \"uuid\": \"4d1dbee8-7b68-11ea-93b9-88e9fe666193\",\n          \"start_time\": \"2020-04-10T20:15:34.912972Z\",\n          \"source_client\": \"mix task\",\n          \"repo\": \"https://github.com/facebook/react\",\n          \"library_version\": \"\",\n          \"end_time\": \"2020-04-10T20:17:28.867848Z\",\n          \"duration\": 114\n        },\n        \"data\": {\n          \"risk\": \"low\",\n          \"results\": {\n            \"top10_contributors\": [\n              {\n                \"name\": \"Paul O’Shannessy\",\n                \"merges\": 959,\n                \"email\": \"paul@oshannessy.com\",\n                \"contributions\": 1777\n              },\n              {\n                \"name\": \"Dan Abramov\",\n                \"merges\": 86,\n                \"email\": \"dan.abramov@gmail.com\",\n                \"contributions\": 1356\n              },\n              {\n                \"name\": \"Sophie Alpert\",\n                \"merges\": 392,\n                \"email\": \"git@sophiebits.com\",\n                \"contributions\": 1265\n              },\n              {\n                \"name\": \"Brian Vaughn\",\n                \"merges\": 101,\n                \"email\": \"bvaughn@fb.com\",\n                \"contributions\": 995\n              },\n              {\n                \"name\": \"Sebastian Markbåge\",\n                \"merges\": 141,\n                \"email\": \"sebastian@calyptus.eu\",\n                \"contributions\": 803\n              },\n              {\n                \"name\": \"Jim Sproch\",\n                \"merges\": 327,\n                \"email\": \"jsproch@fb.com\",\n                \"contributions\": 456\n              },\n              {\n                \"name\": \"Brian Vaughn\",\n                \"merges\": 65,\n                \"email\": \"brian.david.vaughn@gmail.com\",\n                \"contributions\": 363\n              },\n              {\n                \"name\": \"Dominic Gannaway\",\n                \"merges\": 6,\n                \"email\": \"trueadm@users.noreply.github.com\",\n                \"contributions\": 336\n              },\n              {\n                \"name\": \"Pete Hunt\",\n                \"merges\": 126,\n                \"email\": \"floydophone@gmail.com\",\n                \"contributions\": 332\n              },\n              {\n                \"name\": \"Andrew Clark\",\n                \"merges\": 2,\n                \"email\": \"acdlite@fb.com\",\n                \"contributions\": 264\n              }\n            ],\n            \"recent_commit_size_in_percent_of_codebase\": 0.00032,\n            \"large_recent_commit_risk\": \"low\",\n            \"functional_contributors_risk\": \"low\",\n            \"functional_contributors\": 84,\n            \"functional_contributor_names\": [\n              \"yiminghe\",\n              \"Marshall Roch\",\n              \"Flarnie Marchan\",\n              \"Daniel Lo Nigro\",\n              \"Philipp Spieß\",\n              \"Edvin Erikson\",\n              \"Mateusz Burzyński\",\n              \"Pete Hunt\",\n              \"petehunt\",\n              \"Ingvar Stepanyan\",\n              \"Jordan Walke\",\n              \"Jim\",\n              \"chico\",\n              \"Rauno Freiberg\",\n              \"Stefan Dombrowski\",\n              \"Keyan Zhang\",\n              \"Benjamin Woodruff\",\n              \"Nicolas Gallagher\",\n              \"CommitSyncScript\",\n              \"Joe Critchley\",\n              \"Simen Bekkhus\",\n              \"fisherwebdev\",\n              \"Andrey Popp\",\n              \"Lee Byron\",\n              \"Shim Won\",\n              \"Christoph Pojer\",\n              \"Lucas Cordeiro\",\n              \"Bartosz Kaszubowski\",\n              \"Sasha Aickin\",\n              \"Heaven\",\n              \"Charles Marsh\",\n              \"Kohei TAKATA\",\n              \"Cheng Lou\",\n              \"Dustan Kasten\",\n              \"Dominic Gannaway\",\n              \"Ivan Zotov\",\n              \"Sophie Alpert\",\n              \"Josh Duck\",\n              \"Tim Yung\",\n              \"Sunil Pai\",\n              \"Timothy Yung\",\n              \"Thomas Aylott\",\n              \"Isaac Salier-Hellendag\",\n              \"iamchenxin\",\n              \"Raphael Amorim\",\n              \"Brian Vaughn\",\n              \"Jinwoo Oh\",\n              \"Ivan Babak\",\n              \"Nathan Hunzaker\",\n              \"Paul O’Shannessy\",\n              \"Alex Smith\",\n              \"Paul O'Shannessy\",\n              \"Sebastian Markbage\",\n              \"Tom Occhino\",\n              \"Jan Kassens\",\n              \"Kunal Mehta\",\n              \"Luna Ruan\",\n              \"Baraa Hamodi\",\n              \"Christopher Chedeau\",\n              \"Ben Newman\",\n              \"jim\",\n              \"Clement Hoang\",\n              \"Hristo Kanchev\",\n              \"Scott Feeney\",\n              \"Connor McSheffrey\",\n              \"Brandon Dail\",\n              \"Paul Shen\",\n              \"Nate Hunzaker\",\n              \"Vjeux\",\n              \"Jared Forsyth\",\n              \"Eli White\",\n              \"cpojer\",\n              \"Andrew Clark\",\n              \"Toru Kobayashi\",\n              \"Dan\",\n              \"Jeff Morrison\",\n              \"Sebastian Markbåge\",\n              \"Dan Abramov\",\n              \"Rick Beerendonk\",\n              \"Andreas Svensson\",\n              \"Fabio M. Costa\",\n              \"yungsters\",\n              \"Ben Alpert\",\n              \"Jason Quense\"\n            ],\n            \"contributor_risk\": \"low\",\n            \"contributor_count\": 1505,\n            \"commit_currency_weeks\": 0,\n            \"commit_currency_risk\": \"low\",\n            \"sbom_risk\": \"medium\"\n          },\n          \"repo_size\": \"185M\",\n          \"repo\": \"https://github.com/facebook/react\",\n          \"project_types\": {\n            \"node\": [\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/art/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/attribute-behavior/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/concurrent/time-slicing/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/dom/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/eslint/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/eslint/proxy/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/expiration/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/fiber-debugger/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/flight/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/browserify/dev/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/browserify/prod/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/brunch/dev/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/brunch/prod/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/rjs/dev/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/rjs/prod/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/systemjs-builder/dev/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/systemjs-builder/prod/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/webpack-alias/dev/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/webpack-alias/prod/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/webpack/dev/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/packaging/webpack/prod/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/fixtures/ssr/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/create-subscription/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/dom-event-testing-library/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/eslint-plugin-react-hooks/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/jest-mock-scheduler/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/jest-react/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/legacy-events/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-art/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-cache/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-client/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-debug-tools/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-devtools-core/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-devtools-extensions/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-devtools-inline/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-devtools-shared/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-devtools-shared/src/node_modules/react-window/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-devtools-shell/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-devtools/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-dom/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-flight-dom-relay/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-flight-dom-webpack/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-interactions/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-is/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-native-renderer/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-noop-renderer/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-reconciler/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-refresh/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-server/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react-test-renderer/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/react/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/scheduler/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/shared/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/packages/use-subscription/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/scripts/bench/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/scripts/eslint-rules/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/scripts/perf-counters/package.json\",\n              \"/tmp/lei-1586549734-63434-1b1so9e/react/scripts/release/package.json\"\n            ]\n          },\n          \"git\": {\n            \"hash\": \"8e13f099ab0c820c6f97547ad08244340e074266\",\n            \"default_branch\": \"refs/remotes/origin/master\"\n          },\n          \"config\": {\n            \"medium_large_commit_level\": 0.2,\n            \"medium_functional_contributors_level\": 5,\n            \"medium_currency_level\": 26,\n            \"medium_contributor_level\": 5,\n            \"high_large_commit_level\": 0.3,\n            \"high_functional_contributors_level\": 3,\n            \"high_currency_level\": 52,\n            \"high_contributor_level\": 3,\n            \"critical_large_commit_level\": 0.4,\n            \"critical_functional_contributors_level\": 2,\n            \"critical_currency_level\": 104,\n            \"critical_contributor_level\": 2,\n            \"base_temp_dir\": \"/tmp\"\n          }\n        }\n      }\n    ]\n  },\n  \"metadata\": {\n    \"times\": {\n      \"start_time\": \"2020-04-10T20:15:34.901638Z\",\n      \"end_time\": \"2020-04-10T20:17:28.885951Z\",\n      \"duration\": 114\n    },\n    \"risk_counts\": {\n      \"low\": 1\n    },\n    \"repo_count\": 1\n  }\n}{\n  \"state\": \"complete\",\n  \"report\": {\n    \"uuid\": \"caa7f920-aaa3-11ec-9c05-f47b09cc5c9a\",\n    \"repos\": [\n      {\n        \"header\": {\n          \"uuid\": \"caa7c2e8-aaa3-11ec-9481-f47b09cc5c9a\",\n          \"start_time\": \"2022-03-23T12:21:13.234974Z\",\n          \"source_client\": \"mix task\",\n          \"repo\": \"https://github.com/facebook/react\",\n          \"library_version\": \"\",\n          \"end_time\": \"2022-03-23T12:21:39.762485Z\",\n          \"duration\": 26\n        },\n        \"data\": {\n          \"risk\": \"medium\",\n          \"results\": {\n            \"top10_contributors\": [\n              {\n                \"name\": \"Paul O’Shannessy\",\n                \"merges\": 959,\n                \"email\": \"paul@oshannessy.com\",\n                \"contributions\": 1778\n              },\n              {\n                \"name\": \"Dan Abramov\",\n                \"merges\": 86,\n                \"email\": \"dan.abramov@gmail.com\",\n                \"contributions\": 1572\n              },\n              {\n                \"name\": \"Brian Vaughn\",\n                \"merges\": 101,\n                \"email\": \"bvaughn@fb.com\",\n                \"contributions\": 1358\n              },\n              {\n                \"name\": \"Sophie Alpert\",\n                \"merges\": 392,\n                \"email\": \"git@sophiebits.com\",\n                \"contributions\": 1268\n              },\n              {\n                \"name\": \"Sebastian Markbåge\",\n                \"merges\": 143,\n                \"email\": \"sebastian@calyptus.eu\",\n                \"contributions\": 995\n              },\n              {\n                \"name\": \"Andrew Clark\",\n                \"merges\": 0,\n                \"email\": \"git@andrewclark.io\",\n                \"contributions\": 631\n              },\n              {\n                \"name\": \"Jim Sproch\",\n                \"merges\": 327,\n                \"email\": \"jsproch@fb.com\",\n                \"contributions\": 456\n              },\n              {\n                \"name\": \"Dominic Gannaway\",\n                \"merges\": 6,\n                \"email\": \"trueadm@users.noreply.github.com\",\n                \"contributions\": 404\n              },\n              {\n                \"name\": \"Brian Vaughn\",\n                \"merges\": 65,\n                \"email\": \"brian.david.vaughn@gmail.com\",\n                \"contributions\": 363\n              },\n              {\n                \"name\": \"Pete Hunt\",\n                \"merges\": 126,\n                \"email\": \"floydophone@gmail.com\",\n                \"contributions\": 332\n              }\n            ],\n            \"sbom_risk\": \"medium\",\n            \"recent_commit_size_in_percent_of_codebase\": 3e-05,\n            \"large_recent_commit_risk\": \"low\",\n            \"functional_contributors_risk\": \"low\",\n            \"functional_contributors\": 97,\n            \"functional_contributor_names\": [\n              \"Andreas Savvides \u003casavvides@twitter.com\u003e\",\n              \"Pete Hunt \u003cfloydophone@gmail.com\u003e\",\n              \"Fabio M. Costa \u003cfabiomcosta@gmail.com\u003e\",\n              \"Andrew Clark \u003cgit@andrewclark.io\u003e\",\n              \"Sunil Pai \u003cthreepointone@gmail.com\u003e\",\n              \"Jeff Morrison \u003cjeff@anafx.com\u003e\",\n              \"Simen Bekkhus \u003csbekkhus91@gmail.com\u003e\",\n              \"Christoph Pojer \u003cchristoph.pojer@gmail.com\u003e\",\n              \"NE-SmallTown \u003cne_smalltown@163.com\u003e\",\n              \"Ray \u003cray@tomo.im\u003e\",\n              \"Nathan Hunzaker \u003cnate.hunzaker@gmail.com\u003e\",\n              \"Ingvar Stepanyan \u003cme@rreverser.com\u003e\",\n              \"Andreas Svensson \u003candreas@syranide.com\u003e\",\n              \"E-Liang Tan \u003celiang@eliangtan.com\u003e\",\n              \"Dominic Gannaway \u003cdg@domgan.com\u003e\",\n              \"Jan Kassens \u003cjan@kassens.net\u003e\",\n              \"Jared Forsyth \u003cjared@jaredforsyth.com\u003e\",\n              \"Flarnie Marchan \u003cflarnie@users.noreply.github.com\u003e\",\n              \"Dan Abramov \u003cdan.abramov@me.com\u003e\",\n              \"dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e\",\n              \"Cheng Lou \u003cchenglou92@gmail.com\u003e\",\n              \"Marshall Roch \u003cmroch@fb.com\u003e\",\n              \"Toru Kobayashi \u003ckoba0004@gmail.com\u003e\",\n              \"Rick Hanlon \u003crickhanlonii@gmail.com\u003e\",\n              \"Joe Critchley \u003cjoecritch@gmail.com\u003e\",\n              \"Joshua Gross \u003cjoshua.gross@gmail.com\u003e\",\n              \"Sebastian Silbermann \u003csilbermann.sebastian@gmail.com\u003e\",\n              \"Luna Ruan \u003cluna@fb.com\u003e\",\n              \"Ben Newman \u003cbn@cs.stanford.edu\u003e\",\n              \"Sergey Rubanov \u003cchi187@gmail.com\u003e\",\n              \"Shim Won \u003cmarocchino@gmail.com\u003e\",\n              \"Philipp Spiess \u003chello@philippspiess.com\u003e\",\n              \"Sunil Pai \u003cthreepointone@fb.com\u003e\",\n              \"Vjeux \u003cvjeuxx@gmail.com\u003e\",\n              \"Nicolas Gallagher \u003cnicolasgallagher@gmail.com\u003e\",\n              \"Ivan Zotov \u003civanzotov@gmail.com\u003e\",\n              \"Sasha Aickin \u003cxander76@yahoo.com\u003e\",\n              \"Jim Sproch \u003cjsproch@fb.com\u003e\",\n              \"Dominic Gannaway \u003ctrueadm@users.noreply.github.com\u003e\",\n              \"Brian Vaughn \u003cbrian.david.vaughn@gmail.com\u003e\",\n              \"Eli White \u003cgithub@eli-white.com\u003e\",\n              \"Connor McSheffrey \u003cc@conr.me\u003e\",\n              \"Scott Feeney \u003cscott@oceanbase.org\u003e\",\n              \"Stefan Dombrowski \u003csdo451@gmail.com\u003e\",\n              \"Tay Yang Shun \u003ctay.yang.shun@gmail.com\u003e\",\n              \"Paul O’Shannessy \u003cpaul@oshannessy.com\u003e\",\n              \"Brandon Dail \u003caweary@users.noreply.github.com\u003e\",\n              \"Paul Shen \u003cpaul@mnml0.com\u003e\",\n              \"Raphael Amorim \u003crapha850@gmail.com\u003e\",\n              \"Bill Fisher \u003cfisherwebdev@gmail.com\u003e\",\n              \"Rick Beerendonk \u003crick@beerendonk.com\u003e\",\n              \"Daniel Lo Nigro \u003cdaniel@dan.cx\u003e\",\n              \"Andrew Clark \u003cacdlite@fb.com\u003e\",\n              \"Andrew Clark \u003cgithub@andrewclark.io\u003e\",\n              \"Hristo Kanchev \u003christokkanchev@gmail.com\u003e\",\n              \"Luna Ruan \u003clunaris.ruan@gmail.com\u003e\",\n              \"Lee Byron \u003clee@leebyron.com\u003e\",\n              \"salazarm \u003csalazarm@users.noreply.github.com\u003e\",\n              \"iamchenxin \u003ciamchenxin@gmail.com\u003e\",\n              \"Andrey Popp \u003c8mayday@gmail.com\u003e\",\n              \"Timothy Yung \u003cyungsters@gmail.com\u003e\",\n              \"Kohei TAKATA \u003ckt.koheitakata@gmail.com\u003e\",\n              \"Isaac Salier-Hellendag \u003cisaac@fb.com\u003e\",\n              \"Jason Quense \u003cmonastic.panic@gmail.com\u003e\",\n              \"Joseph Savona \u003cjoesavona@fb.com\u003e\",\n              \"Clement Hoang \u003cclement.hoang24@gmail.com\u003e\",\n              \"Sunil Pai \u003cthreepointone@oculus.com\u003e\",\n              \"Brandon Dail \u003cbrandon.dail@formidable.com\u003e\",\n              \"Baraa Hamodi \u003cbhamodi@uwaterloo.ca\u003e\",\n              \"Sebastian Markbåge \u003csebastian@calyptus.eu\u003e\",\n              \"Jinwoo Oh \u003carkist@gmail.com\u003e\",\n              \"Nicolas Gallagher \u003cnecolas@fb.com\u003e\",\n              \"jstejada \u003cjstejada@fb.com\u003e\",\n              \"Jordan Walke \u003cjordojw@gmail.com\u003e\",\n              \"Bartosz Kaszubowski \u003cgosimek@gmail.com\u003e\",\n              \"Keyan Zhang \u003croot@keyanzhang.com\u003e\",\n              \"Moti Zilberman \u003cmotiz88@gmail.com\u003e\",\n              \"Charles Marsh \u003ccharlie@khanacademy.org\u003e\",\n              \"Alex Smith \u003ciqwz@ya.ru\u003e\",\n              \"Rauno Freiberg \u003cfreiberggg@gmail.com\u003e\",\n              \"Tom Occhino \u003ctomocchino@gmail.com\u003e\",\n              \"Sophie Alpert \u003cgit@sophiebits.com\u003e\",\n              \"Lucas Cordeiro \u003cecdb.lucas@gmail.com\u003e\",\n              \"Ivan Babak \u003cbabak.john@gmail.com\u003e\",\n              \"jddxf \u003c740531372@qq.com\u003e\",\n              \"Kunal Mehta \u003ck.mehta@berkeley.edu\u003e\",\n              \"Nikita Lebedev \u003cbloomber111@gmail.com\u003e\",\n              \"Dustan Kasten \u003cdustan.kasten@gmail.com\u003e\",\n              \"Mateusz Burzyński \u003cmateuszburzynski@gmail.com\u003e\",\n              \"Thomas Aylott \u003coblivious@subtlegradient.com\u003e\",\n              \"Dan Abramov \u003cdan.abramov@gmail.com\u003e\",\n              \"Benjamin Woodruff \u003cgithub@benjam.info\u003e\",\n              \"Josh Duck \u003cjosh@fb.com\u003e\",\n              \"Andrew Clark \u003cacdlite@me.com\u003e\",\n              \"Brian Vaughn \u003cbvaughn@fb.com\u003e\",\n              \"Dan Abramov \u003cgaearon@fb.com\u003e\",\n              \"yiminghe \u003cyiminghe@gmail.com\u003e\"\n            ],\n            \"contributor_risk\": \"low\",\n            \"contributor_count\": 1671,\n            \"commit_currency_weeks\": 0,\n            \"commit_currency_risk\": \"low\"\n          },\n          \"repo_size\": \"0\",\n          \"repo\": \"https://github.com/facebook/react\",\n          \"project_types\": {\n            \"node\": [\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/art/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/art/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/attribute-behavior/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/attribute-behavior/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/blocks/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/blocks/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/concurrent/time-slicing/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/concurrent/time-slicing/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/dom/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/dom/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/eslint/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/eslint/proxy/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/eslint/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/expiration/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/expiration/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/fiber-debugger/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/fiber-debugger/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/flight/config/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/flight/loader/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/flight/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/flight/scripts/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/flight/server/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/flight/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/react-14/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/react-14/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/react-15/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/react-15/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/react-16/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/react-16/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/react-17/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/react-17/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/legacy-jsx-runtimes/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/nesting/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/nesting/src/legacy/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/nesting/src/modern/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/browserify/dev/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/browserify/dev/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/browserify/prod/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/browserify/prod/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/brunch/dev/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/brunch/dev/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/brunch/prod/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/brunch/prod/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/rjs/dev/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/rjs/dev/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/rjs/prod/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/rjs/prod/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/systemjs-builder/dev/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/systemjs-builder/dev/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/systemjs-builder/prod/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/systemjs-builder/prod/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/webpack-alias/dev/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/webpack-alias/dev/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/webpack-alias/prod/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/webpack-alias/prod/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/webpack/dev/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/webpack/dev/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/webpack/prod/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/packaging/webpack/prod/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/ssr/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/ssr/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/ssr2/package-lock.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/ssr2/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/fixtures/ssr2/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/create-subscription/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/dom-event-testing-library/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/eslint-plugin-react-hooks/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/jest-mock-scheduler/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/jest-react/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-art/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-cache/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-client/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-debug-tools/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-devtools-core/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-devtools-extensions/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-devtools-inline/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-devtools-shared/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-devtools-shared/src/node_modules/react-window/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-devtools-shell/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-devtools-timeline/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-devtools/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-dom/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-fetch/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-fs/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-interactions/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-is/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-native-renderer/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-noop-renderer/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-pg/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-reconciler/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-refresh/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-server-dom-relay/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-server-dom-webpack/esm/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-server-dom-webpack/npm/esm/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-server-dom-webpack/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-server-native-relay/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-server/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-suspense-test-utils/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react-test-renderer/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/react/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/scheduler/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/shared/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/use-subscription/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/packages/use-sync-external-store/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/scripts/bench/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/scripts/bench/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/scripts/devtools/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/scripts/devtools/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/scripts/eslint-rules/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/scripts/perf-counters/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/scripts/release/package.json\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/scripts/release/yarn.lock\",\n              \"/tmp/lei-1648038073-504683-b6vw7a/react/yarn.lock\"\n            ]\n          },\n          \"git\": {\n            \"hash\": \"de516ca5a635220d0cbe82b8f04003820e3f4072\",\n            \"default_branch\": \"refs/remotes/origin/main\"\n          },\n          \"config\": {\n            \"sbom_risk_level\": \"medium\",\n            \"medium_large_commit_level\": 0.2,\n            \"medium_functional_contributors_level\": 5,\n            \"medium_currency_level\": 26,\n            \"medium_contributor_level\": 5,\n            \"high_large_commit_level\": 0.3,\n            \"high_functional_contributors_level\": 3,\n            \"high_currency_level\": 52,\n            \"high_contributor_level\": 3,\n            \"critical_large_commit_level\": 0.4,\n            \"critical_functional_contributors_level\": 2,\n            \"critical_currency_level\": 104,\n            \"critical_contributor_level\": 2,\n            \"base_temp_dir\": null\n          }\n        }\n      }\n    ]\n  },\n  \"metadata\": {\n    \"times\": {\n      \"start_time\": \"2022-03-23T12:21:13.228056Z\",\n      \"end_time\": \"2022-03-23T12:21:39.803990Z\",\n      \"duration\": 26\n    },\n    \"risk_counts\": {\n      \"medium\": 1\n    },\n    \"repo_count\": 1\n  }\n}\n```\n\nNOTE: that the \"file://\" is also supporting, but presumes that the directory provided\nis a valid Git clone.  Analysis of a file://-based repo will not conclude with the\ndirectory structure being removed.\n\n## Installation\n\n[LowEndInsight available in Hex](https://hex.pm/packages/lowendinsight), the package can be installed\nby adding `lowendinsight` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:lowendinsight, \"~\u003e 0.5\"}\n  ]\nend\n```\n\nNOTE: check hex.pm for the latest version.\n\n## Running\n\nYou either need to have Elixir and Erlang installed locally or possibly\na container to run stuff in.\n\n### Mix Task for Scanning in a Mix-based Project\n\nIt is also possible to drop this in as a library dependency to your Mix-based Elixir project.  Simply add\nthe library to your project's dependencies:\n\n```\ndefp deps do\n  [\n    {:lowendinsight, \"~\u003e 0.5\", except: :prod, runtime: false}\n  ]\nend\n```\n\nThen run `mix deps.get`, and `mix lei.scan`.  This will produce a report for the dependencies (and transitive dependencies) specified in your Mix definition.\n\nYou'll get a full report:\n\n```\n➜  lei_scanner_test mix lei.scan\n{\n  \"state\": \"complete\",\n  \"report\": {\n    \"uuid\": \"3084c312-65ab-11ea-b49b-88e9fe666193\",\n    \"repos\": [\n      {\n        \"header\": {\n          \"uuid\": \"2fc3853a-65ab-11ea-849f-88e9fe666193\",\n          \"start_time\": \"2020-03-14T04:20:47.357888Z\",\n          \"source_client\": \"mix.scan\",\n          \"library_version\": \"\",\n          \"end_time\": \"2020-03-14T04:20:50.335877Z\",\n          \"duration\": 3\n        },\n        \"data\": {\n          \"risk\": \"low\",\n...\n```\n\nIt is also possible to scan against a different repo locally by\npassing the absolute path to the directory where it is cloned:\n\n```\nmix lei.scan /some/path/to/a/git/repo\n```\n### Scanning in a NPM-Based Project\nLowendinsight can now be run against NPM-Based projects. To do so, simply clone the repository and run the following from lowendinsight's root directory.\n```\nmix deps.get\nmix lei.scan /some/path/to/a/git/repo\n```\nIt is important to note that though lowendinsight works on NPM-based projects, scanning still requires a local installation of Mix.\n\nAt the least, the above requires an existing `package.json` file in order to scan the first-degree dependencies of an NPM-based project.  A complete scan of both first-degree and transitive dependencies requires a `package-lock.json` file. In other words, due to how NPM handles dependencies, a complete scan of a repository can only be accomplished if all of its dependencies are listed in `package-lock.json`.\n\nIn the scope of `package.json`, dependencies listed in both `devDependencies` and `dependencies` are scanned. Future iterations of lowendinsight could offer the ability to disable `devDependencies` from being scanned.\n\n### Mix Task for Generating a Dependencies JSON List\n\n```\nmix lei.dependencies /some/path/to/a/Mix-based-project\n```\n\n### Governance/Parameter Configuration\n\nThe library uses a baseline configuration for each of the metrics calculated.  If you want to set your own, all you need to do is add the `:lowendinsight` configuration as mentioned below in the *Configuration* section.  Tuning of these defaults will likely happen over time, as analysis continues to run on a large scale.  The analysis will be made available here soon.\n\n### REPL\n\n```\niex -S mix\n```\n\nThis will get you the `iex` prompt:\n\n```\nErlang/OTP 22 [erts-10.6.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]\n\nInteractive Elixir (1.10.2) - press Ctrl+C to exit (type h() ENTER for help)\niex(1)\u003e AnalyzerModule.analyze \"https://github.com/kitplummer/xmpp4rails\", \"iex\", %{types: false}\n{:ok,\n %{\n   data: %{\n     config: %{\n       base_temp_dir: \"/tmp\",\n       critical_contributor_level: 2,\n       critical_currency_level: 104,\n       critical_functional_contributors_level: 2,\n       critical_large_commit_level: 0.4,\n       high_contributor_level: 3,\n       high_currency_level: 52,\n       high_functional_contributors_level: 3,\n       high_large_commit_level: 0.3,\n       medium_contributor_level: 5,\n       medium_currency_level: 26,\n       medium_functional_contributors_level: 5,\n       medium_large_commit_level: 0.2\n     },\n     git: %{\n       default_branch: \"refs/remotes/origin/master\",\n       hash: \"f47ee5f5ef7fb4dbe3d5d5f54e278ea941cb0332\"\n     },\n     project_types: %{},\n     repo: \"https://github.com/kitplummer/xmpp4rails\",\n     repo_size: \"292K\",\n     results: %{\n       commit_currency_risk: \"critical\",\n       commit_currency_weeks: 584,\n       contributor_count: 1,\n       contributor_risk: \"critical\",\n       functional_contributor_names: [\"Kit Plummer\"],\n       functional_contributors: 1,\n       functional_contributors_risk: \"critical\",\n       large_recent_commit_risk: \"low\",\n       recent_commit_size_in_percent_of_codebase: 0.00368,\n       top10_contributors: [%{\"Kit Plummer\" =\u003e 7}],\n       sbom_risk: \"low\"\n     },\n     risk: \"critical\"\n   },\n   header: %{\n     duration: 0,\n     end_time: \"2020-03-22T23:45:06.563532Z\",\n     library_version: \"\",\n     repo: \"https://github.com/kitplummer/xmpp4rails\",\n     source_client: \"iex\",\n     start_time: \"2020-03-22T23:45:06.078198Z\",\n     uuid: \"28a109ba-6c97-11ea-be3d-88e9fe666193\"\n   }\n }}\n```\n\nHere's the command that you would paste in to the `iex` REPL as an example:\n\n```\nAnalyzerModule.analyze \"https://github.com/kitplummer/xmpp4rails\", \"iex\", %{types: false}\n```\n\n### Docker\n\nYou can pass in this lib and configuration settings, into a base Elixir container.  From the root directory of a clone of this repo run this:\n\n```\ndocker run --rm -v $PWD:/app -w /app -it -e LEI_CRITICAL_CURRENCY_LEVEL=60 elixir:latest bash -c \"mix local.hex;mix deps.get;iex -S mix\"\n```\n\nFrom iex you can access to the library functions.\n\n```\nErlang/OTP 22 [erts-10.6.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]\n\nInteractive Elixir (1.10.0) - press Ctrl+C to exit (type h() ENTER for help)\niex(1)\u003e AnalyzerModule.analyze([\"https://github.com/kitplummer/xmpp4rails\"], \"iex\", DateTime.utc_now(), %{types: true})\n{:ok,\n %{\n   metadata: %{\n     repo_count: 1,\n     risk_counts: %{\"critical\" =\u003e 1},\n     times: %{\n       duration: 1,\n       end_time: \"2020-02-08T04:35:35.109053Z\",\n       start_time: \"2020-02-08T04:35:34.078971Z\"\n     }\n   },\n   report: %{\n     repos: [\n       %{\n...\n}\n```\n\n### Mix Tasks for Analyzing Repos\n\nThere is also an Elixir `mix` task that you can use to access the\n`AnalyzeModule.analyze(url, client)` function.  So if have this repo cloned:\n\n*mix lei.analyze*\n\n```\nmix lei.analyze https://github.com/kitplummer/xmpp4rails | jq\n```\n\nThis will return:\n\n```json\n{\n  \"state\": \"complete\",\n  \"report\": {\n    \"uuid\": \"86ac4538-4a28-11ea-897f-82dd17abe001\",\n    \"repos\": [\n      {\n        \"header\": {\n          \"uuid\": \"86ac38f4-4a28-11ea-89c1-82dd17abe001\",\n          \"start_time\": \"2020-02-08T04:07:29.736126Z\",\n          \"source_client\": \"mix task\",\n...\n}\n```\n\nThere also is a batch/bulk processor:\n\n*mix lei.bulk_analyze*\n\n```\nmix lei.bulk_analyze \"./test/scan_list_test\" | jq\n```\n\nThe expected file is a simple list of URLs, one per line like this:\n\n```\nhttps://github.com/gtri/lowendinsight\nhttps://github.com/gtri/lowendinsight-get\n```\n\n### GitHub Action\nLowendinsight can also be added to a GitHub workflow as an action. In its current state, it works against both NPM and Mix based projects. When run against a GitHub repository, a `.json` file will be generated of the format `lei--Y-m-d--H-M-S.json` and pushed to that repository's root directory by default. This action currently exists in the develop branch. The following is an example usage:\n\n```yaml\nname: LEI\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n    - uses: actions/checkout@master\n      with:\n        persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token\n        fetch-depth: 0 # otherwise, you will fail to push refs to dest repo\n    - name: Generate Report\n      uses: gtri/lowendinsight@gha\n      with:\n        github_token: ${{ secrets.GITHUB_TOKEN }}\n        branch: main\n```\n\n#### Inputs\n\n| name | value | default | description |\n| ---- | ----- | ------- | ----------- |\n| github_token | string | | Token for the repo. Can be passed in using `${{ secrets.GITHUB_TOKEN }}`. |\n| branch | string | 'master' | Destination branch to push changes. |\n| force | boolean | false | Determines if force push is used. |\n| tags | boolean | false | Determines if `--tags` is used. |\n| directory | string | '.' | Directory to change to before pushing. |\n| repository | string | '' | Repository name. Default or empty repository name represents current github repository. If you want to push to other repository, you should make a [personal access token]\n\n#### Privacy\nThis action does not, nor will it ever, collect user data.  Any repository used is Lowendinsight's analysis is cloned and deleted without any information being collected by GTRI nor sent to a third party.\n\n\n### LowEndInsight REST-y API\n\nAlso, there is a sister project that wraps this library and provides an HTTP-based interface.\n\nhttps://github.com/gtri/lowendinsight-get\n\n## Docs\n\nAPI available at: https://hexdocs.pm/lowendinsight/readme.html#content\n\nThis is the library piece of the puzzle.  As mentioned above there is an HTTP API available as well.\n\nThe library is written in Elixir.\n\n`mix docs` will generate static docs available locally within the repo's root, in the `docs/` subdirectory.\n\n### JSON Schema\n\nLowEndInsight makes available the API's schema in JSON form, which can be found in the `schema/` subdirectory. In addition, the schema docs are available in `schema/docs` as Markdown.\n\n### A Note about the metrics used\n* Recent commit size: This is a measure of how large the most recent commit is in relatino to the size of the codebase. The idea being that a large recent commit is much more likely to be bug filled than a relatively small commit.\n* Functional Contributors: A functional contributor is one that contributes above a certain percentage of commits equal to or greater than their \"fair\" share. Simply put, a contributor is counted as a functional contributor if the proportion of their commits to the total commits is greater than or equal to 1 / the total number of committers.  If everyone committed the same amount, everyone would be a functional contributor.\n* Currency is a bit of insight into the activity of the source repo.  This value as a measure of risk, again, isn't to state that the repo is bad. The project simply could be stable. But, it could also mean that the project is unmaintained and that as an attribute of the decision making process around whether to not consume should be considered.\n* `risk` is a top-level key that contains the \"rolled up\" risk, the\n  highest value pulled from any of the discrete analysis items.\n\n### Configuration\n\nLowEndInsight allows for customization of the risk levels, to determine \"low\", \"medium\", \"high\" and \"critical\" acceptance.  The library reads this configuration from config.exs (or dev|test|prod.exs) as seen here, or as providing in environment variables.\n\n```\nconfig :lowendinsight,\n  ## Contributor in terms of discrete users\n  ## NOTE: this currently doesn't discern same user with different email\n  critical_contributor_level:\n    String.to_integer(System.get_env(\"LEI_CRITICAL_CONTRIBUTOR_LEVEL\") || \"2\"),\n  high_contributor_level: System.get_env(\"LEI_HIGH_CONTRIBUTOR_LEVEL\") || 3,\n  medium_contributor_level: System.get_env(\"LEI_CRITICAL_CONTRIBUTOR_LEVEL\") || 5,\n\n  ## Commit currency in weeks - is the project active.  This by itself\n  ## may not indicate anything other than the repo is stable. The reason\n  ## we're reporting it is relative to the likelihood vulnerabilities\n  ## getting fix in a timely manner\n  critical_currency_level:\n    String.to_integer(System.get_env(\"LEI_CRITICAL_CURRENCY_LEVEL\") || \"104\"),\n  high_currency_level: String.to_integer(System.get_env(\"LEI_HIGH_CURRENCY_LEVEL\") || \"52\"),\n  medium_currency_level: String.to_integer(System.get_env(\"LEI_MEDIUM_CURRENCY_LEVEL\") || \"26\"),\n\n  ## Percentage of changes to repo in recent commit - is the codebase\n  ## volatile in terms of quantity of source being changed\n  critical_large_commit_level:\n    String.to_float(System.get_env(\"LEI_CRITICAL_LARGE_COMMIT_LEVEL\") || \"0.40\"),\n  high_large_commit_level:\n    String.to_float(System.get_env(\"LEI_HIGH_LARGE_COMMIT_LEVEL\") || \"0.30\"),\n  medium_large_commit_level:\n    String.to_float(System.get_env(\"LEI_MEDIUM_LARGE_COMMIT_LEVEL\") || \"0.20\"),\n\n  ## Bell curve contributions - if there are 30 contributors\n  ## but 90% of the contributions are from 2...\n  critical_functional_contributors_level:\n    String.to_integer(System.get_env(\"LEI_CRITICAL_FUNCTIONAL_CONTRIBUTORS_LEVEL\") || \"2\"),\n  high_functional_contributors_level:\n    String.to_integer(System.get_env(\"LEI_HIGH_FUNCTIONAL_CONTRIBUTORS_LEVEL\") || \"3\"),\n  medium_functional_contributors_level:\n    String.to_integer(System.get_env(\"LEI_MEDIUM_FUNCTIONAL_CONTRIBUTORS_LEVEL\") || \"5\"),\n\n  ## Jobs per available core for defining max concurrency.  This value\n  ## will be used to set the max_concurrency value.\n  jobs_per_core_max: String.to_integer(System.get_env(\"LEI_JOBS_PER_CORE_MAX\") || \"2\")\n```\n\nTo override with an environment variable you just need to have it set:\n\n```\nLEI_CRITICAL_CURRENCY_PAR_LEVEL=60 mix lei.scan\n```\n\nIf you receive an error in the report with the following (or similar missing environment configuration variable) - the required configuration for LowEndInsight hasn't been made available:\n\n```\ncould not fetch application environment :critical_contributor_level for application :lowendinsight because the application was not loaded/started. If your application depends on :lowendinsight at runtime, make sure to load/start it or list it under :extra_applications in your mix.exs file\n```\n\n## Contributing\n\nThanks for considering, we need your contributions to help this project come to fruition.\n\nHere are some important resources:\n\n  * Bugs? [Issues](https://bitbucket.org/kitplummer/lowendinsight/issues/new) is where to report them\n\n### Style\n\nThe repo includes auto-formatting, please run `mix format` to format to\nthe standard style prescribed by the Elixir project:\n\nhttps://hexdocs.pm/mix/Mix.Tasks.Format.html\nhttps://github.com/christopheradams/elixir_style_guide\n\nCode docs for functions are expected.  Examples are a bonus:\n\nhttps://hexdocs.pm/elixir/writing-documentation.html\n\n### Testing\n\nRequired. Please write ExUnit test for new code you create.\n\nUse `mix test --cover` to verify that you're maintaining coverage.\n\n\n### Github Actions\n\nJust getting this built-out.  But the bitbucket-pipeline config is still\nhere too.\n\n### Submitting changes\n\nPlease send a [Pull Request](https://bitbucket.org/kitplummer/lowendinsight/pull-requests/) with a clear list of what you've done and why. Please follow Elixir coding conventions (above in Style) and make sure all of your commits are atomic (one feature per commit).\n\nAlways write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this:\n\n    $ git commit -m \"A brief summary of the commit\n    \u003e\n    \u003e A paragraph describing what changed and its impact.\"\n\n### JSON Schema\n\nThe JSON schema found in `schema` is and should be used to validate the main analysis interfaces' input and expected outputs. Any modifications in implementations should also be made to the schemas and verified/validated by tests.\n\nThere is an external tool used to do the schema docs conversion: `jsonschema2md -d schema/ -o schema/docs`. If you make a modification to the schema please run the tool to update the docs with the submission.\n\n`jsonschema2md` is a Node.js tool.\n\n## License\n\nBSD 3-Clause.  See https://opensource.org/licenses/BSD-3-Clause or LICENSE file for details.\n\nThere is code in this project [`mixfile.ex` and `encoder.ex`], taken from [mix-deps-json](https://github.com/librariesio/mix-deps-json), that is copyright:\n\nCopyright (c) 2016 Andrew Nesbitt.\n\nBlatant attribution: https://github.com/andrew\n\nAnd licensed with the MIT license.  See the [mix-deps-json](https://github.com/librariesio/mix-deps-json) for more details.\n\nFor a bit of insight into the licensing part of inclusion within another licensed repo, there's [this](https://softwareengineering.stackexchange.com/questions/121998/mit-vs-bsd-vs-dual-license), which is really interesting.\n\nThe logic for pulling the code in versus the project as a dependency is that `mix-deps-json` is really a server I the transitive dependencies aren't worth the weight.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgtri%2Flowendinsight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgtri%2Flowendinsight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgtri%2Flowendinsight/lists"}