Exam 2023-06-12
Here you find sample answers and marking guidelines to the questions on the exam 2023-06-12.
Start by reading through all questions. Peter will not visit the exam. If you find any question unclear, ask the exam administrator (tentavakt in Swedish) to call Peter for clarification.
Max score is 25 points.
- For grade 3, 40% of max score (10 points) is required
- For grade 4, 60% of max score (15 points) is required
- For grade 5, 80% of max score (20 points) is required
You are not allowed to use anything else except:
- The computer you sit at to only answer the questions on this exam
- A dictionary to translate to/from English from/to your native language
- Pen and paper to sketch with (should not be submitted)
Write your answers in either English or Swedish. If you write your answers in Swedish, make sure to not introduce any translation confusement. Write proper sentences (spelling, upper/lower case characters, punctuation, etc.). Answers that do not do this good enough/are vague/are not understandable cannot receive full score on the questions,
Answers that are more or less copies of sample answers given to you or copies of text found somewhere else will be rewarded 0 points. Use your own words to answer the questions.
Good luck!
Client-Side JS
Question 1 (2p)
Question
A webpage should display two numbers and the sum of them. It currently looks like this:
<html>
<head>
<title>Addition</title>
</head>
<body>
<span class="term">5</span> + <span class="term">4</span> = <span class="sum">9</span>
</body>
</html>
Bob is smart and realizes the web browser should be able to compute the sum automatically by using some client-side JavaScript code, so one just needs to change the terms in the HTML code in the future, and the sum will be updated automatically accordingly. So he removes the 9
from the last <span>
element, and in the <head>
element he adds the following:
<script>
function showSum(){
const termSpans = document.querySelectorAll('.term')
const leftTerm = parseInt(termSpans[0].innerText, 10)
const rightTerm = parseInt(termSpans[1].innerText, 10)
const sumSpan = document.querySelector('.sum')
sumSpan.innerText = leftTerm + rightTerm
}
showSum()
</script>
When he tries to run the code, it doesn't work as it should, but crashes. Explain what the problem is, indicate which JavaScript line the code crashes at, and explain how Bob can change the code to make it work as it should (show code for the solution).
Sample answer
Bob's JS code will run before the web browser has parsed the HTML code in the <body>
element. Therefore, termSpans
will be an empty array (NodeList, actually), and not contain any references to elements with the class term
. The code will crash on the following line:
const leftTerm = parseInt(termSpans[0].innerText, 10)
since termSpans[0]
there will evaluate to undefined
, and one can't look up the property innerText
of undefined
.
To fix this problem, the JS code needs to run after the web browser has read all the HTML code. Since the web browser will fire the event DOMContentLoaded
when it's done reading all the HTML code, we can listen for that event and call showSum()
when it happens. So, instead of:
showSum()
Bob can use:
document.addEventListener('DOMContentLoaded', showSum)
Marking guidelines
- 0.66 points for what the problem is
- 0.66 points for correct line where the crash happens
- 0.66 points for explanation of a functional correction
Question 2 (1p)
Question
Name 3 different events that can fire on a webpage, and explain when they fire.
Sample answer
click
: When the user clicks on an elementsubmit
: When the user submits a<form>
DOMContentLoaded
: When the web browser have read/parsed all HTML code on the webpage and is done constructing the Document Object Model
Marking guidelines
- 0.10 points for each correct name
- 0.23 points for each correct explanation
Point deduction for small errors:
- -0.1 points for spelling event name with uppercase letter first
- -0.1 points for event name starting with
on
(that's not part of the name of the event, but if one use theonEVENTNAME
HTML attributes, then one should writeon
before the name of the event) - -0.1 points for having
()
at the end of the event name - -0.1 points for spelling mistake in name
Question 3 (1p)
Question
Give a general explanation of what the method preventDefault()
on the event object does. Then also describe an event where there is a difference between calling it and not calling it.
Sample answer
By calling the method preventDefault()
on the event object you tell the web browser to not do what it usually does when that event happens. For example, when listening for the click
event on an <a>
element, the web browser will by default send an HTTP request to fetch and show the webpage the link leads to, but by calling preventDefault()
on the event object when this event happens, the web browser won't do that.
Marking guidelines
- 0.5 point for general explanation
- 0.5 points for describing an event where it makes a difference to call it
Point deduction for writing incorrect things or missing to mention important details (the amount varies depending on what is incorrect/missing)
Frontend framework
Question 4 (2p)
Question
In a frontend framework of your choice (name which one you choose), implement a component that functions as a counter:
- The one using the component can pass a prop to it (a number) indicating the start value of the counter in the component
- The component should contain a
<button>
the user can click on to increment the counter - The
<button>
should show the current value of the counter
Sample answer
In Svelte:
<script>
export let startValue
let counter = startValue
function incrementCounter(){
counter++
}
</script>
<button on:click={incrementCounter}>{counter}</button>
Marking guidelines
- 1 point for correctly implementing the prop
- 0.25 points correctly showing the counter value in the
<button>
- 0.75 points for correctly incrementing the counter value when
<button>
is clicked - Point deductions for errors:
- -0.5 points for not naming which framework that is used
- -0.25 points for using
var
when it's better to uselet
- -0.1 points for each bad variable and function name
- In Svelte:
- -0.25 points for using something else than
on:click
, such asonClick
- -0.25 points for calling function in
on:click
instead of passing a function to it
- -0.25 points for using something else than
HTTP/REST API
Question 5 (1p)
Question
Match each status code with its corresponding reason phrase.
Status codes:
- 200
- 204
- 400
- 500
- 501
Reason Phrases:
- OK
- No Content
- Bad Request
- Internal Server Error
- Not Implemented
You will get:
- 1 point for all correct
Sample answer
- 200 - OK
- 204 - No Content
- 400 - Bad Request
- 500 - Internal Server Error
- 501 - Not Implemented
Marking guidelines
- 1 point for all correct
Question 6 (2p)
Question
David is assigned the task to design a REST API clients can use to create a new resource on the server representing an anonymous feedback message. In the request, the following information about the resource should be sent:
- A short title of the feedback (required, min length 5 characters, max length 25 characters)
- A long description of the feedback (required, min length 5 characters, max length 500 characters)
From the design, other programmers should have all information they need to be able to implement the REST API and to use it, so details are needed.
David is lazy and wants you design the REST API instead. Design the REST API for David.
Note: If you think you are missing some critical information you have to know to be able to carry out the task in a good way, feel free to make your own assumptions about that information, but clearly state which those assumptions are in your answer.
Sample answer
Assuming the newly created resource gets an integer as id.
Request:
- URI:
/feedback-messages
- Method:
POST
- Headers:
Content-Type
:application/json
Content-Length
:THE_ACTUAL_NUMBER_OF_BYTES_IN_THE_BODY
- Body:
{"title": "THE_ACTUAL_TITLE", "description": "THE_ACTUAL_DESCRIPTION"}
Possible responses:
- If the server can't carry out the request because of some internal error:
- Status code:
500
- Status code:
- If there are validation errors:
- Status code:
400
- Headers:
Content-Type
:application/json
Content-Length
:THE_ACTUAL_NUMBER_OF_BYTES_IN_THE_BODY
- Body:
["ERROR_CODE_1", "ERROR_CODE_2", ...]
- Available
ERROR_CODE_X
:TITLE_TOO_SHORT
(must be at least 5 characters)TITLE_TOO_LONG
(may be at most 25 characters)DESCRIPTION_TOO_SHORT
(must be at least 5 characters)DESCRIPTION_TOO_LONG
(may be at most 500 characters)
- Available
- Status code:
- If the resource was successfully created
- Status code:
201
- Headers:
Content-Type
:application/json
Content-Length
:THE_ACTUAL_NUMBER_OF_BYTES_IN_THE_BODY
Location
:/feedback-messages/THE_ACTUAL_ID
- Body:
{"title": "THE_ACTUAL_TITLE", "description": "THE_ACTUAL_DESCRIPTION", "id": THE_ACTUAL_ID}
- Status code:
Marking guidelines
- 0.5 points for request
- 0.5 points for internal server response
- 0.5 points for validation errors response
- 0.5 points for success response
Point deduction:
- -0.1 points for each piece of information missing, such as:
- URI in request
- Method in request
- Crucial header in request (except
Content-Length
) - Body/property in body
- Crucial header in response
- Error code/message
- Etc.
- -0.1 points for each piece of information written that makes no sense
Question 7 (3p)
Question
Name and describe each constraint REST consists of.
Sample answer
See Chapter 5.1 Deriving REST in Roy Thomas Fielding's dissertation Architectural Styles and the Design of Network-based Software Architectures.
Marking guidelines
- 0.25 points for each correct name
- 0.25 points for each correct description
Question 8 (2p)
Question
In a REST API implementing Access Tokens as JWTs, explain why a hacker can't simply create his own JWT and use as Access Token in the REST API. Be detailed.
Sample answer
When the server creates a JWT Access Token for a user, the server computes a hash value from the data in the token and a secret (some text) only the server knows, and that hash value (known as the signature) is part of the token. When the server later receives a token from a user, it computes the signature from the data in the token and the secret again, and checks if the newly computed signature is the same as the signature stored in the token. If they are the same, the server can be sure the token was created by the server itself, because it's only the server that knows which secret it used to compute the signature in the tokens when it created it.
If a hacker tries to change the data in a token it has received from the server, the server would compute a different signature when it receives back the token from the hacker, and wouldn't trust the data in the token, so a hacker can't change the data in an valid token created by the server.
If a hacker wants to create and use his own token in the REST API, he must use the same secret as the server to compute the signature in the token, otherwise the server will discover that the token wasn't created by the server itself, and not trust it when it receives it. But the hacker can't know which secret the server is using (only the server should know that), so the hacker must guess which secret to use, and as long as the server does not use a short and simple secret like aaa
, it will in practice be impossible for the hacker to guess which secret the server is using.
Marking guidelines
- 0.66 points for explaining how the server computes the signature when creating the token (hashing data + secret)
- 0.66 points for explaining how the server verifies the token when it receives it (hashing data + secret again, and compare with signature in token)
- 0.66 points for explaining why a hacker can't trick the server
Question 9 (3p)
Question
There exists 3 different type of claim names in the JWT specification. Name the three, and explain how they differ.
Sample answer
See Chapter 4. Claims in the JWT specification.
Marking guidelines
- 0.25 points for each (somewhat) correct name
- 0.75 points for each correct explanation
Question 10 (1p)
Question
Bob says that as long as you don't store any sensitive personal information about the user in a JWT Access Token, the REST API does not need to use HTTPS, since the Access Tokens are signed by the server, so there is no security vulnerability. Is Bob right or wrong? Justify your answer.
Sample answer
Bob is wrong. If the REST API does not use HTTPS, then anyone who helps forwarding the HTTP requests/responses between the client and the server can see which access token the client have, and they can then send their own malicious HTTP requests to the server with that access token, and that's a security vulnerability.
Marking guidelines
- 1 point for correct answer and valid justification
Question 11 (1p)
Question
Is the user's account id something you are likely to find in an ID Token? Or in an Access Token? Or none? Or both? Justify your answer.
Sample answer
When the user logs in, the client usually needs to know which account the user logged in to (for example to fetch and show information related to the account the user logged in to), so you are likely to find the user's account id in the ID token, so the client can learn which account the user logged in to through it.
When sending an HTTP request to a server with authorization rules (i.e. not all users should be authorized to do everything on the server), the server often needs to know which account the user has logged in to to figure out if the user is authorized to send the received request or not. For this reason, you often find the user's account id in the Access Token as well.
Marking guidelines
- 0.5 points for an answer with valid justification for ID Token
- 0.5 points for an answer with valid justification for Access Token
Question 12 (1p)
Question
Name one CORS header, give an example of a value it can have, and explain what the meaning of using that header with that value is.
Sample answer
Header name: Access-Control-Allow-Origin
Header value: *
It means that client-side code from any origin (any website at all) should be allowed to send HTTP request to the server that sent back the response with this header.
Marking guidelines
- 0.33 points for header name
- Only 0.23 points if the name is quite wrong, but close enough to be able to understand which CORS header it is
- 0.33 points for valid header value
- 0.33 points for correct explanation
Docker
Question 13 (1p)
Question
Explain the difference between the Docker commands RUN
and CMD
.
Sample answer
The RUN
command specifies a command that should be executed when building a new Docker image. The CMD
command on the other hand specifies which command that should be executed to start the program in the image, and it will be executed when the image starts running in a new container.
Marking guidelines
- 0.5 points for correctly explaining
RUN
- 0.5 points for correctly explaining
CMD
OR:
- 0.25 points for explaining
RUN
asCMD
- 0.25 points for explaining
CMD
asRUN
Question 14 (1p)
Question
Alice sends you a Docker image that contains a program that computes a value and logs it to the file /computed/the-value.txt
. After you have run this image in a new Docker container, you realize the computed value is logged to that file in the container, and not to that file on your host computer's file system, so you can't read the file. But you really want to know what value is logged to the file.
Suggest a solution that enables you to learn what value the program computes.
Note: Since you don't have the source code for the image, you can't make any changes to it.
Sample answer
One solution is to mount a volume to the container when you start running it. You can for example say that the path /computed/
in the container should be mapped to /home/the-username/computed/
on the host computer. That way, the program in the container will write the value to the file /home/the-username/computed/the-value.txt
on the host computer instead, and after running the image in a container you can read the value from that file there on the host computer.
Marking guidelines
- 1 point for suggesting a solution that works
Scaling
Question 15 (1p)
Question
Can a web application that can be scaled vertically always just as easy be scaled horizontally? Justify your answer.
Sample answer
No. To be able to scale horizontally, the web application needs to be stateless. That is not a requirement to be able to scale the web application vertically, so if the web application contains a state it will be harder to scale it horizontally.
Marking guidelines
- 1 point for correct answer and valid justification
JavaScript
Question 16 (2p)
Question
The function getAccountByUsername()
should receive the username of an account as an argument, and it returns a promise that resolves to a JS object with information about the account with that username. If the account object can't be retrieved, the promise is rejected to a JS error object containing information about what went wrong.
Implement the function getAccountsByUsernames()
, that receives an array with usernames as argument, and returns a promise that resolves to an array with account objects retrieved from getAccountByUsername()
for respective username.
If you get an error from getAccountByUsername()
, then your own promise should be rejected with the JS error object you get from it.
It is OK to use async
and await
in your implementation.
Sample answer
async function getAccountsByUsernames(usernames){
const accounts = []
for(const username of usernames){
accounts.push(
await getAccountByUsername(username)
)
}
return accounts
}
Marking guidelines
- 2 points for a solution that is largely correct
- Point deductions for errors:
- -0.1 points for naming the function
getAccountsByUsernames
wrong - -0.1 points for using
let
syntax where it's not allowed (such as in front of parameter names) - -0.1 points for bad variable names, such as ending with
promise
when it doesn't store a promise - -0.1 points for returning and throwing (both stops code execution in function, so no need to use return when you want to throw)
- -0.1 points for using
let
/var
whenconst
should be used - -0.25 points for pushing to array with
+
operator (wont' work in JS) instead of usingpush()
method - -0.25 points for not returning array with account objects
- -0.25 points for writing the loop wrong
- -0.25 points for creating local variables (e.g. in a
try
) and try to access them outside - -0.25 points for using try and catch
- -0.25 points for returning error instead of throwing
- -0.25 points for returning/throwing wrong error
- -0.25 points for having unnecessary code
- -0.25 points for returning the accounts array in an object
- -0.25 points for using
Promise.all()
wrong - -0.5 points for missing the
async
andawait
keywords
- -0.1 points for naming the function
- 0 points for a solution that either:
- Have a callback parameter/function
- Try to read status codes or parse the response body (none of the given functions use HTTP)
- Try to send back an HTTP response of some kind (the function should not use HTTP at all)
- Have SQL code (the data is not stored in a relational database)
- Don't use a loop/iteration of any kind