Giter Site home page Giter Site logo

sobelow's Introduction

Sobelow

Module Version Hex Docs Total Download License Last Updated

Sobelow is a security-focused static analysis tool for Elixir & the Phoenix framework. For security researchers, it is a useful tool for getting a quick view of points-of-interest. For project maintainers, it can be used to prevent the introduction of a number of common vulnerabilities.

Currently Sobelow detects some types of the following security issues:

  • Insecure configuration
  • Known-vulnerable Dependencies
  • Cross-Site Scripting
  • SQL injection
  • Command injection
  • Code execution
  • Denial of Service
  • Directory traversal
  • Unsafe serialization

Potential vulnerabilities are flagged in different colors according to confidence in their insecurity. High confidence is red, medium confidence is yellow, and low confidence is green.

A finding is typically marked "low confidence" if it looks like a function could be used insecurely, but it cannot reliably be determined if the function accepts user-supplied input. i.e. If a finding is marked green, it may be critically insecure, but it will require greater manual validation.

Note: This project is in constant development, and additional vulnerabilities will be flagged as time goes on. If you encounter a bug, or would like to request additional features or security checks, please open an issue!

Table of Contents

Installation

To use Sobelow, you can add it to your application's dependencies.

def deps do
  [
    {:sobelow, "~> 0.13", only: [:dev, :test], runtime: false}
  ]
end

You can also install Sobelow globally by executing the following from the command line:

$ mix escript.install hex sobelow

To install from the master branch, rather than the latest release, the following command can be used:

$ mix escript.install github nccgroup/sobelow

To Use

After installation, the simplest way to scan a Phoenix project is to run the following from the project root:

$ mix sobelow

Options

Note: Any path arguments should be absolute paths, or relative to the application root.

  • --root or -r - Specify the application root directory. Accepts a path argument, e.g. ../my_project.

  • --verbose or -v - Print code snippets and additional finding details.

  • --ignore or -i - Ignore given finding types. Accepts a comma-separated list of module names, e.g. XSS.Raw,Traversal.

  • --ignore-files - Ignore files. Accepts a comma-separated list of file names, e.g. config/prod.exs.

  • --details or -d - Get finding-type details. Accepts a single module name, e.g. Config.CSRF.

  • --all-details - Get details of all finding-types.

  • --private - Skip update checks.

  • --router - Specify router location. This only needs to be used if the router location is non-standard. Accepts a path argument, e.g. my/strange/router.ex.

  • --exit - Return non-zero exit status at or above a confidence threshold of low, medium, or high. Defaults to false which returns a zero exit status

  • --threshold - Return findings at or above a confidence level of low (default), medium, or high.

  • --format or -f - Specify findings output format. Accepts a format, e.g. txt or json.

    Note that options such as --verbose will not work with the json format. All json formatted findings contain a type, file, and line key. Other keys may vary.

  • --quiet - Return a single line indicating number of findings. Otherwise, return no output if there are no findings.

  • --compact - Minimal, single-line findings with output colorised according to confidence.

  • --flycheck - Minimal, single-line findings that are compatible with flycheck-based tooling.

  • --save-config - Generates a configuration file based on command line options. See Configuration Files for more information.

  • --config - Run Sobelow with configuration file. See Configuration Files for more information.

  • --mark-skip-all - Mark all displayed findings as skippable.

  • --clear-skip - Clear configuration created by --mark-skip-all.

  • --skip - Ignore findings that have been marked for skipping. See False Positives for more information.

  • --version - Outputs the current version of Sobelow. This is useful for CI steps or integration with other tools like Salus.

Configuration Files

Sobelow allows users to save frequently used options in a configuration file. For example, if you find yourself constantly running:

$ mix sobelow -i XSS.Raw,Traversal --verbose --exit Low

You can use the --save-config flag to create your .sobelow-conf config file:

$ mix sobelow -i XSS.Raw,Traversal --verbose --exit Low --save-config

This command will create the .sobelow-conf file at the root of your application. You can edit this file directly to make changes.

You can also run the command without any options:

$ mix sobelow --save-config

when you first start out using this package - the generated configuration file will be populated with the default values for each option. (This helps in quickly incorporating this package into a pre-existing codebase.)

Now if you want to run Sobelow with the saved configuration, you can run Sobelow with the --config flag.

$ mix sobelow --config

False Positives

Sobelow favors over-reporting versus under-reporting. As such, you may find a number of false positives in a typical scan. These findings may be individually ignored by adding a # sobelow_skip comment, along with a list of modules, before the function definition.

# sobelow_skip ["Traversal"]
def vuln_func(...) do
  ...
end

When integrating Sobelow into a new project, there can be a large number of false positives. To mark all printed findings as false positives, run sobelow with the --mark-skip-all flag.

Once you have tagged the appropriate findings, run Sobelow with the --skip flag.

$ mix sobelow --skip

While # sobelow_skip comments can only mark function-level findings (and so cannot be used to skip configuration issues), the --mark-skip-all flag can be used to skip any finding type.

Modules

Findings categories are broken up into modules. These modules can then be used to either ignore classes of findings (via the ignore and skip options) or to get vulnerability details (via the details option).

This list, and other helpful information, can be found on the command line:

$ mix help sobelow

Umbrella Apps

In order to run Sobelow against all child apps within an umbrella app with a single command, you can add an alias for sobelow in your root mix.exs file:

defp aliases do
  [
    sobelow: ["cmd mix sobelow"]
  ]
end

If you wish to use configuration files in an umbrella app, create a .sobelow-conf in each child application and use the --config flag.

Updates

When scanning a project, Sobelow will occasionally check for updates, and will print an alert if a new version is available. Sobelow keeps track of the last update-check by creating a .sobelow file in the root of the scanned project.

If this functionality is not desired, the --private flag can be used with the scan.

sobelow's People

Contributors

adrianomitre avatar am-kantox avatar angelikatyborska avatar bwireman avatar cantido avatar danschultzer avatar dmarcoux avatar fastjames avatar gabrielgiordan avatar griffinmb avatar griffinmbncc avatar houllette avatar iarekk avatar jcowgar avatar jherdman avatar kianmeng avatar marpo60 avatar mikaak avatar mmmries avatar nitrino avatar oldhammade avatar pdgonzalez872 avatar prehnra avatar rahiparikh avatar samhstn avatar squaresurf avatar techgaun avatar tmecklem avatar tomciopp avatar veverkap avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sobelow's Issues

Installation docs incomplete

First, great project!

Some feedback though. It would ease the unnecessary learning curve for folks who come across this project and are like "what is phoenix? what is mix? how do I get this thing setup?", etc.

Adding some basics to the docs around how to actually use this thing on a computer, such apt-get install erlang elixir, mix local hex, .... would be very helpful to know ahead of time for initial setup.

config folder not scanned in umbrella projects

If I generate a default phoenix project from scratch
mix phx.new simple
then sobelow finds three problems in the phoenix configuration file config.exs in the first run:

mix sobelow --compact   
[+] Config.CSP: Missing Content-Security-Policy - lib/simple_web/router.ex:9
[+] Config.HTTPS: HTTPS Not Enabled - config/prod.exs:0
[+] Config.CSWH: Cross-Site Websocket Hijacking - lib/simple_web/endpoint.ex:17

But if I generate a default phoenix umbrella project from scratch
mix phx.new complex --umbrella
then sobelow does not find the tree problems in the phoenix configuration file config.exs in the first run.
(Probably because the configuration file is in the root folder of the umbrella project not in the selected app-folder.)

mix sobelow --root apps/complex_web --compact
WARNING: Web directory was not found in the expected location.
This may be a result of non-standard directory structure, or use
of an umbrella project. All files in the "lib" directory were
scanned for vulnerabilities.

{:error, :eisdir} error after upgrade to 0.10.0

I have just upgraded my sobelow version from 0.9.3 -> 0.10.0.

On our CI (on Heroku) returns the following error:

-----> Running test command `MIX_ENV=test mix do format --check-formatted, sobelow --config, test && MIX_ENV=test mix ecto.rollback --all && (cd assets && npm audit --production)`...
Checking Sobelow version...
** (MatchError) no match of right hand side value: {:error, :eisdir}
    lib/sobelow.ex:479: Sobelow.load_ignored_fingerprints/1
    lib/sobelow.ex:128: Sobelow.init_state/2
    lib/sobelow.ex:74: Sobelow.run/0
    (mix 1.10.0) lib/mix/task.ex:330: Mix.Task.run_task/3
    (elixir 1.10.0) lib/enum.ex:783: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir 1.10.0) lib/enum.ex:783: Enum.each/2
    (mix 1.10.0) lib/mix/task.ex:330: Mix.Task.run_task/3
    (mix 1.10.0) lib/mix/cli.ex:82: Mix.CLI.run_task/2
-----> test command `MIX_ENV=test mix do format --check-formatted, sobelow --config, test && MIX_ENV=test mix ecto.rollback --all && (cd assets && npm audit --production)` failed with exit status 1

Locally I can run:
mix sobelow --config no problem.


$ ls -a
.                               .iex.exs                        config                          mix.exs
..                              .sobelow-conf                   coveralls.json                  mix.lock
.elixir_ls                      Procfile                        deps                            phoenix_static_buildpack.config
.env                            README.md                       dump.rdb                        priv
.formatter.exs                  _build                          elixir_buildpack.config         release-task.sh
.git                            app.json                        infra                           test
.gitignore                      assets                          lib                             tmp
$ cat .sobelow-conf
[
  verbose: false,
  private: false,
  skip: true,
  router: "",
  exit: "Low",
  format: "txt",
  out: "",
  threshold: "low",
  ignore: ["Config.CSP", "XSS.Raw", "SQL.Query"],
  ignore_files: [""]
]

Is there an update guide? I couldn't see anything breaking-looking in the changelog ๐Ÿค”

Thanks in advance for any advice.

New Phoenix 1.3 directory structure support

Hi @GriffinMB,

I was in the process of refactoring my Phoenix 1.3-rc app with the latest directory structure changes recently pushed to master phoenixframework/phoenix@20e2826 which currently breaks sobelow.

When running mix sobelow I initially get the message that the router can't be found. When passing the new location of the router lib/my_app_web/router.ex I then get an error message "Could not read /web".

I know you may not want to address this yet until at least another RC is released on Phoenix in case the directory structure is modified again in future commits but just wanted to let you know.

Thanks for the great project!!

Directory Traversal incorrectly marks `send_download(_, {:binary, _}, filename: _` as unsafe due to keyword list arg

Phoenix (at least version 1.3.0) requires the filename keyword list arg when specifying a :binary download. Sobelow incorrectly marks binary send_download calls with that required arguments as a vulnerability.

The Phoenix exception for send_download without a filename option is

** (RuntimeError) :filename option is required when sending binary download

but specifying filename triggers

Directory Traversal in `send_download` - Medium Confidence

Not well configured for umbrella projects

As a developer with a phoenix umbrella application. (mix phx.new example --umbrella).

I would like to use sobelow to test the main web (apps/example_web) directory successfully. (Perhaps an --umbrella flag would be a good thing to have to specify in this scenario).

Current usage:

mix sobelow -r apps/example_web --exit # produces: "cannot find router" warning and "could not read web directory" warning
mix sobelow -r apps/example --exit # produces: "cannot find router" warning and "could not read web directory" warning

I have had better results with

mix sobelow -r apps/example_web --router apps/example_web/lib/example_web/router.ex

but I still get the could not read web directory and can't specify the web directory.

The web directory is hardcoded here: https://github.com/nccgroup/sobelow/blob/master/lib/sobelow.ex#L229
When specifying apps/example_web as the --root, app_name is "example_web" and so "#{app_name}_web" will be "example_web_web" which is wrong.
(The code from lines 240-242 here will also not be correct here).

Would you accept a pr adding the use of an --umbrella flag which would have usage something like the following:

mix sobelow --umbrella

The command would go through each directory in apps and check if it is a phoenix application.
For each of those phoenix applications, we would run mix sobelow, and it would be nice to print out one single report.
It would also facilitate phoenix apps being called example_web.

Error: ranges (first..last) expect both sides to be integers...

When i launch
mix sobelow
i receive this error:
** (ArgumentError) ranges (first..last) expect both sides to be integers, got: 1..{:unquote, [line: 134], [{:+, [line: 134], [{:length, [line: 134], [{:path_args, [line: 134], nil}]}, 3]}]} (elixir 1.10.3) lib/range.ex:63: Range.new/2 lib/sobelow/parse.ex:448: Sobelow.Parse.create_fun_cap/3 lib/sobelow/parse.ex:427: Sobelow.Parse.get_funs_of_type/3 (elixir 1.10.3) lib/macro.ex:410: Macro.do_traverse/4 (elixir 1.10.3) lib/enum.ex:1520: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3 (elixir 1.10.3) lib/macro.ex:416: Macro.do_traverse/4 (elixir 1.10.3) lib/enum.ex:1520: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3 (elixir 1.10.3) lib/macro.ex:396: Macro.do_traverse/4

Add suggestions for fixes

Awesome tool @GriffinMB ๐Ÿ™Œ

What do you think about adding suggestions for how to fix the vulnerabilities flagged i.e.

Use String.to_existing_atom/1 instead of String.to_atom/1 etc ?

I'm happy to raise a PR if you think this is something worth adding.

Consider replacing `@sobelow_skip` because of Elixir 1.4 compiler warnings

We use Elixirโ€™s --warnings-as-errors option on CI, which helps us ship tip-top code. However, in Elixir 1.4, using @sobelow_skip to turn off a Sobelow false-positive check will result in a compiler error. This means our CI runs fail to build. Hereโ€™s the error:

$ mix compile --warnings-as-errors
warning: module attribute @sobelow_skip was set but never used
Compilation failed due to warnings while using the --warnings-as-errors option

One way to get around this is to do _ = @sobelow_skip immediately below the usage, but that is an ugly hack.

@sobelow_skip ["DOS.StringToAtom"]
def vulnerable(foo) do   
  _ = @sobelow_skip
  String.to_atom(foo)
end

I would recommend moving away from @sobelow_skip and consider something else โ€” perhaps a code comment would work?

For reference, Credo also considered this same issue and decided to drop their own module attribute usage for a code comment: https://github.com/rrrene/credo/issues/291

-i not working.

Hi, I'm trying to run sobelow with the "-i" flag (following the example in the https.ex source file) and I'm getting an argument error.

$ mix sobelow -i Config.HTTPS
** (ArgumentError) argument error
:erlang.binary_to_existing_atom("Elixir.Sobelow.Config.HTTPS", :utf8)
lib/sobelow.ex:351: Sobelow.get_mod/1
(elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
lib/sobelow.ex:47: Sobelow.run/0
(mix) lib/mix/task.ex:294: Mix.Task.run_task/3
(mix) lib/mix/cli.ex:58: Mix.CLI.run_task/2

Am I doing something wrong, or is this a bug? Thanks.

Question about how to solve error: `Config.CSP: Missing Content-Security-Policy - High Confidence`

I just created a fresh phx application and added sobelow. I then ran the below:

my-app [my-branch] :> mix sobelow
##############################################
#                                            #
#          Running Sobelow - v0.10.1         #
#  Created by Griffin Byatt - @griffinbyatt  #
#     NCC Group - https://nccgroup.trust     #
#                                            #
##############################################

Config.CSP: Missing Content-Security-Policy - High Confidence
File: lib/my_app_web/router.ex
Pipeline: browser
Line: 9

-----------------------------------------------

Config.HTTPS: HTTPS Not Enabled - High Confidence

-----------------------------------------------

... SCAN COMPLETE ...

Looking at the help, I was pointed to: https://hexdocs.pm/phoenix/Phoenix.Controller.html#put_secure_browser_headers/2

But, I'm not exactly sure how to fix this issue here. After doing some research, there is some discussion that folks either disable the check or implement some in-house solution: https://elixirforum.com/t/working-content-security-policy-for-phoenix-channels/11443

When it comes to security I always defer to public and discussed solutions, since incorrect in-house solutions are usually the ones that end up failing and causing vulnerabilities.

So I have a couple of questions:

  1. Is there set of policies you suggest we use?
plug :put_secure_browser_headers, %{"Content-Security-Policy" => "what should go here?"}
  1. If question 1 is answered, would could I create a patch to add some docs on potential fixes for this issue?

Thank you for your time and thank you for sobelow. This is very helpful and makes you think about things that pay off in the future. Along with credo, it is a must have lib in a lot of people's opinion. โค๏ธ

** (FunctionClauseError) no function clause matching in Sobelow.Utils.binarize_app_name/2

Installation via mix archive.install hex sobelow

From the root of the project: mix sobelow

** (FunctionClauseError) no function clause matching in Sobelow.Utils.binarize_app_name/2

    The following arguments were given to Sobelow.Utils.binarize_app_name/2:

        # 1
        []

        # 2
        {:defmodule, [line: 1], [{:__aliases__, [line: 1], [:OMG, :Umbrella, :MixProject]}, [do: {:__block__, [], [{:use, [line: 2], [{:__aliases__, [line: 2], [:Mix, :Project]}]}, {:def, [line: 4], [{:umbrella_version, [line: 4], nil}, [do: "0.2.0"]]}, {:def, [line: 6], [{:project, [line: 6], nil}, [do: [apps_path: "apps", start_permanent: {:==, [line: 9], [{{:., [line: 9], [{:__aliases__, [line: 9], [:Mix]}, :env]}, [line: 9], []}, :prod]}, deps: {:deps, [line: 10], []}, preferred_cli_env: [coveralls: :test, "coveralls.detail": :test, "coveralls.html": :test, "coveralls.circle": :test, dialyzer: :test], dialyzer: {:dialyzer, [line: 18], []}, test_coverage: [tool: {:__aliases__, [line: 19], [:ExCoveralls]}], aliases: {:aliases, [line: 20], []}, source_url: "https://github.com/omisego/elixir-omg"]]]}, {:defp, [line: 26], [{:deps, [line: 26], nil}, [do: [{:{}, [line: 28], [:distillery, "~> 2.0", [runtime: false]]}, {:{}, [line: 29], [:dialyxir, "~> 1.0.0-rc.6", [only: [:dev, :test], runtime: false]]}, {:{}, [line: 30], [:credo, "~> 1.0.5", [only: [:dev, :test], runtime: false]]}, {:{}, [line: 31], [:excoveralls, "~> 0.11.1", [only: [:test], runtime: false]]}, {:{}, [line: 32], [:licensir, "~> 0.2.0", [only: :dev, runtime: false]]}, {:ex_unit_fixtures, [git: "https://github.com/omisego/ex_unit_fixtures.git", branch: "feature/require_files_not_load", only: [:test]]}, {:{}, [line: 37], [:ex_doc, "~> 0.20.2", [only: :dev, runtime: false]]}, {:statix, "~> 1.1"}, {:appsignal, "~> 1.9"}, {:sentry, "~> 7.0"}, {:libsecp256k1, [git: "https://github.com/InoMurko/libsecp256k1.git", ref: "83d4c91b7b5ad79fdd3c020be8c57ff6e2212780", override: true]}]]]}, {:defp, [line: 48], [{:aliases, [line: 48], nil}, [do: [test: ["test --no-start"], coveralls: ["coveralls --no-start"], "coveralls.html": ["coveralls.html --no-start"], "coveralls.detail": ["coveralls.detail --no-start"], "coveralls.post": ["coveralls.post --no-start"], "ecto.setup": ["ecto.create", "ecto.migrate", "run apps/omg_watcher/priv/repo/seeds.exs"], "ecto.reset": ["ecto.drop", "ecto.setup"]]]]}, {:defp, [line: 60], [{:dialyzer, [line: 60], nil}, [do: [flags: [:specdiffs, :error_handling, :race_conditions, :underspecs, :unknown, :unmatched_returns], ignore_warnings: "dialyzer.ignore-warnings", list_unused_filters: true, plt_add_apps: {:plt_apps, [line: 65], []}]]]}, {:defp, [line: 69], [{:plt_apps, [line: 69], nil}, [do: [:briefly, :cowboy, :distillery, :ex_unit, :exexec, :fake_server, :iex, :jason, :mix, :plug, :propcheck, :proper, :ranch, :sentry, :vmstats, :statix]]]}]}]]}

    Attempted function clauses (showing 3 out of 3):

        defp binarize_app_name(app_name, _) when is_binary(app_name)
        defp binarize_app_name(app_name, _) when is_atom(app_name)
        defp binarize_app_name({:@, _, [{module_attribute, _, _}]}, ast)

    lib/sobelow/utils.ex:70: Sobelow.Utils.binarize_app_name/2
    lib/sobelow.ex:31: Sobelow.run/0
    (mix) lib/mix/task.ex:316: Mix.Task.run_task/3
    (mix) lib/mix/cli.ex:79: Mix.CLI.run_task/2

send_download not always captured

If we have a module which calls Phoenix.Controller.send_download as opposed to just send_download, it is not picked up.

The bug is captured by adding the following failing test in here: test/traversal/send_download_test.exs

test "vulnerable Phoenix.Controller.send_download" do
  func = """
  def index(conn, %{"test" => test}) do
    Phoenix.Controller.send_download(conn, {:file, test})
  end
  """

  {_, ast} = Code.string_to_quoted(func)

  assert SendDownload.parse_def(ast) |> is_vuln?
end

I haven't started investigating how to fix it yet

Possible to stop updating the timestamp in .sobelow file if --private flag is used?

Hey, first thanks for the hard work on this project!

I am trying to integrate sobelow into a git-based workflow, but one problem is the .sobelow file gets changed every time I run sobelow. This means that every time I run my unit tests + sobelow checks, git thinks I've modified .sobelow. It's confusing people who are unfamiliar with sobelow, and they ask why this mysterious file changes every time they run tests.

Is there any way you can update the code to prevent the timestamp changing in the .sobelow file if we use the --private flag?

FunctionClauseError is raised when sobelow encounters a JSON file in the root directory

While trying to scan my Phoenix project, sobelow crashes almost immediately after trying to parse a JSON file in the root directory of my project. Here is the trace:

** (FunctionClauseError) no function clause matching in Sobelow.Utils.extract_opts/1
    lib/sobelow/utils.ex:348: Sobelow.Utils.extract_opts(".alloy-ci.json")
    lib/sobelow/utils.ex:436: Sobelow.Utils.get_pipe_val/3
    (elixir) lib/macro.ex:296: anonymous fn/4 in Macro.do_traverse_args/4
    (elixir) lib/enum.ex:1325: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
    (elixir) lib/macro.ex:262: Macro.do_traverse/4

    (elixir) lib/enum.ex:1325: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
    (elixir) lib/enum.ex:1325: Enum."-map_reduce/3-lists^mapfoldl/2-

I'm running sobelow v0.3.11 installed from hex.

`--exit` option doesnโ€™t work when there are no errors

I have a project that passes Sobelow with no warnings:

$ mix sobelow
##############################################
#                                            #
#          Running Sobelow - v0.3.6          #
#  Created by Griffin Byatt - @griffinbyatt  #
#     NCC Group - https://nccgroup.trust     #
#                                            #
##############################################

... SCAN COMPLETE โ€ฆ
$ echo $? # 0

If I pass the --exit option, it gives me a failure and a 0 exit status:

$ mix sobelow --exit Low
##############################################
#                                            #
#          Running Sobelow - v0.3.6          #
#  Created by Griffin Byatt - @griffinbyatt  #
#     NCC Group - https://nccgroup.trust     #
#                                            #
##############################################

... SCAN COMPLETE ...
** (FunctionClauseError) no function clause matching in System.halt/1
    (elixir) lib/system.ex:466: System.halt(nil)
    (mix) lib/mix/task.ex:294: Mix.Task.run_task/3
    (mix) lib/mix/cli.ex:58: Mix.CLI.run_task/2
    (elixir) lib/code.ex:370: Code.require_file/2
$ echo $? # 1

I would expect this to not error and exit with 0.

`--exit` doesn't change anything

I do currently have no HTTPS configured and mix sobelow as well as mix sobelow --exit do exit with an exit code of 0.

This stops me using sobelow on CI.

Can we have a CHANGELOG.md

Can we have a CHANGELOG.md to keep track of what has changed in each release version?

I want to upgrade for 0.5.1 to 0.6.2, but I don't know if there are breaking changes or extra features, etc.

Preferred CSP Header Causes Test Failure

Using the example good router as an example and a freshly generated Phoenix app, the following error occurs:

  1) test GET / (MyApp.PageControllerTest)
     test/my_app/controllers/page_controller_test.exs:4
     ** (Plug.Conn.InvalidHeaderError) header key is not lowercase: "Content-Security-Policy"
     code: conn = get(conn, "/")
     stacktrace:
       (plug) lib/plug/conn.ex:1604: Plug.Conn.validate_header_key_if_test!/2
       (plug) lib/plug/conn.ex:853: anonymous fn/3 in Plug.Conn.merge_resp_headers/2
       (stdlib) maps.erl:232: :maps.fold_1/3
       (plug) lib/plug/conn.ex:851: Plug.Conn.merge_resp_headers/2
       (my_app) MyApp.Router.browser/2
       (my_app) lib/my_app/router.ex:1: MhApp.Router.__pipe_through0__/1
       (phoenix) lib/phoenix/router.ex:283: Phoenix.Router.__call__/2
       (my_app) lib/my_app/endpoint.ex:1: MyApp.Endpoint.plug_builder_call/2
       (my_app) lib/my_app/endpoint.ex:1: MyApp.Endpoint.call/2
       (phoenix) lib/phoenix/test/conn_test.ex:235: Phoenix.ConnTest.dispatch/5
       test/my_app/controllers/page_controller_test.exs:5: (test)

Feature request: Output captured in file

Currently, the output is printed to stdout, but as part of our CI jobs, it would be easier to capture this as a file to be saved as a build artifact. Could you please provide this?

Issues with `--skip`

Iโ€™m having issues with skipping. This is on v0.3.9. Consider this function:

@sobelow_skip ["DOS.StringToAtom"]
def vulnerable(foo) do   
  String.to_atom(foo)
end

If I tell Sobelow to skip that module on the command line, it works as expected:

$ mix sobelow -i DOS.StringToAtom
##############################################
#                                            #
#          Running Sobelow - v0.3.9          #
#  Created by Griffin Byatt - @griffinbyatt  #
#     NCC Group - https://nccgroup.trust     #
#                                            #
##############################################

... SCAN COMPLETE โ€ฆ

However, with the @sobelow_skip attribute, if I run it with --skip, it fails:

$ mix sobelow --skip             
##############################################
#                                            #
#          Running Sobelow - v0.3.9          #
#  Created by Griffin Byatt - @griffinbyatt  #
#     NCC Group - https://nccgroup.trust     #
#                                            #
##############################################

Unsafe `String.to_atom` - Low Confidence
File: web/plugs/included.ex - vulnerable:45
Variable: foo

... SCAN COMPLETE โ€ฆ

Note that this worked in v0.3.6 and v0.3.7 for me but failed in v0.3.8 and v0.3.9.

Support for non-phoenix projects?

I have a project that receives protobuf-encoded messages from a message broker. Would it be possible to add support for this type of system by noting where data is coming in from an outside system?

I would be happy to help out with this if there is an interest in supporting non-phoenix projects written in Elixir.

Config.HTTPS and Config.Secrets are too limited

Config.HTTPS and Config.Secrets only look in config/prod.exs (HTTPS and Secrets) and config/prod.secret.exs (Secrets). We have three public-facing environments (dit, staging, and production; itโ€™s called production for compatibility with our Rails deploy configurations).

As such, Iโ€™m getting a report on Config.HTTPS even though all three of my public-facing environments have both HTTPS and HSTS configured. I recommend that Config.HTTPS and Config.Secrets have two major changes:

  1. Change to an all-files scan (config/(.+)(?:\.secret)?\.exs) instead of specific files. In reporting, report on each file that has the problem unless the captured part is dev or test.

  2. Allow for configuration of included or excluded files. This second is probably bigger, because other than code decorators, Iโ€™m not seeing any configuration options.

Incorrect Status - Hardcoded Secret - High Confidence

Hello,

I just wanted to report that the tool reports the following as

Hardcoded Secret - High Confidence
File: /config/prod.exs - line 31
Type: password

The corresponding line 31 is the first

config :martide, Martide.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "${DB_USER}",
  password: "${DB_PASSWORD}",
  database: "${DB_NAME}",
  hostname: "${DB_HOST}",
  pool_size: 20,
  loggers: [
    {Ecto.LogEntry, :log, []},
    {ScoutApm.Instruments.EctoLogger, :log, []}
  ]

We use edeliver and locally set environment variables on the server to replace ${DB_USER} with actual values on deploy.

Thanks
Sam

Feature Request: Add a filter on confidence level

We have attached this to our CI/CD process however we have a large amount of low confidence findings. It would be nice if there was a way to filter those out considering in the documentation you included

That is to say, if a finding is marked green, it may be critically insecure, but it will require greater manual validation.

That doesn't seem like it would be a good fit to be shown during automated testing and would likely be better to catch during a manual security review before a release. Is this something you would look at adding for the next version?

error when printing variable in `map.key` format

sobelow throw error when printing variable in format of map.key
Example:

# in lib/my_web/router.ex
# 
# 
# 
# other code

defmodule RunElixir do
  import Plug.Conn

  def init(_opts), do: []

  # sobelow_skip ["RCE.CodeModule"]
  def call(conn, _opts) do
    {res, _} = Code.eval_string(conn.body_params["code"])

    conn
    |> put_status(200)
    |> Phoenix.Controller.json(%{result: Poison.encode!(res)})
  end
end
when run mix sobelow

-----------------------------------------------

DOS.StringToAtom: Unsafe `String.to_atom` - Low Confidence
File: lib/my_web/some_component/some_file.ex
Line: 333
Function: query_rendered_sql_map:331
Variable: col

-----------------------------------------------

RCE.CodeModule: Code Execution in `Code.eval_string` - Low Confidence
File: lib/my_web/router.ex
Line: 240
Function: call:239
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:., [line: 240], [{:conn, [line: 240], nil}, :body_params]} of type Tuple
    (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir) lib/string/chars.ex:22: String.Chars.to_string/1
    lib/sobelow/print.ex:180: Sobelow.Print.finding_variable/1
    lib/sobelow/print.ex:83: Sobelow.Print.do_print_finding_metadata/9
    (elixir) lib/enum.ex:783: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir) lib/enum.ex:783: Enum.each/2
    (elixir) lib/enum.ex:783: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir) lib/enum.ex:783: Enum.each/2

Possibility to handle multiple routers

Hi,

We're starting to use this project, but ran into an issue using the --router flag. Our project is an umbrella, and we have multiple routers for different subapplications. I was wondering if you could add a possibility to add multiple routers, for instance by expanding a path or allowing for multiple instances of the flag. Or if you would be open to a pull request solving this.

Thanks!

FunctionClauseError in Utils.extract_opts/1

What did i do :
Inside an API only phoenix project inside an umbrella app, after calling mix sobelow, we got a couple of hits, then the following error.

** (FunctionClauseError) no function clause matching in Sobelow.Utils.extract_opts/1
    lib/sobelow/utils.ex:158: Sobelow.Utils.extract_opts({:pipe, {{:., [line: 25], [{:__aliases__, [line: 25], [:Plug, :Conn]}, :send_file]}, [line: 25], [200, {:filename, [line: 25], ["categories"]}]}})
    (elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
    lib/sobelow/traversal/send_file.ex:65: Sobelow.Traversal.SendFile.parse_aliased_send_file_def/1
    lib/sobelow/traversal/send_file.ex:52: Sobelow.Traversal.SendFile.parse_send_file_def/1
    lib/sobelow/traversal/send_file.ex:12: Sobelow.Traversal.SendFile.run/2
    (elixir) lib/enum.ex:645: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir) lib/enum.ex:645: Enum.each/2
    (elixir) lib/enum.ex:645: Enum."-each/2-lists^foreach/1-0-"/2

It does not happen in a phoenix project with html stuff, for what it is worth, in the same umbrella.

I can sadly not disclose the code of the app in question.

Environnement :
macOS Sierra 10.12.4
elixir 1.4.2
erlang/OTP 19.3
Phoenix 1.2.1

AST parser displays incorrect line-number

sobelow reports the line number of the function signature of the variable in question.

Sample Code

defp event_properties(direction, event) do
  %{
    :"event_#{direction}_1" => event.a,
    :"event_#{direction}_2" => event.b,
    :"event_#{direction}_3" => event.c,
    :"event_#{direction}_4" => event.d
  }
  end
end

Sample Output

{
            "context": "  defp event_properties(direction, event) do
    %{
      :"event_#{direction}_1" => event.a,
",
            "file": "apps/events.ex",
            "function": "event_properties",
            "line": "57",
            "type": "Unsafe atom interpolation",
            "variable": "direction"
         },
         {
            "context": "  defp event_properties(direction, event) do
    %{
      :"event_#{direction}_1" => event.a,
",
            "file": "apps/events.ex",
            "function": "event_properties",
            "line": "57",
            "type": "Unsafe atom interpolation",
            "variable": "direction"
         },
         {
            "context": "  defp event_properties(direction, event) do
    %{
      :"event_#{direction}_1" => event.a,
",
            "file": "apps/events.ex",
            "function": "event_properties",
            "line": "57",
            "type": "Unsafe atom interpolation",
            "variable": "direction"
         },
         {
            "context": "  defp event_properties(direction, event) do
    %{
      :"event_#{direction}_1" => event.a,
",
            "file": "apps/events.ex",
            "function": "event_properties",
            "line": "57",
            "type": "Unsafe atom interpolation",
            "variable": "direction"
         },

Please note that the sobelow version in sample is the one used in pull request.

Handle configuration merges

With the advent of elixir 1.9 releases, it would be good if sobelow could handle configuration merges:

-----------------------------------------------

Config.HSTS: HSTS Not Enabled - Medium Confidence


HSTS configuration details could not be found in `releases.exs`.

In this case, the following is in prod.exs

force_ssl: [hsts: true],

But it is not present in releases.exs. The reason config merging would be desirable is because if one is using sobelow, it would allow for deduplication of configuration options which currently sobelow forces onto users. Additionally it makes sense for something like this to be an explicit compile time concern and not something someone could come in and edit at runtime via the releases.exs file present in releases.

Error when analysing pipeline

When I run mix sobelow (version 0.9.1) in my Phoenix application (version 1.4.9), I get the following error:

** (FunctionClauseError) no function clause matching in Sobelow.Config.is_vuln_pipeline?/2

    The following arguments were given to Sobelow.Config.is_vuln_pipeline?/2:

        # 1
        {:pipeline, [line: 78], [{:config, [line: 78], nil}, {:pipeline_opts, [line: 78], nil}]}

        # 2
        :csrf

    Attempted function clauses (showing 2 out of 2):

        def is_vuln_pipeline?({:pipeline, _, [_name, [do: block]]}, :csrf)
        def is_vuln_pipeline?({:pipeline, _, [_name, [do: block]]}, :headers)

    lib/sobelow/config.ex:93: Sobelow.Config.is_vuln_pipeline?/2
    (elixir) lib/stream.ex:481: anonymous fn/4 in Stream.filter/2
    (elixir) lib/enum.ex:3325: Enumerable.List.reduce/3
    (elixir) lib/stream.ex:1583: Enumerable.Stream.do_each/4
    (elixir) lib/enum.ex:3023: Enum.each/2
    (elixir) lib/enum.ex:783: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir) lib/enum.ex:783: Enum.each/2
    (elixir) lib/enum.ex:783: Enum."-each/2-lists^foreach/1-0-"/2

This is the relevant part in my router.ex file:

  scope "/" do
    pipe_through :graphql

    forward "/graphiql",
      Absinthe.Plug.GraphiQL,
      schema: AppWeb.Schema,
      analyze_complexity: true,
      max_complexity: @max_complexity,
      interface: :playground,
      pipeline: {__MODULE__, :pipeline}

    forward "/graphql",
      Absinthe.Plug,
      schema: AppWeb.Schema,
      analyze_complexity: true,
      max_complexity: @max_complexity,
      pipeline: {__MODULE__, :pipeline}
  end

  def pipeline(config, pipeline_opts) do
    config
    |> Map.fetch!(:schema_mod)
    |> Pipeline.for_document(pipeline_opts)
    |> Pipeline.insert_after(Resolution, ObjectScopeAuthorization)
  end

The problem is that I have this def pipeline defined, and Sobelow does not expect any other function defined, I guess, since when I remove that function it works fine.

Cannot skip RCE Module

Seems like # sobelow_skip ["RCE"] isn't working for me like the rest. Could this be missed somewhere or do I need to configure it differently? Thanks!

casting foreign keys in ecto changesets

๐Ÿ‘‹

Not particularly phoenix related, but is it possible to catch if foreign keys can be set from params passed in by a user through a controller action? Abusing different ecto casts down the line (up the stack?) allows attackers to modify resources that don't belong to them.

I've seen many phoenix projects do something like this

# controller
def some_action(conn, params) do
  # ...
  Resources.create(params) # or update, or delete
  # ...
end

# schema / changeset
schema "resources" do
  # ...
  belongs_to(:something, Something)
  # ...
end

def changeset(resource, attrs) do
  resource
  |> cast(attrs, [:something_id, ...])
  # or cast_assoc(:something)
  # or put_assoc(:something)
end

# resources context 
def create(attrs) do
  %Resource{}
  |> Resource.changeset(attrs)
  |> Repo.insert()
end

As a possible solution / suggestion:

# don't cast foreign keys in Resource.changeset 
# but pass them in Resources.create

@spec create(attrs, for: pos_integer) :: {:ok, Resource.t} | {:error, Ecto.Changeset.t}
def create(attrs, for: something_id) do
  %Resource{something_id: something_id}
  |> Resource.changeset(attrs)
  |> Repo.insert()
end

# or

@spec create(attrs, for: Something.t) :: {:ok, Resource.t} | {:error, Ecto.Changeset.t}
def create(attrs, for: %Something{id: something_id}) do
  %Resource{something_id: something_id}
  |> Resource.changeset(attrs)
  |> Repo.insert()
end

# controller action then becomes
def some_action(conn, params) do
  # something or something_id are set by some plug (possibly based on user id)
  something = conn.assigns.something
  # or something_id = conn.assigns.something_id
  Resources.create(params, for: something)
  # ...
end

Enhancement to Reporting (JSON)

First of all, I'd like to say thank you for the hard work that went into sobelow.

I've looked into making sobelow accessible through Guardrails.

However, there are some challenges with processing the JSON output format as described below. I would love to fund work on this issue using gitcoin.

1. The findings are not consistently reporting file names.

Only some of the modules report the offending file.

For example:

  "type": "Config.HTTPS: HTTPS Not Enabled"

vs

  "file": "config/config.exs - line 23",
  "key": "secret_key",
  "type": "Config.Secrets: Hardcoded Secret"

vs

  "file": "lib/broadway/server.ex",
  "function": "build_consumer_supervisor_spec:381",
  "type": "DOS.BinToAtom: Unsafe atom interpolation",
  "variable": "name_prefix and key"

2. Some filenames contain the line number, some don't.

There are inconsistencies in how the filenames are reported.

  "file": "config/config.exs - line 23",

vs

  "file": "/lib/broadway/server.ex",

3. The line numbers are not pointing to the offending line.

This has been reported previously (#37), but the decision to point to the function signature makes it difficult to point developers to the right line.

For example:

  "file": "config/config.exs - line 23",

points to:

config :real_world, RealWorldWeb.Guardian,

instead of:

  secret_key: "MDLMflIpKod5YCnkdiY7C4E3ki2rgcAAMwfBl0+vyC5uqJNgoibfQmAh7J3uZWVK",

Conclusion

To conclude, I would suggest that all findings:

  • include a consistent filename
  • include a lineNumber that points to the offending line of code (can be 0 if a config setting is missing)

Note that the function field can remain as is.

@GriffinMB and team I would appreciate your thoughts on that.

Thanks!

Support Phoenix.Controller.html#put_secure_browser_headers/2 method in Sobelow.Config.CSP check

Hi! I've just started to use Sobelow in my Phoenix application and got confused about CSP check. Before adding any additional headers I ran mix sobelow --config and got following failure:

Missing Content-Security-Policy - High Confidence
Pipeline: browser:5


pipeline(:browser) do
  plug(:accepts, ["html"])
  plug(:fetch_session)
  plug(:fetch_flash)
  plug(:protect_from_forgery)
  plug(:put_secure_browser_headers)
end

I decided to go with a separate Plug for CSP and define two security headers Plugs in router.ex:

pipeline(:browser) do
  plug(:accepts, ["html"])
  plug(:fetch_session)
  plug(:fetch_flash)
  plug(:protect_from_forgery)
  plug(:put_secure_browser_headers)
  plug(MyAppWeb.CSPHeader)
end

myapp/lib/myapp_web/plugs/csp_header.ex:

defmodule MyAppWeb.CSPHeader do
  @moduledoc """
  Plug to set Content-Security-Policy with websocket endpoints
  """

  alias Phoenix.Controller
  alias Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    Controller.put_secure_browser_headers(conn, %{
      "content-security-policy" => "\
        connect-src 'self' #{websocket_endpoints(conn)}; \
        default-src 'self';\
        script-src 'self' 'unsafe-inline' 'unsafe-eval';\
        style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com;\
        img-src 'self' 'unsafe-inline' 'unsafe-eval' data:;\
        font-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.gstatic.com data:;\
      "
    })
  end

  defp websocket_endpoints(conn) do
    host = Conn.get_req_header(conn, "host")
    "ws://#{host} wss://#{host}"
  end
end

.sobelow-config:

[
  verbose: true,
  private: false,
  skip: false,
  router: "",
  exit: "Low",
  format: "txt",
  ignore: [""],
  ignore_files: [
    "config/prod.secret.exs"
  ]
]

I thought Sobelow would also check Phoenix.Controller.html#put_secure_browser_headers/2 in my custom plug, but it's not true. I still have a failure.

Is it any rational workaround for such case? Is it make sense to extend Sobelow to support Phoenix.Controller.html#put_secure_browser_headers/2?

Thanks!

Other SQL injection vectors

Right now the only considered vector for SQL injections is Ecto.Adapters.SQL.query/3. Here are some more:

  • since ecto 2.1 each repo defines Repo.query/2 that delegates to the Ecto.Adapters.SQL.query/3 function
  • since ecto 2.1 there's Ecto.Adapters.SQL.stream/4 for building a cursor streams based on a raw queries.

For analysis of repos, you can get a list of all repos using code below, since ecto requires defining the ecto_repos setting in order to have migrations, etc working properly.

Application.loaded_applications()
|> Enum.flat_map(fn {app, _, _} -> Application.get_env(app, :ecto_repos, []) end) 
|> Enum.uniq

Missing sweet_xml vulnerability?

A teammate suggested that we run mix_audit in addition to sobelow. I thought it was unnecessary since sobelow also does patch level verification but to my surprise mix_audit reported CVE-2019-15160 as a vulnerability while sobelow did not. MixAudit actually gets its vulnerability info from dependabot/elixir-security-advisories.

TL;DR I'm wondering if sobelow wants to reach parity with the elixir-security-advisories database or if it has excluded any vulnerabilities for specific reasons.

Usage with umbrella projects

I'm trying to run mix sobelow in my Elixir/Phoenix umbrella project, and I keep getting This does not appear to be a Phoenix application.

Any way to make this work?

Exit status reflects a clean or failed run

Thank you so much for this project, this literally could save me from having to quit my job.

Currently mix sobelow will always exit with a status of 0 even when issues were discovered. This makes using this tool as part of a ci pipeline difficult. This maybe ought to be configurable via a flag that lets you set the severity of issue that would trigger the non-zero exit status.

I'm happy to take a crack at this but the change is going to touch a lot of places as currently each check only returns :ok from Enum.each, also want to get some test coverage in place as well to validate functionality and was wondering if you had considered a strategy for that (a fixture folder with different phoenix apps in it?).

Thanks again!

Protocol.UndefinedError with format json

When running Sobelow in a Phoenix 1.5 umbrella app, I ran into the following error:

$ mix sobelow --root apps/myapp_web --format json
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:., [line: 5], [{:socket, [line: 5], nil}, :assigns]} of type Tuple
    (elixir 1.10.2) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir 1.10.2) lib/string/chars.ex:22: String.Chars.to_string/1
    lib/sobelow/finding_log.ex:58: anonymous fn/2 in Sobelow.FindingLog.format_json/2
    (elixir 1.10.2) lib/enum.ex:1400: anonymous fn/3 in Enum.map/2
    (stdlib 3.12.1) maps.erl:232: :maps.fold_1/3
    (elixir 1.10.2) lib/enum.ex:2127: Enum.map/2
    lib/sobelow/finding_log.ex:58: Sobelow.FindingLog.format_json/2
    (elixir 1.10.2) lib/enum.ex:1396: Enum."-map/2-lists^map/1-0-"/2

This was in a LiveView module, so that may be related.

[Bug] 'String.Chars not implemented' for json format

Hi, I try sobelow in a private repo and I get:

    mix sobelow -v --router --format json
    ##############################################
    #                                            #
    #          Running Sobelow - v0.6.6          #
    #  Created by Griffin Byatt - @griffinbyatt  #
    #     NCC Group - https://nccgroup.trust     #
    #                                            #
    ##############################################

    ** (Protocol.UndefinedError) protocol String.Chars not implemented for {:unquote, [line: 16], [:response_template]}
        (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
        (elixir) lib/string/chars.ex:22: String.Chars.to_string/1
        lib/sobelow/utils.ex:173: Sobelow.Utils.log_json_finding/6
        (elixir) lib/enum.ex:737: Enum."-each/2-lists^foreach/1-0-"/2
        (elixir) lib/enum.ex:737: Enum.each/2
        (elixir) lib/enum.ex:737: Enum."-each/2-lists^foreach/1-0-"/2
        (elixir) lib/enum.ex:737: Enum.each/2
        (elixir) lib/enum.ex:737: Enum."-each/2-lists^foreach/1-0-"/2

Is this some library version mismatch?

    mix --version
    Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]

    Mix 1.6.0 (compiled with OTP 19)

It happens in alpine, ubuntu and osx.

Sobelow.XSS.Raw JSON schema missing `line` key

Hey there,

The mandatory line key appears to be missing from here:

"json" ->

case Sobelow.format() do
      "json" ->
        json_finding = [
          type: finding.type,
          file: finding.filename,
          variable: "#{finding.vuln_variable}",
          template: "#{t_name}"
        ]

I haven't tested this live (in case it is handled elsewhere). I just noticed when I was trying to get a feel for the optional JSON keys various types might present back.

Thanks!

Timeout in version check

It looks like there's currently an outage on for griffinbyatt.com - it takes a full minute for Cloudflare to return a 504 Gateway Timeout on this request. The terminal appears to just hang completely, as this check is performed before anything is written to out to the user. Could Sobelow print some sort of message before the network call to fetch the version so it's clear where it is hanging?

Also it appears that the .sobelow file is updated even if the check fails, I'm not sure if this is intended behavior or not.

Make tests public

Make tests public to make contribution easier, and to improve coverage.

Add `--compact` and `--quiet` options for less output

sobelow is very handy to be used with a git pre-commit hook, however the output is quite "long". It would be great if there were a --quiet option to only show output when there's a "failure", and a --compact option to reduce the vertical line impact on the console.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.