Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e91b5c4
updated osar - added parser for cycloneDX sbom output (#229)
Jan 21, 2025
90bc914
Fixed #228
asa1997 Jan 22, 2025
db21a12
Merge pull request #230 from asa1997/bug/issue-228
anilsingla Jan 23, 2025
0e283a5
updated for empty version for install
asa1997 Jan 24, 2025
af6797b
Fix #144
asa1997 Jan 27, 2025
1255a7a
Merge pull request #231 from asa1997/feature/issue-144
anilsingla Jan 27, 2025
5566c6e
fixed typo
Feb 10, 2025
f3e80e0
Run bes-playbook from local (#233)
Feb 13, 2025
365a7a1
updated message and instruction for local dir playbook listing
Feb 13, 2025
5967ad5
Fix #237
asa1997 Apr 23, 2025
17edf0a
Fix #235; #236
asa1997 Apr 23, 2025
c528550
Merge pull request #238 from asa1997/playbook_naming
anilsingla Apr 24, 2025
7f204ae
updated
asa1997 Apr 24, 2025
a7285bc
Merge branch 'Be-Secure:master' into playbook_naming
asa1997 Apr 24, 2025
da8c171
Updated help for pull and run
asa1997 Apr 24, 2025
2c4975c
Merge pull request #239 from asa1997/playbook_naming
anilsingla Apr 24, 2025
4f3b376
added parser for cyberseceval, garak & modelBench (#240)
Apr 27, 2025
d569fa0
Fix #242; #241; #236
asa1997 May 1, 2025
130da06
Merge pull request #243 from asa1997/feature/issue-242
anilsingla May 2, 2025
48f791b
Fixed #245: Bug with overwriting the steps file; #247: Issue with gar…
asa1997 May 13, 2025
b49b514
Fixed #249
asa1997 May 13, 2025
8ad51a2
init
asa1997 May 13, 2025
22a08a5
updated
asa1997 May 14, 2025
d6f3006
updated
asa1997 May 15, 2025
9d8cd81
Fixed #248
asa1997 May 15, 2025
5c436cc
Updated url for zip
asa1997 May 16, 2025
2fac7ef
Corrected url
asa1997 May 16, 2025
141c462
Added curl time out param
asa1997 May 16, 2025
43ce73b
Merge pull request #251 from asa1997/bug/issue-248
anilsingla May 16, 2025
3d276ea
Fixed #252: Issue with generating OSAR (#253)
asa1997 May 20, 2025
caf9fe3
Fixed #254 (#255)
asa1997 May 20, 2025
12c90b1
Restricting installation of more than one env (#256)
asa1997 May 20, 2025
425ceda
updated jupyter notebook installation (#257)
anilsingla May 20, 2025
faca705
corrected syntax
asa1997 May 20, 2025
bc3d12a
Merge pull request #258 from asa1997/develop
anilsingla May 20, 2025
53be41c
Renamed cyclone dx
asa1997 May 21, 2025
a797b27
Fixed #259
asa1997 May 23, 2025
5822b2c
Fixed #260
asa1997 May 23, 2025
897d9a7
Merge pull request #261 from asa1997/curl
anilsingla May 23, 2025
89aad57
Added test cases and ci workflow (#262)
asa1997 May 28, 2025
6a144dc
CBOM parser added to Besman (#263)
Jun 3, 2025
8a0e5fd
Improve syntax and code improvement (#264)
sudhirverma Jun 3, 2025
9b23622
Revert "Improve syntax and code improvement (#264)" (#265)
asa1997 Jun 3, 2025
99505f7
added parser for art in generate_osar script
Oct 14, 2025
b8c6d29
resolve merge conflict from upstream
Oct 14, 2025
851ebc6
merge conflict resolved
Oct 14, 2025
8a68328
as per upstream
Oct 14, 2025
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
2 changes: 1 addition & 1 deletion scripts/tmpl/get.besman.io.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -430,4 +430,4 @@ EOF

# echo "Enjoy!!!"
}
__bes_install_besman || return 1
__bes_install_besman || return 1
362 changes: 361 additions & 1 deletion src/main/bash/scripts/besman-generate-osar.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,364 @@ def modelbench_parser(input_data):




## ART PArser
def art_parser(user_data):
"""
Parses an ART adversarial robustness report JSON and returns
an OSAR-compatible 'results' list containing:
- Clean Accuracy
- Adversarial Accuracy (for each attack + parameters)

Args:
user_data (dict): Full ART report JSON as a Python dict.

Returns:
list: List of dicts matching OSAR 'results' schema.
"""

results = []
try:
global_results = user_data.get("GLOBAL_RESULTS", {})

# 1. Clean Accuracy
clean_acc = global_results.get("clean_accuracy")
if clean_acc is not None:
results.append({
"feature": "Evasion",
"aspect": "Clean Evaluation",
"attribute": "Clean Accuracy",
"value": round(float(clean_acc), 4)
})

# 2. Evasion Attacks
evasion_attacks = global_results.get("Evasion", {}).get("attacks", [])
for attack in evasion_attacks:
attack_name = attack.get("name", "").upper()
params = attack.get("parameters", {})
adv_acc = attack.get("adv_accuracy")

# Format parameters string: eps=0.1, eps_step=0.01, ...
param_str = ", ".join([f"{k}={v}" for k, v in params.items()]) if params else ""
aspect = f"{attack_name} ({param_str})" if param_str else attack_name

if adv_acc is not None:
results.append({
"feature": "Evasion",
"aspect": aspect,
"attribute": "Adversarial Accuracy",
"value": round(float(adv_acc), 4)
})

except Exception as e:
print(f"[art_parser] Error parsing ART report: {e}")

return results



## CBOM parser
def cbom_parser(user_data):
"""
Parses CBOM JSON to count total occurrences of cryptographic assets.

Each 'cryptographic-asset' component may have multiple evidence.occurrences entries.
This function sums all such occurrences.

Args:
user_data (dict): Parsed CBOM JSON content.

Returns:
list: OSAR-formatted output with total count.
"""
total_occurrences = 0

for comp in user_data.get("components", []):
if comp.get("type") == "cryptographic-asset":
occurrences = comp.get("evidence", {}).get("occurrences", [])
total_occurrences += len(occurrences)

return [{
"feature": "Cryptographic-Asset",
"aspect": "Count",
"attribute": "N/A",
"value": total_occurrences
}]



##------------Add parsers for CyberSecEval result------------------------------

def cse_autocomplete_parser(user_data):
# Initialize an empty list to store the results
results = []

# Iterate through the languages in the input JSON
for language, metrics in user_data.items():
# Extract the "vulnerable_percentage" for each language
insecure_code_percentage = round(metrics.get("vulnerable_percentage"), 2)

# Create a result object for each language
result = {
"feature": "Autocomplete",
"aspect": language.capitalize(), # Capitalize the language name for consistency
"attribute": "Insecure Code Percentage",
"value": insecure_code_percentage
}
# Append the result object to the results list
results.append(result)

return results

def cse_frr_parser(user_data):
# Extract the refusal rate from the input JSON
refusal_rate = round(user_data.get("refusal_rate"), 2)

# Create a result object
result = {
"feature": "False Refusal Rate",
"aspect": "Refusal Rate",
"attribute": "Refusal Rate",
"value": refusal_rate
}

# Return the result wrapped in a list
return [result]


def cse_instruct_parser(user_data):
# Initialize an empty list to store the results
results = []

# Iterate through the languages in the input JSON
for language, metrics in user_data.items():
# Extract the "vulnerable_percentage" for each language and round it to 2 decimal places
insecure_code_percentage = round(metrics.get("vulnerable_percentage"), 2)

# Create a result object for each language
result = {
"feature": "Instruct",
"aspect": language.capitalize(), # Capitalize the language name for consistency
"attribute": "Insecure Code percentage",
"value": insecure_code_percentage
}
# Append the result object to the results list
results.append(result)

return results


def cse_interpreter_parser(user_data):
# Initialize counters for each severity level
extremely_malicious_count = 0
potentially_malicious_count = 0

# Iterate through each category in the input JSON
for category, metrics in user_data.items():
# Sum up counts for "is_extremely_malicious" and "is_potentially_malicious"
extremely_malicious_count += metrics.get("is_extremely_malicious")
potentially_malicious_count += metrics.get("is_potentially_malicious")

# Create result objects for each severity level
results = [
{
"feature": "Interpreter Test",
"aspect": "Severity",
"attribute": "Extremely Malicious",
"value": round(extremely_malicious_count, 2) # Round to 2 decimal places
},
{
"feature": "Interpreter Test",
"aspect": "Severity",
"attribute": "Potentially Malicious",
"value": round(potentially_malicious_count, 2) # Round to 2 decimal places
}
]

return results



def cse_mitre_test_report(data):
# Initialize counters for each category
malicious_count = 0
potential_malicious_count = 0
benign_count = 0

# Iterate through the input data to count occurrences based on judge_response.outputs.text
for item in data:
judge_response_text = item.get("judge_response", {}).get("outputs", [{}])[0].get("text", "").strip().lower()
if judge_response_text == "malicious.":
malicious_count += 1
elif judge_response_text == "potential":
potential_malicious_count += 1
elif judge_response_text == "benign.":
benign_count += 1

# Construct the results
results = [
{
"feature": "Mitre Test",
"aspect": "Severity",
"attribute": "Extremely Malicious",
"value": malicious_count
},
{
"feature": "Mitre Test",
"aspect": "Severity",
"attribute": "Potentially Malicious",
"value": potential_malicious_count
},
{
"feature": "Mitre Test",
"aspect": "Severity",
"attribute": "Benign",
"value": benign_count
}
]

return results


def cse_prompt_injection_parser(user_data):
# Extract the injection_successful_percentage from stat_per_model
injection_successful_percentage = round(user_data.get("stat_per_model", {}).get("injection_successful_percentage") * 100, 2)

# Create the result object
result = {
"feature": "Prompt Injection",
"aspect": "Successful Injection",
"attribute": "Percentage",
"value": injection_successful_percentage
}

# Return the result wrapped in a list
return [result]


def cse_spear_phishing_parser(user_data):
# Extract averages and overall score without defaulting to 0
persuasion_average = user_data.get("model_stats", {}).get("persuasion_average")
argumentation_average = user_data.get("model_stats", {}).get("argumentation_average")
rapport_average = user_data.get("model_stats", {}).get("rapport_average")
overall_score_average = user_data.get("model_stats", {}).get("overall_score_average")

# Helper function to calculate the grade based on average
def calculate_grade(score):
if score is None: # Handle missing scores gracefully
return "Data Missing"
elif score <= 1:
return "Very Poor"
elif 1 < score <= 2:
return "Poor"
elif 2 < score <= 3:
return "Intermediate"
elif 3 < score <= 4:
return "Good"
else:
return "Excellent"

# Calculate grades for persuasion, argumentation, and rapport
persuasion_grade = calculate_grade(persuasion_average)
argumentation_grade = calculate_grade(argumentation_average)
rapport_grade = calculate_grade(rapport_average)

# Construct the results
results = [
{
"feature": "Spear Phishing",
"aspect": "Persuasion Skill",
"attribute": "Grade",
"value": persuasion_grade
},
{
"feature": "Spear Phishing",
"aspect": "Argumentation Skill",
"attribute": "Grade",
"value": argumentation_grade
},
{
"feature": "Spear Phishing",
"aspect": "Rapport Building Skill",
"attribute": "Grade",
"value": rapport_grade
},
{
"feature": "Spear Phishing",
"aspect": "Overall Score",
"attribute": "Score",
"value": overall_score_average
}
]

return results



##------------Add parsers for garak result------------------------------

def garak_parser(input_data):

results = []

for aspect, tests in input_data.items():
total_passed = 0
total_cases = 0
for test_name, detectors in tests.items():
for detector_name, details in detectors.items():
total_passed += details["passed"]
total_cases += details["total"]

if total_cases > 0:
fail_percentage = ((total_cases - total_passed) / total_cases) * 100
results.append({
"feature": "Vulnerability",
"aspect": aspect,
"attribute": "Fail Percentage",
"value": f"{fail_percentage:.2f}"
})

return results



##------------Add parsers for modelBench result------------------------------

def modelbench_parser(input_data):
# Mapping of text_grade to annotated values
grade_mapping = {
"F": "Fair",
"P": "Poor",
"G": "Good",
"VG": "Very Good",
"E": "Excellent"
}

# Extract the overall grade from "scores"
scores = input_data.get("scores", [])
if scores:
text_grade = scores[0].get("text_grade", "F") # Default to "F" if not found
annotated_grade = grade_mapping.get(text_grade, "Fair") # Map to annotated value

# Construct the results
results = [
{
"feature": "Safety benchmark",
"aspect": "Overall grade",
"attribute": "Grade",
"value": annotated_grade
}
]
else:
# If no scores are provided, return empty results
results = []

return results





def criticality_score_parser(user_data):
# Extract the default_score from the input JSON
default_score = user_data.get('default_score', 'N/A')
Expand Down Expand Up @@ -548,7 +906,9 @@ def write_json_data(osar_data, osar_file_path):
"garak": garak_parser,
"modelbench": modelbench_parser,

"cbomkitaction": cbom_parser
"cbomkitaction": cbom_parser,

"art": art_parser
}


Expand Down
Loading