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 element
  • submit: 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 the onEVENTNAME HTML attributes, then one should write on 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 use let
    • -0.1 points for each bad variable and function name
    • In Svelte:
      • -0.25 points for using something else than on:click, such as onClick
      • -0.25 points for calling function in on:click instead of passing a function to it

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
  • 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)
  • 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}
      

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 RESTopen in new window 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. Claimsopen in new window 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 as CMD
  • 0.25 points for explaining CMD as RUN

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 when const should be used
    • -0.25 points for pushing to array with + operator (wont' work in JS) instead of using push() 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 and await keywords
  • 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