Skip to content

Commit

Permalink
Enforce required fields in objects
Browse files Browse the repository at this point in the history
  • Loading branch information
aronmolnar committed Mar 15, 2024
1 parent a56d4c7 commit 852be54
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 8 deletions.
24 changes: 17 additions & 7 deletions reptor/models/Section.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,32 @@ def __init__(
# property is of type ProjectDesignField
try:
property_value[property.name] = self.__class__(
property, value[property.name]
property,
value[property.name],
raise_on_unknown_fields=raise_on_unknown_fields,
)

except KeyError:
if raise_on_unknown_fields:
raise KeyError(
f"Object name '{property.name}' not found. Did you use "
f"wrong project design for your data?"
if property.required:
raise ValueError(
f'"{property.name}" is a required field for "{self.name}".'
)
if raise_on_unknown_fields and (
unknown_fields := [
v
for v in value.keys()
if v not in [p.name for p in self.properties]
]
):
raise ValueError(
f"Unknown fields in {self.name}: {','.join(unknown_fields)}"
)
self.value = property_value
elif self.type == ProjectFieldTypes.list.value:
self.value = list()
if not isinstance(value, list):
raise ValueError(f"Value of '{self.name}' must be list.")
for v in value: # type: ignore
self.value.append(self.__class__(self.items, v)) # type: ignore
self.value.append(self.__class__(self.items, v, raise_on_unknown_fields=raise_on_unknown_fields)) # type: ignore
else:
self.value = value

Expand Down
80 changes: 79 additions & 1 deletion reptor/tests/test_section_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from reptor.models.FindingTemplate import FindingTemplate
from reptor.models.ProjectDesign import ProjectDesign, ProjectDesignField
from reptor.models.Section import SectionData, SectionDataField, SectionRaw
from reptor.models.Section import Section, SectionData, SectionDataField, SectionRaw


class TestSectionModelParsing:
Expand Down Expand Up @@ -118,6 +118,84 @@ class TestSectionModelParsing:

example_finding_template = '{"id":"82c5d4c5-b1ff-4d17-9567-c2679df325d5","created":"2022-10-19T10:30:48.055000Z","updated":"2023-07-24T10:19:59.815926Z","details":"https://syslifters.sysre.pt/api/v1/findingtemplates/82c5d4c5-b1ff-4d17-9567-c2679df325d5","images":"https://syslifters.sysre.pt/api/v1/findingtemplates/82c5d4c5-b1ff-4d17-9567-c2679df325d5/images","usage_count":14,"source":"created","tags":["web"],"translations":[{"id":"7171d2ef-a2ca-4710-9fea-6689ff2b9a26","created":"2023-07-24T10:19:59.813648Z","updated":"2023-07-24T10:19:59.813885Z","language":"en-US","status":"finished","is_main":true,"risk_score":9.8,"risk_level":"critical","data":{"title":"SQL Injection (SQLi)","cvss":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","summary":"The web application processed user input in an insecure manner and was thus vulnerable to SQL injection. In an SQL injection attack, special input values in the web application are used to influence the application\'s SQL statements to its database. Depending on the database used and the design of the application, this may make it possible to read and modify the data stored in the database, perform administrative actions (e.g., shut down the DBMS), or in some cases even gain code execution and the accompanying complete control over the vulnerable server.","description":"We identified an SQL injection vulnerability in the web application and were able to access stored data in the database as a result.\\n\\n**TODO: technical description**\\n\\nSQL Injection is a common server-side vulnerability in web applications. It occurs when software developers create dynamic database queries that contain user input. In an attack, user input is crafted in such a way that the originally intended action of an SQL statement is changed. SQL injection vulnerabilities result from an application\'s failure to dynamically create database queries insecurely and to properly validate user input. They are based on the fact that the SQL language basically does not distinguish between control characters and data characters. In order to use a control character in the data part of an SQL statement, it must be encoded or escaped appropriately beforehand.\\n\\nAn SQL injection attack is therefore essentially carried out by inserting a control character such as `\'` (single apostrophe) into the user input to place new commands that were not present in the original SQL statement. A simple example will demonstrate this process. The following SELECT statement contains a variable userId. The purpose of this statement is to get data of a user with a specific user id from the Users table.\\n\\n`sqlStmnt = \'SELECT * FROM Users WHERE UserId = \' + userId;`\\n\\nAn attacker could now use special user input to change the original intent of the SQL statement. For example, he could use the string `\' or 1=1` as user input. In this case, the application would construct the following SQL statement:\\n\\n`sqlStmnt = \'SELECT * FROM Users WHERE UserId = \' + \' or 1=1;`\\n\\nInstead of the data of a user with a specific user ID, the data of all users in the table is now returned to the attacker after executing the statement. This gives an attacker the ability to control the SQL statement in his own favor. \\n\\nThere are a number of variants of SQL injection vulnerabilities, attacks and techniques that occur in different situations and depending on the database system used. However, what they all have in common is that, as in the example above, user input is always used to dynamically construct SQL statements. Successful SQL injection attacks can have far-reaching consequences. One would be the loss of confidentiality and integrity of the stored data. Attackers could gain read and possibly write access to sensitive data in the database. SQL injection could also compromise the authentication and authorization of the web application, allowing attackers to bypass existing access controls. In some cases, SQL injection can also be used to gain code execution, allowing an attacker to gain complete control over the vulnerable server.","precondition":null,"impact":null,"recommendation":"* Use prepared statements throughout the application to effectively avoid SQL injection vulnerabilities. Prepared statements are parameterized statements and ensure that even if input values are manipulated, an attacker is unable to change the original intent of an SQL statement. \\n* Use existing stored procedures by default where possible. Typically, stored procedures are implemented as secure parameterized queries and thus protect against SQL injections.\\n* Always validate all user input. Ensure that only input that is expected and valid for the application is accepted. You should not sanitize potentially malicious input.\\n* To reduce the potential damage of a successful SQL Injection attack, you should minimize the assigned privileges of the database user used according to the principle of least privilege.\\n* For detailed information and assistance on how to prevent SQL Injection vulnerabilities, see OWASP\'s linked SQL Injection Prevention Cheat Sheet.","short_recommendation":"Make sure that Prepared Statements and Stored Procedures (where possible) are used throughout the application. This prevents the originally intended action of an SQL statement from being manipulated by an attacker.","references":["https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet"],"affected_components":[],"owasp_top10_2021":null,"wstg_category":null,"retest_notes":null,"retest_status":null,"retest":null,"retest_state":null}},{"id":"e1aa87ee-7eed-4f47-b730-ab4b255d7abe","created":"2023-08-11T11:23:04.659627Z","updated":"2023-08-11T11:23:04.661221Z","language":"de-DE","status":"in-progress","is_main":false,"risk_score":0.0,"risk_level":"info","data":{"title":"Template Titel","summary":"Auch test."}}],"lock_info":null}'

@pytest.mark.parametrize(
"value,raises",
[
(
[{"description": "Test"}],
True,
),
(
[{"description": "Test", "count": 1, "invalid": 1}],
True,
),
(
[{"description": "Test", "count": 1}],
False,
),
(
[{"description": "Test", "count": "abc"}],
True,
),
],
)
def test_list_of_objects(self, value, raises):
project_design = {
"id": "c95a84f7-82cf-40a2-afd4-38a2f2abcdb2",
"created": "2024-03-15T11:40:25.541705Z",
"updated": "2024-03-15T11:40:25.555168Z",
"source": "snapshot",
"scope": "project",
"name": "Demo",
"language": "en-US",
"report_fields": {
"description_objects": {
"type": "list",
"items": {
"type": "object",
"label": "",
"origin": "custom",
"properties": {
"description": {
"type": "string",
"label": "Description",
"origin": "custom",
"default": None,
"required": True,
"spellcheck": False,
},
"count": {
"type": "number",
"label": "Count",
"origin": "custom",
"default": None,
"required": True,
},
},
},
"label": "Description Objects",
"origin": "custom",
"required": False,
}
},
}

project_design = ProjectDesign(project_design)
if raises:
with pytest.raises(ValueError):
s = Section(
{"data": {"description_objects": value}},
project_design,
raise_on_unknown_fields=True,
)
pass
else:
Section(
{"data": {"description_objects": value}},
project_design,
raise_on_unknown_fields=True,
)

def test_section_data(self):
json_example = json.loads(self.example_section)
section_raw = SectionRaw(json_example)
Expand Down

0 comments on commit 852be54

Please sign in to comment.