diff --git a/modules/2-owasp.livemd b/modules/2-owasp.livemd index 02fa5de..4b31bf5 100644 --- a/modules/2-owasp.livemd +++ b/modules/2-owasp.livemd @@ -101,7 +101,7 @@ Notable CWEs included are CWE-259: Use of Hard-coded Password, CWE-327: Broken o _Please uncomment the function call that you believe is correct._ - + ```elixir result = @@ -121,26 +121,7 @@ result = end end -[module_id, question_id] = - "#OWASP:1\ndefmodule PasswordCompare do\n def option_one(password, md5_hash) do\n case :crypto.hash(:md5, password) == md5_hash do\n true -> :entry_granted_op1\n false -> :entry_denied_op1\n end\n end\n\n def option_two(password, bcrypt_salted_hash) do\n case Bcrypt.verify_pass(password, bcrypt_salted_hash) do\n true -> :entry_granted_op2\n false -> :entry_denied_op2\n end\n end\nend\n\n# DO NOT CHANGE CODE ABOVE THIS LINE =========================\n\n# PasswordCompare.option_one(\"users_password\", md5_hash)\n# PasswordCompare.option_two(\"users_password\", bcrypt_salted_hash)" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{"OWASP" => OWASP}[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(OWASP, 1, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) @@ -292,26 +273,7 @@ result = Kino.Input.read(answer) ) -[module_id, question_id] = - "#OWASP:2\nanswer = \n Kino.Input.select(\"Answer\", [\n {:ecto, \"Ecto v2.2.2\"},\n {:nx, \"Nx v0.5.0\"},\n {:plug, \"Plug v1.3.2\"}\n ])\n\nKino.render(answer)\n\nKino.Input.read(answer)" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{"OWASP" => OWASP}[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(OWASP, 2, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) diff --git a/modules/3-ssdlc.livemd b/modules/3-ssdlc.livemd index 94dd24f..276c1e2 100644 --- a/modules/3-ssdlc.livemd +++ b/modules/3-ssdlc.livemd @@ -52,26 +52,7 @@ _Use `System.get_env/1` on line 2._ ```elixir result = super_secret_password = "p@ssw0rd" -[module_id, question_id] = - "# SDLC:1\nsuper_secret_password = \"p@ssw0rd\"" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{"ESCT" => ESCT, "OWASP" => OWASP}[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(SDLC, 1, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) diff --git a/modules/4-graphql.livemd b/modules/4-graphql.livemd index f97b831..19e716c 100644 --- a/modules/4-graphql.livemd +++ b/modules/4-graphql.livemd @@ -76,26 +76,7 @@ result = Kino.Input.read(input) ) -[module_id, question_id] = - "# GRAPHQL:1\n\ninput = Kino.Input.select(\"Choose your answer\", [\n a: \"API6_2019_Mass_Assignment\", \n b: \"API10_2019_Insufficient_Logging_Monitoring\", \n c: \"API3_2019_Excessive_Data_Exposure\",\n d: \"API4_2019_Lack_of_Resources_Rate_Limiting\"\n])\n\nKino.render(input)\n\nKino.Input.read(input)" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{"ESCT" => ESCT, "GRAPHQL" => GRAPHQL, "OWASP" => OWASP}[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(GRAPHQL, 1, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) @@ -141,26 +122,7 @@ result = Kino.Input.read(input) ) -[module_id, question_id] = - "# GRAPHQL:2\n\n# -------------------------------------------------------------\n# Option 1\n#\n# HTTP/2 401 Unauthorized\n# Date: Tues, 16 Aug 2022 21:06:42 GMT\n# …\n# {\n# \t“error”:”token expired”\n# {\n# -------------------------------------------------------------\n# Option 2\n#\n# HTTP/2 200 OK\n# Date: Tues, 16 Aug 2021 22:06:42 GMT\n# …\n# {\n# \t“errors”:[\n# \t\t{\n# \t\t\t“locations”:[\n# \t\t\t{\n# \t\t\t\t“column”:2,\n# \t\t\t\t:line”:2\n# \t\t\t}\n# \t\t\t],\n# \t\t\t“message”: “Parsing failed at\n# \t\t}\n# \t]\n# }\n# --------------------------------------------------------------\n# Option 3\n#\n# HTTP/2 200 OK\n# Date: Tues, 16 Aug 2022 21:06:42 GMT\n# …\n# {\n# \t“error”:”ID token for user 55e4cb07 at org 1234 expired”\n# {\n# ---------------------------------------------------------------\n# Option 4\n#\n# HTTP/2 404 File Not Found\n# Date: Tues, 16 Aug 2022 21:06:42 GMT\n# …\n# {\n# \t“error”:”/www/home/file.txt not found ”\n# {\n# ---------------------------------------------------------------\n\ninput = Kino.Input.select(\"Choose your answer\", [\n none: \"\",\n a: \"Option 1\", \n b: \"Option 2\", \n c: \"Option 3\",\n d: \"Option 4\"\n], default: :none)\n\nKino.render(input)\n\nKino.Input.read(input)" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{"ESCT" => ESCT, "GRAPHQL" => GRAPHQL, "OWASP" => OWASP}[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(GRAPHQL, 2, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) diff --git a/modules/5-elixir.livemd b/modules/5-elixir.livemd index b507a8b..a3afc69 100644 --- a/modules/5-elixir.livemd +++ b/modules/5-elixir.livemd @@ -64,31 +64,7 @@ result = end ) -[module_id, question_id] = - "# ELIXIR_SECURITY:1\nmalicious_user_input = UUID.uuid4()\n\ntry do\n malicious_user_input\n # ONLY CHANGE NEXT LINE\n |> String.to_atom()\nrescue\n e -> e\nend" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{ - "ELIXIR_SECURITY" => ELIXIR_SECURITY, - "GRAPHQL" => GRAPHQL, - "OWASP" => OWASP, - "SDLC" => SDLC - }[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(ELIXIR_SECURITY, 1, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) @@ -263,31 +239,7 @@ result = end ) -[module_id, question_id] = - "# ELIXIR_SECURITY:2\n\ndefmodule SecurityCheck do\n def validate(input, password_hash) do\n case Plug.Crypto.secure_compare(input, password_hash) do\n true -> :ok\n false -> :access_denied\n end\n end\n\n defexception message: \"There was an issue\"\nend\n\npassword = \"some_secure_password_hash\"\nuser_input = \"some_string_which_obviously_isnt_the_same_as_the_password\"\n:ok\n# DO NOT EDIT ANY CODE ABOVE THIS LINE =====================\n\ntry do\n# if SecurityCheck.validate(user_input, password) or raise(SecurityCheck) do :you_let_a_baddie_in end\n# if SecurityCheck.validate(user_input, password) || raise(SecurityCheck) do :you_let_a_baddie_in end\nrescue\n e -> e\nend" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{ - "ELIXIR_SECURITY" => ELIXIR_SECURITY, - "GRAPHQL" => GRAPHQL, - "OWASP" => OWASP, - "SDLC" => SDLC - }[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(ELIXIR_SECURITY, 2, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) @@ -361,31 +313,7 @@ result = :ets.info(secret_table)[:protection] ) -[module_id, question_id] = - "# ELIXIR_SECURITY:3\n\n# ONLY EDIT THIS LINE\nsecret_table = :ets.new(:secret_table, [:public])\n:ets.info(secret_table)[:protection]" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{ - "ELIXIR_SECURITY" => ELIXIR_SECURITY, - "GRAPHQL" => GRAPHQL, - "OWASP" => OWASP, - "SDLC" => SDLC - }[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(ELIXIR_SECURITY, 3, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) diff --git a/modules/6-cookies.livemd b/modules/6-cookies.livemd index 475d367..ec4f5a4 100644 --- a/modules/6-cookies.livemd +++ b/modules/6-cookies.livemd @@ -197,32 +197,7 @@ result = {cookie, binary_part(cookie_name, 0, 6)} ) -[module_id, question_id] = - "# COOKIE_SECURITY:1 \n\ncookie_name = \"CHANGE_ME\"\n\n# Uncomment and change the put_resp_cookie call below\n# conn =\n# Plug.Conn.put_resp_cookie(\n# conn,\n# cookie_name,\n# <<0::8, 42::8>>,\n# domain: ...,\n# path: ...,\n# secure: ...,\n# http_only: ...,\n# same_site: ...\n# )\n\ncookie = \n conn\n |> Plug.Conn.fetch_cookies()\n |> Plug.Conn.get_resp_cookies()\n |> Map.fetch!(cookie_name)\n\n{cookie, binary_part(cookie_name, 0, 6)}" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{ - "COOKIE_SECURITY" => COOKIE_SECURITY, - "ELIXIR_SECURITY" => ELIXIR_SECURITY, - "GRAPHQL" => GRAPHQL, - "OWASP" => OWASP, - "SDLC" => SDLC - }[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(COOKIE_SECURITY, 1, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) diff --git a/modules/7-anti-patterns.livemd b/modules/7-anti-patterns.livemd index b5bc65e..3904a6d 100644 --- a/modules/7-anti-patterns.livemd +++ b/modules/7-anti-patterns.livemd @@ -71,9 +71,7 @@ Penguin.slide([3, 4, 5, 2, 1]) ### Quiz -**What sort method is the module above using?** - -_Uncomment the line with your answer._ +**What sorting algorithm is the module above using?** @@ -92,33 +90,7 @@ result = Kino.Input.read(input) ) -[module_id, question_id] = - "# ANTIPATTERNS:1\n\ninput = Kino.Input.select(\"Answer\", [\n a: \"Bubble Sort\",\n b: \"Merge Sort\",\n c: \"Quick Sort\",\n d: \"Random Sort\"\n])\n\nKino.render(input)\n\nKino.Input.read(input)" - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - -module_id = - case %{ - "ANTIPATTERNS" => ANTIPATTERNS, - "COOKIE_SECURITY" => COOKIE_SECURITY, - "ELIXIR_SECURITY" => ELIXIR_SECURITY, - "GRAPHQL" => GRAPHQL, - "OWASP" => OWASP, - "SDLC" => SDLC - }[String.trim(module_id)] do - nil -> raise "invalid module id: #{module_id}" - module_id -> module_id - end - -question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> id - _ -> raise "invalid question id: #{question_id}" - end - -case GradingClient.check_answer(module_id, question_id, result) do +case GradingClient.check_answer(ANTIPATTERNS, 1, result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) diff --git a/modules/8-cicd.livemd b/modules/8-cicd.livemd index 781b6b4..729ce41 100644 --- a/modules/8-cicd.livemd +++ b/modules/8-cicd.livemd @@ -31,22 +31,25 @@ This module will cover over some of the automated processes you may see in a CI/ Built in Elixir, for Elixir, by NCC Group - this tool will try to determine whether your codebase has a number of web vulnerabilities as well as the insecurites outlined in [Module 5 - Elixir Security](./5-elixir.livemd). - ### Example +### Example + Install [Sobelow](https://sobelow.io/) and add it to your application dependencies or install it by following the instructions https://hexdocs.pm/sobelow/readme.html Scan your project by running the following at a terminal in your project's root directory + ``` $ mix sobelow ``` -As a vulnerability scanner, there are multiple categories of vulnerabilities sobelow is capable of discovering and reporting on. -For instance, there are a number of security issues published on the Common Weakness Enumeration (CWE) site - [CWE's](https://cwe.mitre.org/top25/archive/2022/2022_cwe_top25.html) and on OWASP Top 10 [OWASP Top 10](https://owasp.org/www-project-top-ten/). +As a vulnerability scanner, there are multiple categories of vulnerabilities sobelow is capable of discovering and reporting on. + +For instance, there are a number of security issues published on the Common Weakness Enumeration (CWE) site - [CWE's](https://cwe.mitre.org/top25/archive/2022/2022_cwe_top25.html) and on OWASP Top 10 [OWASP Top 10](https://owasp.org/www-project-top-ten/). Scanning tools like Sobelow identify code patterns that match these issues and report them back to developers/users. - ### Example - -Let's say you are interested finding in places in your application that may be susceptible to injection attacks. +### Example + +Let's say you are interested finding in places in your application that may be susceptible to injection attacks. There are several types of injection. Referring to the CWE list, we see #17 CWE-77 for Command Injection, #25 CWE-94 is Code Injection, and #3 CWE-89 is SQL Injection. If we look at the OWASP Top 10 for 2021, A03:2021-Injection is third on the list. Sobelow has the capability to detect these types of security issues. @@ -59,7 +62,7 @@ Sobelow.CI Sobelow.CI.OS Sobelow.CI.System ``` - + Reference: https://docs.guardrails.io/docs/vulnerabilities/elixir/insecure_use_of_dangerous_function ### Usage diff --git a/modules/grading_client/lib/grading_client/graded_cell.ex b/modules/grading_client/lib/grading_client/graded_cell.ex index d4e7355..2284fe4 100644 --- a/modules/grading_client/lib/grading_client/graded_cell.ex +++ b/modules/grading_client/lib/grading_client/graded_cell.ex @@ -29,35 +29,34 @@ defmodule GradingClient.GradedCell do source_attr = attrs["source"] source = Code.string_to_quoted!(source_attr) - quote do - result = unquote(source) - - [module_id, question_id] = - unquote(source_attr) - |> String.split("\n", parts: 2) - |> hd() - |> String.trim_leading("#") - |> String.split(":", parts: 2) - - module_id = - case unquote(Macro.escape(modules))[String.trim(module_id)] do - nil -> - raise "invalid module id: #{module_id}" + [module_id, question_id] = + String.split(source_attr, "\n", parts: 2) + |> hd() + |> String.trim_leading("#") + |> String.split(":", parts: 2) + + module_id = + case modules[String.trim(module_id)] do + nil -> + raise "invalid module id: #{module_id}" + + module_id -> + module_id + end - module_id -> - module_id - end + question_id = + case Integer.parse(String.trim(question_id)) do + {id, ""} -> + id - question_id = - case Integer.parse(String.trim(question_id)) do - {id, ""} -> - id + _ -> + raise "invalid question id: #{question_id}" + end - _ -> - raise "invalid question id: #{question_id}" - end + quote do + result = unquote(source) - case GradingClient.check_answer(module_id, question_id, result) do + case GradingClient.check_answer(unquote(module_id), unquote(question_id), result) do :correct -> IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()]) @@ -71,7 +70,23 @@ defmodule GradingClient.GradedCell do rescue error -> IO.inspect(error) - {:<<>>, [delimiter: ~s["""]], [attrs["source"] <> "\n"]} + source_attr = attrs["source"] + {:ok, ast} = Code.Fragment.container_cursor_to_quoted(source_attr) + + case ast do + {:__block__, meta, args} -> + { + :__block__, + meta, + Enum.reject(args, fn node -> + match?({:__cursor__, _, _}, node) + end) ++ + [:ok] + } + + _ -> + ast + end end Kino.SmartCell.quoted_to_string(source_ast)