Skip to content

Commit d851ce7

Browse files
authored
Merge pull request #92 from polvalente/pv-feat/add-missing-answers
feat: Add graded cells for all quiz cells
2 parents de6a6b6 + d3b694c commit d851ce7

File tree

6 files changed

+434
-107
lines changed

6 files changed

+434
-107
lines changed

modules/3-ssdlc.livemd

+35-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Mix.install([
55
{:grading_client, path: "#{__DIR__}/grading_client"}
66
])
77

8+
System.put_env("envar_secret", "some-secret-password")
9+
810
:ok
911
```
1012

@@ -45,12 +47,40 @@ A very easy way to prevent secrets being added to files is to access them via En
4547

4648
_Use `System.get_env/1` on line 2._
4749

48-
```elixir
49-
# let's assume there is an environment variable named 'envar_secret'
50-
super_secret_password = "p@ssw0rd"
50+
<!-- livebook:{"attrs":"eyJzb3VyY2UiOiIjIFNETEM6MVxuc3VwZXJfc2VjcmV0X3Bhc3N3b3JkID0gXCJwQHNzdzByZFwiIn0","chunks":null,"kind":"Elixir.GradingClient.GradedCell","livebook_object":"smart_cell"} -->
5151

52-
# DO NOT CHANGE CODE BELOW THIS COMMENT
53-
IO.puts(super_secret_password)
52+
```elixir
53+
result = super_secret_password = "p@ssw0rd"
54+
55+
[module_id, question_id] =
56+
"# SDLC:1\nsuper_secret_password = \"p@ssw0rd\""
57+
|> String.split("\n", parts: 2)
58+
|> hd()
59+
|> String.trim_leading("#")
60+
|> String.split(":", parts: 2)
61+
62+
module_id =
63+
case %{"ESCT" => ESCT, "OWASP" => OWASP}[String.trim(module_id)] do
64+
nil -> raise "invalid module id: #{module_id}"
65+
module_id -> module_id
66+
end
67+
68+
question_id =
69+
case Integer.parse(String.trim(question_id)) do
70+
{id, ""} -> id
71+
_ -> raise "invalid question id: #{question_id}"
72+
end
73+
74+
case GradingClient.check_answer(module_id, question_id, result) do
75+
:correct ->
76+
IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()])
77+
78+
{:incorrect, help_text} when is_binary(help_text) ->
79+
IO.puts([IO.ANSI.red(), "Incorrect: ", IO.ANSI.reset(), help_text])
80+
81+
_ ->
82+
IO.puts([IO.ANSI.red(), "Incorrect.", IO.ANSI.reset()])
83+
end
5484
```
5585

5686
## Making Secret Rotation Easy

modules/4-graphql.livemd

+87-57
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,52 @@ It can also intercept responses to ensure no schema data is being leaked in any
5959

6060
**Which of the OWASP API Security Top 10 2019 issues does disabling introspection queries address?**
6161

62-
_Uncomment the line with your answer._
62+
<!-- livebook:{"attrs":"eyJzb3VyY2UiOiIjIEdSQVBIUUw6MVxuXG5pbnB1dCA9IEtpbm8uSW5wdXQuc2VsZWN0KFwiQ2hvb3NlIHlvdXIgYW5zd2VyXCIsIFtcbiAgYTogXCJBUEk2XzIwMTlfTWFzc19Bc3NpZ25tZW50XCIsIFxuICBiOiBcIkFQSTEwXzIwMTlfSW5zdWZmaWNpZW50X0xvZ2dpbmdfTW9uaXRvcmluZ1wiLCBcbiAgYzogXCJBUEkzXzIwMTlfRXhjZXNzaXZlX0RhdGFfRXhwb3N1cmVcIixcbiAgZDogXCJBUEk0XzIwMTlfTGFja19vZl9SZXNvdXJjZXNfUmF0ZV9MaW1pdGluZ1wiXG5dKVxuXG5LaW5vLnJlbmRlcihpbnB1dClcblxuS2luby5JbnB1dC5yZWFkKGlucHV0KSJ9","chunks":null,"kind":"Elixir.GradingClient.GradedCell","livebook_object":"smart_cell"} -->
6363

6464
```elixir
65-
# answer = :API6_2019_Mass_Assignment
66-
# answer = :API10_2019_Insufficient_Logging_Monitoring
67-
# answer = :API3_2019_Excessive_Data_Exposure
68-
# answer = :API4_2019_Lack_of_Resources_Rate_Limiting
69-
70-
IO.puts(answer)
65+
result =
66+
(
67+
input =
68+
Kino.Input.select("Choose your answer",
69+
a: "API6_2019_Mass_Assignment",
70+
b: "API10_2019_Insufficient_Logging_Monitoring",
71+
c: "API3_2019_Excessive_Data_Exposure",
72+
d: "API4_2019_Lack_of_Resources_Rate_Limiting"
73+
)
74+
75+
Kino.render(input)
76+
Kino.Input.read(input)
77+
)
78+
79+
[module_id, question_id] =
80+
"# 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)"
81+
|> String.split("\n", parts: 2)
82+
|> hd()
83+
|> String.trim_leading("#")
84+
|> String.split(":", parts: 2)
85+
86+
module_id =
87+
case %{"ESCT" => ESCT, "GRAPHQL" => GRAPHQL, "OWASP" => OWASP}[String.trim(module_id)] do
88+
nil -> raise "invalid module id: #{module_id}"
89+
module_id -> module_id
90+
end
91+
92+
question_id =
93+
case Integer.parse(String.trim(question_id)) do
94+
{id, ""} -> id
95+
_ -> raise "invalid question id: #{question_id}"
96+
end
97+
98+
case GradingClient.check_answer(module_id, question_id, result) do
99+
:correct ->
100+
IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()])
101+
102+
{:incorrect, help_text} when is_binary(help_text) ->
103+
IO.puts([IO.ANSI.red(), "Incorrect: ", IO.ANSI.reset(), help_text])
104+
105+
_ ->
106+
IO.puts([IO.ANSI.red(), "Incorrect.", IO.ANSI.reset()])
107+
end
71108
```
72109

73110
## Error Disclosure
@@ -88,58 +125,51 @@ OWASP recommends explicitly defining and enforcing all API response payload sche
88125

89126
**Select the best example of a “good” error message, from the perspective of developer who is writing code that is intended to inform a user (who may or may not be a malicious actor) that the action they have attempted was unsuccessful:**
90127

91-
_Uncomment the item number (1-4) with your answer_
128+
<!-- livebook:{"attrs":"eyJzb3VyY2UiOiIjIEdSQVBIUUw6MlxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiMgT3B0aW9uIDFcbiNcbiMgSFRUUC8yIDQwMSBVbmF1dGhvcml6ZWRcbiMgRGF0ZTogVHVlcywgMTYgQXVnIDIwMjIgMjE6MDY6NDIgR01UXG4jIOKAplxuIyB7XG4jIFx04oCcZXJyb3LigJ064oCddG9rZW4gZXhwaXJlZOKAnVxuIyB7XG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiMgT3B0aW9uIDJcbiNcbiMgSFRUUC8yIDIwMCBPS1xuIyBEYXRlOiBUdWVzLCAxNiBBdWcgMjAyMSAyMjowNjo0MiBHTVRcbiMg4oCmXG4jIHtcbiMgXHTigJxlcnJvcnPigJ06W1xuIyBcdFx0e1xuIyBcdFx0XHTigJxsb2NhdGlvbnPigJ06W1xuIyBcdFx0XHR7XG4jIFx0XHRcdFx04oCcY29sdW1u4oCdOjIsXG4jIFx0XHRcdFx0OmxpbmXigJ06MlxuIyBcdFx0XHR9XG4jIFx0XHRcdF0sXG4jIFx0XHRcdOKAnG1lc3NhZ2XigJ06IOKAnFBhcnNpbmcgZmFpbGVkIGF0XG4jIFx0XHR9XG4jIFx0XVxuIyB9XG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4jIE9wdGlvbiAzXG4jXG4jIEhUVFAvMiAyMDAgT0tcbiMgRGF0ZTogVHVlcywgMTYgQXVnIDIwMjIgMjE6MDY6NDIgR01UXG4jIOKAplxuIyB7XG4jIFx04oCcZXJyb3LigJ064oCdSUQgdG9rZW4gZm9yIHVzZXIgNTVlNGNiMDcgYXQgb3JnIDEyMzQgZXhwaXJlZOKAnVxuIyB7XG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIyBPcHRpb24gNFxuI1xuIyBIVFRQLzIgNDA0IEZpbGUgTm90IEZvdW5kXG4jIERhdGU6IFR1ZXMsIDE2IEF1ZyAyMDIyIDIxOjA2OjQyIEdNVFxuIyDigKZcbiMge1xuIyBcdOKAnGVycm9y4oCdOuKAnS93d3cvaG9tZS9maWxlLnR4dCBub3QgZm91bmQg4oCdXG4jIHtcbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbmlucHV0ID0gS2luby5JbnB1dC5zZWxlY3QoXCJDaG9vc2UgeW91ciBhbnN3ZXJcIiwgW1xuICBub25lOiBcIlwiLFxuICBhOiBcIk9wdGlvbiAxXCIsIFxuICBiOiBcIk9wdGlvbiAyXCIsIFxuICBjOiBcIk9wdGlvbiAzXCIsXG4gIGQ6IFwiT3B0aW9uIDRcIlxuXSwgZGVmYXVsdDogOm5vbmUpXG5cbktpbm8ucmVuZGVyKGlucHV0KVxuXG5LaW5vLklucHV0LnJlYWQoaW5wdXQpIn0","chunks":null,"kind":"Elixir.GradingClient.GradedCell","livebook_object":"smart_cell"} -->
92129

93130
```elixir
94-
# -------------------------------------------------------------
95-
# answer = 1
96-
#
97-
# HTTP/2 401 Unauthorized
98-
# Date: Tues, 16 Aug 2022 21:06:42 GMT
99-
#
100-
# {
101-
# “error”:”token expired”
102-
# {
103-
# -------------------------------------------------------------
104-
# answer = 2
105-
#
106-
# HTTP/2 200 OK
107-
# Date: Tues, 16 Aug 2021 22:06:42 GMT
108-
#
109-
# {
110-
# “errors”:[
111-
# {
112-
# “locations”:[
113-
# {
114-
# “column”:2,
115-
# :line”:2
116-
# }
117-
# ],
118-
# “message”: “Parsing failed at
119-
# }
120-
# ]
121-
# }
122-
# --------------------------------------------------------------
123-
# answer = 3
124-
#
125-
# HTTP/2 200 OK
126-
# Date: Tues, 16 Aug 2022 21:06:42 GMT
127-
#
128-
# {
129-
# “error”:”ID token for user 55e4cb07 at org 1234 expired”
130-
# {
131-
# ---------------------------------------------------------------
132-
# answer = 4
133-
#
134-
# HTTP/2 404 File Not Found
135-
# Date: Tues, 16 Aug 2022 21:06:42 GMT
136-
#
137-
# {
138-
# “error”:”/www/home/file.txt not found ”
139-
# {
140-
# ---------------------------------------------------------------
141-
142-
IO.puts(answer)
131+
result =
132+
(
133+
input =
134+
Kino.Input.select(
135+
"Choose your answer",
136+
[none: "", a: "Option 1", b: "Option 2", c: "Option 3", d: "Option 4"],
137+
default: :none
138+
)
139+
140+
Kino.render(input)
141+
Kino.Input.read(input)
142+
)
143+
144+
[module_id, question_id] =
145+
"# 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)"
146+
|> String.split("\n", parts: 2)
147+
|> hd()
148+
|> String.trim_leading("#")
149+
|> String.split(":", parts: 2)
150+
151+
module_id =
152+
case %{"ESCT" => ESCT, "GRAPHQL" => GRAPHQL, "OWASP" => OWASP}[String.trim(module_id)] do
153+
nil -> raise "invalid module id: #{module_id}"
154+
module_id -> module_id
155+
end
156+
157+
question_id =
158+
case Integer.parse(String.trim(question_id)) do
159+
{id, ""} -> id
160+
_ -> raise "invalid question id: #{question_id}"
161+
end
162+
163+
case GradingClient.check_answer(module_id, question_id, result) do
164+
:correct ->
165+
IO.puts([IO.ANSI.green(), "Correct!", IO.ANSI.reset()])
166+
167+
{:incorrect, help_text} when is_binary(help_text) ->
168+
IO.puts([IO.ANSI.red(), "Incorrect: ", IO.ANSI.reset(), help_text])
169+
170+
_ ->
171+
IO.puts([IO.ANSI.red(), "Incorrect.", IO.ANSI.reset()])
172+
end
143173
```
144174

145175
### Resources

0 commit comments

Comments
 (0)