Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions framework/python/src/common/risk_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __init__(self, profile_json=None, profile_format=None):
self.risk = None

self._validate(profile_json, profile_format)
self._update_risk(profile_format)
self.update_risk(profile_format)

# Load a profile without modifying the created date
# but still validate the profile
Expand All @@ -51,7 +51,7 @@ def load(self, profile_json, profile_format):
self.status = None

self._validate(profile_json, profile_format)
self._update_risk(profile_format)
self.update_risk(profile_format)

return self

Expand All @@ -70,17 +70,25 @@ def update(self, profile_json, profile_format):
self.questions = new_profile.questions
self.status = new_profile.status

self.risk = new_profile.risk

def get_file_path(self):
return os.path.join(PROFILES_PATH,
self.name + '.json')

def _validate(self, profile_json, profile_format):
if self._valid(profile_json, profile_format):
self.status = 'Expired' if self._expired() else 'Valid'
if self._expired():
self.status = 'Expired'
# User only wants to save a draft
elif 'status' in profile_json and profile_json['status'] == 'Draft':
self.status = 'Draft'
else:
self.status = 'Valid'
else:
self.status = 'Draft'

def _update_risk(self, profile_format):
def update_risk(self, profile_format):

if self.status == 'Valid':

Expand All @@ -103,8 +111,8 @@ def _update_risk(self, profile_format):
# We only want to check the select or select-multiple
# questions for now
if format_q['type'] in ['select', 'select-multiple']:
answer = question['answer']

answer = question['answer']
question_risk = 'Limited'

# The answer is a single string (select)
Expand Down Expand Up @@ -143,11 +151,10 @@ def _update_risk(self, profile_format):
if 'risk' in question and question['risk'] == 'High':
risk = 'High'

self.risk = risk

else:
# Remove risk
risk = None

self.risk = risk

def _get_format_question(self, question, profile_format):
Expand Down Expand Up @@ -224,6 +231,19 @@ def _valid(self, profile_json, profile_format):
LOGGER.error('Missing answer for question: ' +
profile_question.get('question'))
all_questions_answered = False

answer = profile_question.get('answer')

# Check if a multi-select answer has been completed
if isinstance(answer, list):
if len(answer) == 0:
all_questions_answered = False

# Check if string answer has a length greater than 0
elif isinstance(answer, str):
if required and len(answer) == 0:
all_questions_answered = False

elif required:
LOGGER.error('Missing question: ' + format_question.get('question'))
all_questions_present = False
Expand Down
14 changes: 6 additions & 8 deletions framework/python/src/common/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,23 +501,21 @@ def update_profile(self, profile_json):

else:

# Update the profile
risk_profile.update(profile_json, profile_format=self._profile_format)

# Check if name has changed
if 'rename' in profile_json:
new_name = profile_json.get('rename')
old_name = profile_json.get('name')

# Delete the original file
os.remove(os.path.join(PROFILES_DIR, risk_profile.name + '.json'))

risk_profile.name = new_name

# Update questions and answers
risk_profile.questions = profile_json.get('questions')
os.remove(os.path.join(PROFILES_DIR, old_name + '.json'))

# Write file to disk
with open(os.path.join(PROFILES_DIR, risk_profile.name + '.json'),
'w',
encoding='utf-8') as f:
f.write(json.dumps(risk_profile.to_json()))
f.write(risk_profile.to_json())

return risk_profile

Expand Down
2 changes: 1 addition & 1 deletion modules/test/services/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
xmltodict
xmltodict==0.13.0
2 changes: 1 addition & 1 deletion testing/unit/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

docker build -f testing/unit/unit_test.Dockerfile -t test-run/unit-test .
sudo docker build -f testing/unit/unit_test.Dockerfile -t testrun/unit-test .
2 changes: 1 addition & 1 deletion testing/unit/ntp/ntp_module_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def ntp_module_report_test(self):
formatted_report = self.add_formatting(report_out)

# Write back the new formatted_report value
out_report_path = os.path.join(OUTPUT_DIR,'ntp_report_with_ntp.html')
out_report_path = os.path.join(OUTPUT_DIR, 'ntp_report_with_ntp.html')
with open(out_report_path, 'w', encoding='utf-8') as file:
file.write(formatted_report)

Expand Down
2 changes: 1 addition & 1 deletion testing/unit/report/report_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def create_report(self, results_file_path):
reports_md = []
#reports_md.append(self.get_module_html_report('tls'))
reports_md.append(self.get_module_html_report('dns'))
reports_md.append(self.get_module_html_report('nmap'))
reports_md.append(self.get_module_html_report('services'))
reports_md.append(self.get_module_html_report('ntp'))
report.add_module_reports(reports_md)

Expand Down
134 changes: 23 additions & 111 deletions testing/unit/risk_profile/profiles/risk_profile_draft.json
Original file line number Diff line number Diff line change
@@ -1,144 +1,56 @@
{
"name": "Primary profile",
"status": "Valid",
"created": "2024-05-23",
"version": "v1.3",
"version": "1.3-alpha",
"created": "2024-07-01",
"status": "Draft",
"questions": [
{
"question": "What type of device is this?",
"type": "select",
"options": [
"IoT Sensor",
"IoT Controller",
"Smart Device",
"Something else"
],
"answer": "IoT Sensor",
"validation": {
"required": true
}
"answer": "Sensor - Lighting"
},
{
"question": "How will this device be used at Google?",
"type": "text-long",
"answer": "Installed in a building",
"validation": {
"max": "128",
"required": true
}
},
{
"question": "What is the email of the device owner(s)?",
"type": "email-multiple",
"answer": "boddey@google.com, cmeredith@google.com",
"validation": {
"required": true,
"max": "128"
}
"answer": "sakjdhaskjdh"
},
{
"question": "Is this device going to be managed by Google or a third party?",
"type": "select",
"options": [
"Google",
"Third Party"
],
"answer": "Google",
"validation": {
"required": true
}
"answer": "Google"
},
{
"question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?",
"type": "select",
"options": [
"Yes",
"No",
"N/A"
],
"default": "N/A",
"answer": "Yes",
"validation": {
"required": true
}
"answer": "N/A"
},
{
"question": "Are any of the following statements true about your device?",
"answer": [
3
]
},
{
"category": "Data Transmission",
"question": "Which of the following statements are true about this device?",
"description": "This tells us about the types of data that are transmitted from this device and how the transmission is performed from a technical standpoint.",
"type": "select-multiple",
"answer": [
0,
1,
5
],
"options": [
"PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership",
"Data transmission occurs across less-trusted networks (e.g. the internet).",
"A failure in data transmission would likely have a substantial negative impact (https://www.rra.rocks/docs/standard_levels#levels-definitions)",
"A confidentiality breach during transmission would have a substantial negative impact",
"The device encrypts data during transmission",
"The device network protocol is well-established and currently used by Google"
],
"validation": {
"required": true
}
]
},
{
"category": "Data Transmission",
"question": "Does the network protocol assure server-to-client identity verification?",
"type": "select",
"answer": "Yes",
"options": [
"Yes",
"No",
"I don't know"
],
"validation": {
"required": true
}
"answer": "Yes"
},
{
"category": "Remote Operation",
"question": "Click the statements that best describe the characteristics of this device.",
"description": "This tells us about how this device is managed remotely.",
"type": "select-multiple",
"answer": [
0,
1,
2
],
"options": [
"PII/PHI, or confidential business data is accessible from the device without authentication",
"Unrecoverable actions (e.g. disk wipe) can be performed remotely",
"Authentication is required for remote access",
"The management interface is accessible from the public internet",
"Static credentials are used for administration"
],
"validation": {
"required": true
}
5
]
},
{
"category": "Operating Environment",
"question": "Are any of the following statements true about this device?",
"description": "This informs us about what other systems and processes this device is a part of.",
"type": "select-multiple",
"answer": [
2,
3
],
"options": [
"The device monitors an environment for active risks to human life.",
"The device is used to convey people, or critical property.",
"The device controls robotics in human-accessible spaces.",
"The device controls physical access systems.",
"The device is involved in processes required by regulations, or compliance. (ex. privacy, security, safety regulations)",
"The device's failure would cause faults in other high-criticality processes."
],
"validation": {
"required": true
}
6
]
},
{
"question": "Comments",
"answer": ""
}
]
}
Loading