lab01 : Vulnerability Database - Python Classes

num ready? description assigned due
lab01 true Vulnerability Database - Python Classes Mon 04/07 11:00AM Tue 04/15 11:59PM

Introduction

The second assignment is to create a vulnerability database to aid in keeping track of the vulnerabilities that you have been reading about and the weakness classes that they belong to. Vulnerabilities are recorded in the authoritative list of Common Vulnerabilities and Exposures (CVE), and given a unique CVE identifier. After analysis, each vulnerability is classified according to a known weakness that is among the Common Weakness Enumeration (CWE) list. These are important classifications (look up some of the examples in this lesson to learn more), but they are difficult for humans to process visually. Fortunately, you can make a data structure to help you keep track of this.

The practical skills to be developed include:

Note: In general, it is always important to work on labs and reading early so you can gain the proper context and utilize our office hours to seek assistance / ask clarifying questions during the week before the deadline, if needed!

It is a good idea to read up on some tools we’ll use in this lab before you get started, specifically:

The main idea for this lab is to write a program that will organize Vulnerabilities into a Vulnerability list. The program should have the ability to add / remove / search for vulnerabilities.

Instructions

We recommend that you organize your lab work for this lab in its own directory, e.g., lab01. This way all files for a lab are located in a single folder. Also, this will be easy to import various files into your code using the import * from technique shown in lecture.

You will need to create three files (note that the file names are case-sensitive when we import them in Python!):

Vulnerability.py class

The Vulnerability.py file will contain the definition of a Vulnerability record.

We will define the Vulnerability attributes as follows:

You will write a constructor that allows the user to construct a Vulnerability object by passing in values for all of the fields. Your constructor should set these attributes to the value None by default.

In addition to your constructor, your class definition should also support “setter” methods that can update the state of the Vulnerability objects:

Each Vulnerability object should be able to call a method to_string() that you will implement, which returns a str with all the vulnerability attributes EXACTLY as shown (i.e., the string should contain all attributes in the following EXACT format “CVE” (CWE) - YEAR):

vulnerability1 = Vulnerability("CVE-2024-37032", "CWE-20", 2024)
print(vulnerability1.to_string())
print() # separate two vulnerabilities with a newline
vulnerability2 = Vulnerability("CVE-2024-3402", "CWE-1426", 2024)
print(vulnerability2.to_string())

Output:

"CVE-2024-37032" (CWE-20) - 2024

"CVE-2024-3402" (CWE-1426) - 2024

IMPORTANT: The .to_string() return value in the example above does not contain a newline character (\n) at the end.

testFile.py - Test your code

To ensure that your methods return the correct values of correct types, create a file testFile.py to hold the tests for the methods. Below are potential objects and corresponding assertions that you can include:

from Vulnerability import Vulnerability

vuln0 = Vulnerability()
assert vuln0.cve == None
### TODO: write additional asserts for the other methods
### to test the default form of the constructor

vuln1 = Vulnerability("CVE-2008-1303", "CWE-20", 2008)
assert vuln1.cve == "CVE-2008-1303"
### TODO: write additional asserts for the other methods
assert vuln1.to_string() == '"CVE-2008-1303" (CWE-20) - 2008'

### TODO: write additional asserts for at least one other vulnerability

VulnDB.py

The VulnDB.py file will contain the definition of a single VulnDB object. An VulnDB object will contain a dictionary structure where the keys of the dictionary will be a str type representing a vulnerability weakness classification (all upper-case characters). The dictionary value will be a list of Vulnerability objects that the VulnDB contains. Note that the order of the Vulnerabilities in the list is based on when the Vulnerability object was inserted into the dictionary structure (most recent Vulnerability inserted will be at the end of the list).

Your code should support the following constructor and methods:

Given an example VulnDB, add the following objects and assertions to your testFile.py:

remember to import the correct module into your testFile.py

vulnerabilities = VulnDB()
vuln1 = Vulnerability("CVE-2022-21668", "CWE-400", 2022)
vuln2 = Vulnerability()
vuln3 = Vulnerability("CVE-2020-7218", "CWE-400", 2020)
vulnerabilities.add_vuln(vuln1)
vulnerabilities.add_vuln(vuln2)

try to create more vuln objects and add them in the VulnDB, you must use assert statements as shown below to test if the vuln objects were inserted correctly to the VulnDB

assert vulnerabilities.does_vuln_exist(vuln1) == True
assert vulnerabilities.does_vuln_exist(vuln2) == True
assert vulnerabilities.does_vuln_exist(vuln3) == False

vulnerabilities.add_vuln(vuln3)
assert vulnerabilities.does_vuln_exist(vuln1) == True
assert vulnerabilities.does_vuln_exist(vuln2) == True
assert vulnerabilities.does_vuln_exist(vuln3) == True

vulnerabilities.remove_vuln(vuln1)
assert vulnerabilities.does_vuln_exist(vuln1) == False
assert vulnerabilities.does_vuln_exist(vuln2) == True
assert vulnerabilities.does_vuln_exist(vuln3) == True

do the same for the other vuln objects in the VulnDB. use the assert statements as shown to check if the vuln objects were removed correctly

vuln1 = Vulnerability("CVE-2022-21668", "CWE-400", 2022)
vuln2 = Vulnerability()
vuln3 = Vulnerability("CVE-2020-7218", "CWE-400", 2020)
vuln4 = Vulnerability("CVE-2022-21668", "CWE-1284", 2022)
vuln5 = Vulnerability("CVE-2008-1303", "CWE-20", 2008)

vulnerabilities = VulnDB()
vulnerabilities.add_vuln(vuln1)
vulnerabilities.add_vuln(vuln2)
vulnerabilities.add_vuln(vuln3)
vulnerabilities.add_vuln(vuln4)
vulnerabilities.add_vuln(vuln5)

assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-400") == (
    '"CVE-2022-21668" (CWE-400) - 2022\n'
    '"CVE-2020-7218" (CWE-400) - 2020'
)

assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-1284") == \
    '"CVE-2022-21668" (CWE-1284) - 2022'
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-20") == \
    '"CVE-2008-1303" (CWE-20) - 2008'

# Check that the removal is working correctly
vulnerabilities.remove_cwe("CWE-20")
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-400") == (
    '"CVE-2022-21668" (CWE-400) - 2022\n'
    '"CVE-2020-7218" (CWE-400) - 2020'
)
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-1284") == \
    '"CVE-2022-21668" (CWE-1284) - 2022'
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-20") == ""

vulnerabilities.remove_cwe("CWE-1284")
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-1284") == ""
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-400") == (
    '"CVE-2022-21668" (CWE-400) - 2022\n'
    '"CVE-2020-7218" (CWE-400) - 2020'
)
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-20") == ""

vulnerabilities.remove_cwe("CWE-400")
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-1284") == ""
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-400") == ""
assert vulnerabilities.get_vulnerabilities_by_cwe("CWE-20") == ""

Please make sure that your logic for all the methods in VulnDB.py pass the above example assert statements. IMPORTANT: You are highly encouraged to test your code locally with additional assert statements before you submit your files to Gradescope - please do not use Gradescope to debug.

Submission

Once you’re done with writing your class definition, submit your testFile.py, Vulnerability.py, and VulnDB.py to the Lab01 assignment on Gradescope. There will be various unit tests Gradescope will run to ensure your code is working correctly based on the specifications given in this lab. If the tests don’t pass, you may get some error message that may or may not be obvious at this point. Don’t worry - if the tests didn’t pass, take a minute to think about what may have caused the error. Check the Troubleshooting guide below. If your tests didn’t pass and you’re still not sure why you’re getting the error, feel free to ask your TAs or Learning Assistants.

Troubleshooting

The autograder failed to execute correctly...

Make sure your files don’t have any print statements in them. The classes should only be returning values, not printing. testFile.py shouldn’t have any output if all the tests pass; it should only produce an AssertionError when something’s wrong with the code.

If you are testing your code with print statements, make sure to either add them inside the if __name__ == "__main__": block or comment them out before submitting your files to Gradescope.


ModuleNotFoundError: No module named ...

Check that you named your file EXACTLY as was specified - remember that Python is case-sensitive. Additionally, make sure that if you submitted the zip file, you didn’t zip the folder: Gradescope expects just the files themselves.


NoneType has no attribute ...

Remember that before you can use .title() or .upper() in your constructor, you need to verify that the parameter is a string instead of None. Use the if/else branches to differentiate between these cases.


Acknowledgment: This lab has been modified in collaboration with Noah Spahn to incorporate cybersecurity context.