|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>AIBOM Generated</title> |
|
|
<style> |
|
|
body { |
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
line-height: 1.6; |
|
|
color: #333; |
|
|
background-color: #f9f9f9; |
|
|
} |
|
|
.container { |
|
|
max-width: 1000px; |
|
|
margin: 0 auto; |
|
|
padding: 0 20px; |
|
|
} |
|
|
|
|
|
|
|
|
.header { |
|
|
background-color: #ffffff; |
|
|
padding: 15px 20px; |
|
|
border-bottom: 1px solid #e9ecef; |
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.05); |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: space-between; |
|
|
margin-bottom: 30px; |
|
|
} |
|
|
.header-left { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
} |
|
|
.header img { |
|
|
height: 60px; |
|
|
margin-right: 15px; |
|
|
} |
|
|
.header-content { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
} |
|
|
.header h1 { |
|
|
margin: 0; |
|
|
font-size: 28px; |
|
|
color: #2c3e50; |
|
|
font-weight: 600; |
|
|
margin-bottom: 5px; |
|
|
} |
|
|
.header-right { |
|
|
display: flex; |
|
|
gap: 10px; |
|
|
} |
|
|
.generate-another-btn { |
|
|
padding: 12px 20px; |
|
|
background-color: #3498db; |
|
|
color: white; |
|
|
text-decoration: none; |
|
|
border-radius: 6px; |
|
|
font-weight: 500; |
|
|
font-size: 15px; |
|
|
transition: background-color 0.3s; |
|
|
} |
|
|
.generate-another-btn:hover { |
|
|
background-color: #2980b9; |
|
|
text-decoration: none; |
|
|
} |
|
|
.success-message { |
|
|
text-align: left; |
|
|
padding: 15px; |
|
|
background-color: #d4edda; |
|
|
border: 1px solid #c3e6cb; |
|
|
border-radius: 8px; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
.success-message h2 { |
|
|
margin: 0; |
|
|
font-size: 18px; |
|
|
color: #155724; |
|
|
font-weight: 500; |
|
|
} |
|
|
.model-name { |
|
|
font-weight: 600; |
|
|
color: #2c3e50; |
|
|
} |
|
|
|
|
|
.footer { |
|
|
text-align: center; |
|
|
padding: 20px; |
|
|
color: #7f8c8d; |
|
|
font-size: 14px; |
|
|
margin-top: 30px; |
|
|
} |
|
|
|
|
|
|
|
|
.content-section { |
|
|
background-color: #ffffff; |
|
|
border-radius: 8px; |
|
|
padding: 25px; |
|
|
margin-bottom: 30px; |
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.05); |
|
|
} |
|
|
|
|
|
.content-section h2 { |
|
|
color: #2c3e50; |
|
|
margin-top: 0; |
|
|
margin-bottom: 20px; |
|
|
font-size: 22px; |
|
|
border-bottom: 2px solid #f0f0f0; |
|
|
padding-bottom: 10px; |
|
|
} |
|
|
|
|
|
.content-section h3 { |
|
|
color: #2c3e50; |
|
|
margin-top: 0; |
|
|
margin-bottom: 15px; |
|
|
font-size: 20px; |
|
|
} |
|
|
|
|
|
.content-section p { |
|
|
margin-bottom: 20px; |
|
|
font-size: 16px; |
|
|
line-height: 1.7; |
|
|
color: #555; |
|
|
} |
|
|
|
|
|
|
|
|
.button { |
|
|
display: inline-block; |
|
|
padding: 12px 20px; |
|
|
background-color: #7f8c8d; |
|
|
color: white; |
|
|
border: none; |
|
|
border-radius: 6px; |
|
|
cursor: pointer; |
|
|
font-size: 15px; |
|
|
font-weight: 500; |
|
|
text-decoration: none; |
|
|
transition: background-color 0.3s; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
|
|
|
.button:hover { |
|
|
background-color: #95a5a6; |
|
|
text-decoration: none; |
|
|
} |
|
|
|
|
|
button { |
|
|
padding: 12px 20px; |
|
|
background-color: #3498db; |
|
|
color: white; |
|
|
border: none; |
|
|
border-radius: 6px; |
|
|
cursor: pointer; |
|
|
font-size: 15px; |
|
|
font-weight: 500; |
|
|
transition: background-color 0.3s; |
|
|
} |
|
|
|
|
|
button:hover { |
|
|
background-color: #2980b9; |
|
|
} |
|
|
|
|
|
|
|
|
table { |
|
|
border-collapse: collapse; |
|
|
width: 100%; |
|
|
margin-top: 15px; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
th, td { |
|
|
border: 1px solid #e9ecef; |
|
|
padding: 12px; |
|
|
} |
|
|
th { |
|
|
background-color: #f8f9fa; |
|
|
color: #2c3e50; |
|
|
font-weight: 600; |
|
|
} |
|
|
|
|
|
|
|
|
.check-mark { color: #27ae60; } |
|
|
.x-mark { color: #e74c3c; } |
|
|
.field-name { color: #000; } |
|
|
.field-stars { color: #000; } |
|
|
|
|
|
.improvement { |
|
|
color: #2c3e50; |
|
|
background-color: #ecf0f1; |
|
|
padding: 20px; |
|
|
border-radius: 8px; |
|
|
margin-bottom: 30px; |
|
|
border-left: 4px solid #3498db; |
|
|
} |
|
|
.improvement-value { color: #27ae60; font-weight: bold; } |
|
|
.ai-badge { |
|
|
background-color: #3498db; |
|
|
color: white; |
|
|
padding: 3px 8px; |
|
|
border-radius: 3px; |
|
|
font-size: 0.8em; |
|
|
margin-left: 10px; |
|
|
} |
|
|
|
|
|
|
|
|
.aibom-viewer { |
|
|
margin: 20px 0; |
|
|
border: 1px solid #e9ecef; |
|
|
border-radius: 8px; |
|
|
padding: 20px; |
|
|
background-color: #f9f9f9; |
|
|
} |
|
|
.aibom-section { |
|
|
margin-bottom: 20px; |
|
|
padding: 20px; |
|
|
border-radius: 8px; |
|
|
background-color: white; |
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.05); |
|
|
} |
|
|
.aibom-section h4 { |
|
|
margin-top: 0; |
|
|
color: #2c3e50; |
|
|
border-bottom: 2px solid #f0f0f0; |
|
|
padding-bottom: 10px; |
|
|
margin-bottom: 15px; |
|
|
font-size: 18px; |
|
|
} |
|
|
.aibom-property { |
|
|
display: flex; |
|
|
margin: 10px 0; |
|
|
} |
|
|
.property-name { |
|
|
font-weight: bold; |
|
|
width: 200px; |
|
|
color: #34495e; |
|
|
} |
|
|
.property-value { |
|
|
flex: 1; |
|
|
color: #555; |
|
|
line-height: 1.6; |
|
|
} |
|
|
.aibom-tabs { |
|
|
display: flex; |
|
|
border-bottom: 1px solid #e9ecef; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
.aibom-tab { |
|
|
padding: 12px 20px; |
|
|
cursor: pointer; |
|
|
background-color: #f8f9fa; |
|
|
margin-right: 5px; |
|
|
border-radius: 8px 8px 0 0; |
|
|
font-weight: 500; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
.aibom-tab.active { |
|
|
background-color: #6c7a89; |
|
|
color: white; |
|
|
} |
|
|
.aibom-tab:hover:not(.active) { |
|
|
background-color: #e9ecef; |
|
|
} |
|
|
.tab-content { |
|
|
display: none; |
|
|
} |
|
|
.tab-content.active { |
|
|
display: block; |
|
|
} |
|
|
.json-view { |
|
|
background-color: #f8f9fa; |
|
|
border: 1px solid #e9ecef; |
|
|
border-radius: 8px; |
|
|
padding: 20px; |
|
|
overflow: auto; |
|
|
max-height: 500px; |
|
|
font-family: monospace; |
|
|
line-height: 1.5; |
|
|
} |
|
|
.collapsible { |
|
|
cursor: pointer; |
|
|
position: relative; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
.collapsible:after { |
|
|
content: '+'; |
|
|
position: absolute; |
|
|
right: 10px; |
|
|
font-weight: bold; |
|
|
} |
|
|
.collapsible.active:after { |
|
|
content: '-'; |
|
|
} |
|
|
.collapsible-content { |
|
|
max-height: 0; |
|
|
overflow: hidden; |
|
|
transition: max-height 0.3s ease-out; |
|
|
} |
|
|
.collapsible-content.active { |
|
|
max-height: 500px; |
|
|
} |
|
|
.tag { |
|
|
display: inline-block; |
|
|
background-color: #e9ecef; |
|
|
padding: 4px 10px; |
|
|
border-radius: 16px; |
|
|
margin: 3px; |
|
|
font-size: 0.9em; |
|
|
} |
|
|
.key-info { |
|
|
background-color: #e3f2fd; |
|
|
border-left: 4px solid #2196F3; |
|
|
padding: 20px; |
|
|
margin-bottom: 20px; |
|
|
border-radius: 8px; |
|
|
} |
|
|
|
|
|
|
|
|
.progress-container { |
|
|
width: 100%; |
|
|
background-color: #f1f1f1; |
|
|
border-radius: 8px; |
|
|
margin: 8px 0; |
|
|
overflow: hidden; |
|
|
} |
|
|
.progress-bar { |
|
|
height: 24px; |
|
|
border-radius: 8px; |
|
|
text-align: center; |
|
|
line-height: 24px; |
|
|
color: white; |
|
|
font-size: 14px; |
|
|
font-weight: 500; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
transition: width 0.5s ease; |
|
|
} |
|
|
.progress-excellent { |
|
|
background-color: #4CAF50; |
|
|
} |
|
|
.progress-good { |
|
|
background-color: #2196F3; |
|
|
} |
|
|
.progress-fair { |
|
|
background-color: #FF9800; |
|
|
} |
|
|
.progress-poor { |
|
|
background-color: #f44336; |
|
|
} |
|
|
.score-table { |
|
|
width: 100%; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
.score-table th { |
|
|
text-align: left; |
|
|
padding: 12px; |
|
|
background-color: #f8f9fa; |
|
|
} |
|
|
.score-table td { |
|
|
padding: 12px; |
|
|
} |
|
|
|
|
|
.score-table th:nth-child(1), .score-table td:nth-child(1) { |
|
|
width: 25%; |
|
|
} |
|
|
.score-table th:nth-child(2), .score-table td:nth-child(2) { |
|
|
width: 20%; |
|
|
} |
|
|
.score-table th:nth-child(3), .score-table td:nth-child(3) { |
|
|
width: 15%; |
|
|
} |
|
|
.score-table th:nth-child(4), .score-table td:nth-child(4) { |
|
|
width: 40%; |
|
|
} |
|
|
.score-weight { |
|
|
font-size: 0.9em; |
|
|
color: #666; |
|
|
margin-left: 5px; |
|
|
} |
|
|
.score-label { |
|
|
display: inline-block; |
|
|
padding: 3px 8px; |
|
|
border-radius: 4px; |
|
|
color: white; |
|
|
font-size: 0.9em; |
|
|
margin-left: 5px; |
|
|
background-color: transparent; |
|
|
} |
|
|
.total-score-container { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
margin-bottom: 25px; |
|
|
background-color: white; |
|
|
padding: 20px; |
|
|
border-radius: 8px; |
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.05); |
|
|
} |
|
|
.total-score { |
|
|
font-size: 28px; |
|
|
font-weight: bold; |
|
|
margin-right: 20px; |
|
|
color: #2c3e50; |
|
|
} |
|
|
.total-progress { |
|
|
flex: 1; |
|
|
} |
|
|
|
|
|
|
|
|
.tooltip { |
|
|
position: relative; |
|
|
display: inline-block; |
|
|
cursor: help; |
|
|
} |
|
|
.tooltip .tooltiptext { |
|
|
visibility: hidden; |
|
|
width: 300px; |
|
|
background-color: #34495e; |
|
|
color: #fff; |
|
|
text-align: left; |
|
|
border-radius: 6px; |
|
|
padding: 12px; |
|
|
position: absolute; |
|
|
z-index: 1; |
|
|
bottom: 125%; |
|
|
left: 50%; |
|
|
margin-left: -150px; |
|
|
opacity: 0; |
|
|
transition: opacity 0.3s; |
|
|
font-size: 0.9em; |
|
|
line-height: 1.5; |
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.1); |
|
|
} |
|
|
.tooltip:hover .tooltiptext { |
|
|
visibility: visible; |
|
|
opacity: 1; |
|
|
} |
|
|
.tooltip .tooltiptext::after { |
|
|
content: ""; |
|
|
position: absolute; |
|
|
top: 100%; |
|
|
left: 50%; |
|
|
margin-left: -5px; |
|
|
border-width: 5px; |
|
|
border-style: solid; |
|
|
border-color: #34495e transparent transparent transparent; |
|
|
} |
|
|
.missing-fields { |
|
|
background-color: #ffebee; |
|
|
border-left: 4px solid #f44336; |
|
|
padding: 20px; |
|
|
margin: 20px 0; |
|
|
border-radius: 8px; |
|
|
} |
|
|
.missing-fields h4 { |
|
|
margin-top: 0; |
|
|
color: #d32f2f; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
.missing-fields ul { |
|
|
margin-bottom: 0; |
|
|
padding-left: 20px; |
|
|
} |
|
|
.recommendations { |
|
|
background-color: #e8f5e9; |
|
|
border-left: 4px solid #4caf50; |
|
|
padding: 20px; |
|
|
margin: 20px 0; |
|
|
border-radius: 8px; |
|
|
} |
|
|
.recommendations h4 { |
|
|
margin-top: 0; |
|
|
color: #2e7d32; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
.recommendations ul { |
|
|
margin-bottom: 0; |
|
|
padding-left: 20px; |
|
|
} |
|
|
.importance-indicator { |
|
|
display: inline-block; |
|
|
margin-left: 5px; |
|
|
} |
|
|
.high-importance { |
|
|
color: #d32f2f; |
|
|
} |
|
|
.medium-importance { |
|
|
color: #ff9800; |
|
|
} |
|
|
.low-importance { |
|
|
color: #2196f3; |
|
|
} |
|
|
.scoring-rubric { |
|
|
background-color: #e3f2fd; |
|
|
border-left: 4px solid #2196f3; |
|
|
padding: 20px; |
|
|
margin: 20px 0; |
|
|
border-radius: 8px; |
|
|
} |
|
|
.scoring-rubric h4 { |
|
|
margin-top: 0; |
|
|
color: #1565c0; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
.scoring-rubric table { |
|
|
width: 100%; |
|
|
margin-top: 15px; |
|
|
} |
|
|
.scoring-rubric th, .scoring-rubric td { |
|
|
padding: 10px; |
|
|
text-align: left; |
|
|
} |
|
|
.note-box { |
|
|
background-color: #fffbea; |
|
|
border-left: 4px solid #ffc107; |
|
|
padding: 20px; |
|
|
margin: 20px 0; |
|
|
border-radius: 8px; |
|
|
} |
|
|
.download-section { |
|
|
margin: 20px 0; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
} |
|
|
.download-section p { |
|
|
margin: 0; |
|
|
margin-right: 15px; |
|
|
} |
|
|
|
|
|
|
|
|
.completeness-profile { |
|
|
background-color: #f6f5f5; |
|
|
border-radius: 8px; |
|
|
padding: 20px; |
|
|
margin: 20px 0; |
|
|
border-left: 4px solid #7f7c7c; |
|
|
} |
|
|
.profile-badge { |
|
|
display: inline-block; |
|
|
padding: 5px 12px; |
|
|
border-radius: 20px; |
|
|
color: white; |
|
|
font-weight: bold; |
|
|
margin-right: 10px; |
|
|
} |
|
|
.profile-basic { |
|
|
background-color: #ff9800; |
|
|
} |
|
|
.profile-standard { |
|
|
background-color: #2196f3; |
|
|
} |
|
|
.profile-advanced { |
|
|
background-color: #4caf50; |
|
|
} |
|
|
|
|
|
.profile-incomplete { |
|
|
background-color: #f44336; |
|
|
color: white; |
|
|
} |
|
|
.field-tier { |
|
|
display: inline-block; |
|
|
width: 12px; |
|
|
height: 12px; |
|
|
border-radius: 50%; |
|
|
margin-right: 5px; |
|
|
} |
|
|
.tier-critical { |
|
|
background-color: #d32f2f; |
|
|
} |
|
|
.tier-important { |
|
|
background-color: #ff9800; |
|
|
} |
|
|
.tier-supplementary { |
|
|
background-color: #2196f3; |
|
|
} |
|
|
.tier-legend { |
|
|
display: flex; |
|
|
margin: 15px 0; |
|
|
font-size: 0.9em; |
|
|
} |
|
|
.tier-legend-item { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
margin-right: 20px; |
|
|
} |
|
|
|
|
|
.validation-penalty-info { |
|
|
background-color: #fff3e0; |
|
|
border-left: 4px solid #ff9800; |
|
|
padding: 20px; |
|
|
margin: 20px 0; |
|
|
border-radius: 8px; |
|
|
font-size: 0.95em; |
|
|
} |
|
|
.validation-penalty-info h4 { |
|
|
margin-top: 0; |
|
|
color: #e65100; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
|
|
|
|
|
|
.validation-warning-box { |
|
|
background-color: #fff3e0; |
|
|
border: 1px solid #ff9800; |
|
|
border-left: 4px solid #ff9800; |
|
|
border-radius: 8px; |
|
|
padding: 20px; |
|
|
margin: 20px 0; |
|
|
box-shadow: 0 2px 10px rgba(255, 152, 0, 0.1); |
|
|
} |
|
|
.validation-warning-box h4 { |
|
|
margin-top: 0; |
|
|
color: #e65100; |
|
|
margin-bottom: 15px; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
} |
|
|
.validation-warning-box .warning-icon { |
|
|
margin-right: 10px; |
|
|
font-size: 1.2em; |
|
|
} |
|
|
.validation-warning-box .issue-summary { |
|
|
margin-bottom: 15px; |
|
|
line-height: 1.6; |
|
|
} |
|
|
.validation-warning-box .issue-details { |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
.validation-warning-box .issue-list { |
|
|
margin: 10px 0; |
|
|
padding-left: 20px; |
|
|
} |
|
|
.validation-warning-box .issue-list li { |
|
|
margin-bottom: 8px; |
|
|
line-height: 1.5; |
|
|
} |
|
|
.validation-warning-box .call-to-action { |
|
|
margin-top: 15px; |
|
|
padding-top: 15px; |
|
|
border-top: 1px solid #ffcc80; |
|
|
} |
|
|
.validation-warning-box .call-to-action p { |
|
|
margin-bottom: 10px; |
|
|
} |
|
|
.issue-tracker-link { |
|
|
display: inline-block; |
|
|
padding: 8px 16px; |
|
|
background-color: #3498db; |
|
|
color: white; |
|
|
text-decoration: none; |
|
|
border-radius: 4px; |
|
|
font-weight: 500; |
|
|
transition: background-color 0.3s; |
|
|
} |
|
|
.issue-tracker-link:hover { |
|
|
background-color: #2980b9; |
|
|
text-decoration: none; |
|
|
} |
|
|
|
|
|
|
|
|
.category-table { |
|
|
margin-bottom: 30px; |
|
|
} |
|
|
.category-table h4 { |
|
|
color: #2c3e50; |
|
|
margin-bottom: 10px; |
|
|
font-size: 18px; |
|
|
} |
|
|
.category-result { |
|
|
background-color: #f8f9fa; |
|
|
padding: 10px; |
|
|
border-radius: 4px; |
|
|
margin-top: 10px; |
|
|
font-weight: bold; |
|
|
} |
|
|
.field-type-legend { |
|
|
background-color: #e3f2fd; |
|
|
border-left: 4px solid #2196f3; |
|
|
padding: 15px; |
|
|
margin: 20px 0; |
|
|
border-radius: 8px; |
|
|
font-size: 0.9em; |
|
|
} |
|
|
.field-type-legend h4 { |
|
|
margin-top: 0; |
|
|
color: #1565c0; |
|
|
margin-bottom: 10px; |
|
|
} |
|
|
.legend-item { |
|
|
display: inline-block; |
|
|
margin-right: 20px; |
|
|
margin-bottom: 5px; |
|
|
} |
|
|
|
|
|
|
|
|
.support-section { |
|
|
background-color: #f8f9fa; |
|
|
border-left: 4px solid #6c757d; |
|
|
padding: 20px; |
|
|
margin: 30px 0; |
|
|
border-radius: 8px; |
|
|
text-align: center; |
|
|
} |
|
|
.support-section h3 { |
|
|
margin-top: 0; |
|
|
color: #495057; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
.support-section p { |
|
|
margin-bottom: 15px; |
|
|
color: #6c757d; |
|
|
} |
|
|
.github-button { |
|
|
display: inline-block; |
|
|
padding: 12px 20px; |
|
|
background-color: #3498db; |
|
|
color: white; |
|
|
text-decoration: none; |
|
|
border-radius: 6px; |
|
|
font-weight: 500; |
|
|
transition: background-color 0.3s; |
|
|
} |
|
|
.github-button:hover { |
|
|
background-color: #2980b9; |
|
|
text-decoration: none; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
|
|
|
<div class="header"> |
|
|
<div class="header-left"> |
|
|
<a href="https://genai.owasp.org/" target="_blank"> |
|
|
<img src="https://huggingface.co/spaces/GenAISecurityProject/OWASP-AIBOM-Generator/resolve/main/templates/images/OWASP_GenAI_Security_Project-logo.png" alt="OWASP GenAI Security Project logo"> |
|
|
</a> |
|
|
<div class="header-content"> |
|
|
<h1>OWASP AIBOM Generator</h1> |
|
|
</div> |
|
|
</div> |
|
|
<div class="header-right"> |
|
|
|
|
|
<a href="/" class="generate-another-btn">π Generate Another AIBOM</a> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="key-info"> |
|
|
<h3>π AIBOM Summary for model <span class="model-name">{{ model_id }}</span></h3> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Model:</span> |
|
|
<span class="property-value">{{ model_id }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Generated:</span> |
|
|
<span class="property-value">{{ aibom.metadata.timestamp }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">SBOM Format:</span> |
|
|
<span class="property-value">{{ aibom.bomFormat }} {{ aibom.specVersion }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Serial Number:</span> |
|
|
<span class="property-value">{{ aibom.serialNumber }}</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="download-section"> |
|
|
<p><strong>Download your AIBOM:</strong></p> |
|
|
<button onclick="downloadJSON()">π₯ Download JSON</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
{% if completeness_score.completeness_profile %} |
|
|
<div class="completeness-profile"> |
|
|
<h3>π Completeness Assessment</h3> |
|
|
<div> |
|
|
<span class="profile-badge profile-{{ completeness_score.completeness_profile.name|lower }}"> |
|
|
{{ completeness_score.completeness_profile.name }} |
|
|
</span> |
|
|
<span>{{ completeness_score.completeness_profile.description }}</span> |
|
|
</div> |
|
|
</div> |
|
|
{% endif %} |
|
|
|
|
|
|
|
|
<div class="aibom-viewer"> |
|
|
<div class="aibom-tabs"> |
|
|
<div class="aibom-tab active" onclick="switchTab('human-view')">Human-Friendly View</div> |
|
|
<div class="aibom-tab" onclick="switchTab('field-checklist')">Field Checklist</div> |
|
|
<div class="aibom-tab" onclick="switchTab('score-view')">Score Report</div> |
|
|
<div class="aibom-tab" onclick="switchTab('json-view')">JSON View</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="human-view" class="tab-content active"> |
|
|
<div class="aibom-section"> |
|
|
<h4>π€ AI Model Information</h4> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Name:</span> |
|
|
<span class="property-value">{{ aibom.components[0].name if aibom.components else 'Not specified' }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Type:</span> |
|
|
<span class="property-value">{{ aibom.components[0].type if aibom.components else 'Not specified' }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Version:</span> |
|
|
<span class="property-value">{{ aibom.components[0].version if aibom.components else 'Not specified' }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Description:</span> |
|
|
<span class="property-value">{{ aibom.components[0].description if aibom.components and aibom.components[0].description else 'Not specified' }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">PURL:</span> |
|
|
<span class="property-value">{{ aibom.components[0].purl if aibom.components and aibom.components[0].purl else 'Not specified' }}</span> |
|
|
</div> |
|
|
{% if aibom.components and aibom.components[0].licenses %} |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Licenses:</span> |
|
|
<span class="property-value"> |
|
|
{% for license in aibom.components[0].licenses %} |
|
|
<span class="tag">{{ license.license.id if license.license else 'Unknown' }}</span> |
|
|
{% endfor %} |
|
|
</span> |
|
|
</div> |
|
|
{% endif %} |
|
|
</div> |
|
|
|
|
|
{% if aibom.components and aibom.components[0].modelCard %} |
|
|
<div class="aibom-section"> |
|
|
<h4>π Model Card</h4> |
|
|
{% if aibom.components[0].modelCard.modelParameters %} |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Architecture:</span> |
|
|
<span class="property-value">{{ aibom.components[0].modelCard.modelParameters.modelArchitecture if aibom.components[0].modelCard.modelParameters.modelArchitecture else 'Not specified' }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Task:</span> |
|
|
<span class="property-value">{{ aibom.components[0].modelCard.modelParameters.task if aibom.components[0].modelCard.modelParameters.task else 'Not specified' }}</span> |
|
|
</div> |
|
|
{% endif %} |
|
|
{% if aibom.components[0].modelCard.properties %} |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Additional Properties:</span> |
|
|
<span class="property-value"> |
|
|
{% for prop in aibom.components[0].modelCard.properties %} |
|
|
<span class="tag">{{ prop.name }}: {{ prop.value }}</span> |
|
|
{% endfor %} |
|
|
</span> |
|
|
</div> |
|
|
{% endif %} |
|
|
</div> |
|
|
{% endif %} |
|
|
|
|
|
{% if aibom.externalReferences %} |
|
|
<div class="aibom-section"> |
|
|
<h4>π External References</h4> |
|
|
{% for ref in aibom.externalReferences %} |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">{{ ref.type|title }}:</span> |
|
|
<span class="property-value"><a href="{{ ref.url }}" target="_blank">{{ ref.url }}</a></span> |
|
|
</div> |
|
|
{% endfor %} |
|
|
</div> |
|
|
{% endif %} |
|
|
|
|
|
<div class="aibom-section"> |
|
|
<h4>π οΈ Generation Metadata</h4> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Generated by:</span> |
|
|
<span class="property-value">{{ aibom.metadata.tools.components[0].name if aibom.metadata.tools and aibom.metadata.tools.components else 'Unknown' }}</span> |
|
|
</div> |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Timestamp:</span> |
|
|
<span class="property-value">{{ aibom.metadata.timestamp }}</span> |
|
|
</div> |
|
|
{% if aibom.metadata.component %} |
|
|
<div class="aibom-property"> |
|
|
<span class="property-name">Component Ref:</span> |
|
|
<span class="property-value">{{ aibom.metadata.component['bom-ref'] }}</span> |
|
|
</div> |
|
|
{% endif %} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="field-checklist" class="tab-content"> |
|
|
<div class="content-section"> |
|
|
<h3>Field Checklist & Mapping</h3> |
|
|
|
|
|
|
|
|
<div class="field-type-legend"> |
|
|
<h4>Legend</h4> |
|
|
<div class="legend-item"> |
|
|
<span class="field-tier tier-critical"></span> |
|
|
<span>Critical</span> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<span class="field-tier tier-important"></span> |
|
|
<span>Important</span> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<span class="field-tier tier-supplementary"></span> |
|
|
<span>Supplementary</span> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<strong>CDX</strong> = CycloneDX Standard |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<strong>AI</strong> = AI-Specific Extension |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<p>This breakdown outlines field categories and statuses in the AIBOM generated for model <strong>{{ model_id }}</strong>, showing how each field impacts the completeness score.</p> |
|
|
|
|
|
{% if completeness_score.field_checklist %} |
|
|
|
|
|
<div class="category-table"> |
|
|
<h4>Required Fields Category</h4> |
|
|
<table> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>Status</th> |
|
|
<th>Field Name</th> |
|
|
<th>Actual Location</th> |
|
|
<th>Tier</th> |
|
|
<th>Type</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
{% set required_fields = ['bomFormat', 'specVersion', 'serialNumber', 'version'] %} |
|
|
{% for field in required_fields %} |
|
|
<tr> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
<span class="check-mark">β</span> |
|
|
{% else %} |
|
|
<span class="x-mark">β</span> |
|
|
{% endif %} |
|
|
</td> |
|
|
<td>{{ field }}</td> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
$.{{ field }} |
|
|
{% else %} |
|
|
Not found |
|
|
{% endif %} |
|
|
</td> |
|
|
<td><span class="field-tier tier-critical"></span> Critical</td> |
|
|
<td>CDX</td> |
|
|
</tr> |
|
|
{% endfor %} |
|
|
</tbody> |
|
|
</table> |
|
|
<div class="category-result"> |
|
|
Result: {{ completeness_score.category_details.required_fields.present_fields if completeness_score.category_details else 'N/A' }}/{{ completeness_score.category_details.required_fields.total_fields if completeness_score.category_details else 'N/A' }} present |
|
|
({{ completeness_score.category_details.required_fields.percentage if completeness_score.category_details else 'N/A' }}%) = |
|
|
{{ completeness_score.section_scores.required_fields if completeness_score.section_scores else 'N/A' }}/20 points |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="category-table"> |
|
|
<h4>Metadata Category</h4> |
|
|
<table> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>Status</th> |
|
|
<th>Field Name</th> |
|
|
<th>Actual Location</th> |
|
|
<th>Tier</th> |
|
|
<th>Type</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
{% set metadata_fields = [ |
|
|
('primaryPurpose', 'Critical'), |
|
|
('suppliedBy', 'Critical'), |
|
|
('standardCompliance', 'Supplementary'), |
|
|
('domain', 'Supplementary'), |
|
|
('autonomyType', 'Supplementary') |
|
|
] %} |
|
|
{% for field, tier in metadata_fields %} |
|
|
<tr> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
<span class="check-mark">β</span> |
|
|
{% else %} |
|
|
<span class="x-mark">β</span> |
|
|
{% endif %} |
|
|
</td> |
|
|
<td>{{ field }}</td> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
$.metadata.properties[name="{{ field }}"] |
|
|
{% else %} |
|
|
Not found |
|
|
{% endif %} |
|
|
</td> |
|
|
<td><span class="field-tier tier-{{ tier.lower() }}"></span> {{ tier }}</td> |
|
|
<td>AI</td> |
|
|
</tr> |
|
|
{% endfor %} |
|
|
</tbody> |
|
|
</table> |
|
|
<div class="category-result"> |
|
|
Result: {{ completeness_score.category_details.metadata.present_fields if completeness_score.category_details else 'N/A' }}/{{ completeness_score.category_details.metadata.total_fields if completeness_score.category_details else 'N/A' }} present |
|
|
({{ completeness_score.category_details.metadata.percentage if completeness_score.category_details else 'N/A' }}%) = |
|
|
{{ completeness_score.section_scores.metadata if completeness_score.section_scores else 'N/A' }}/20 points |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="category-table"> |
|
|
<h4>Component Basic Category</h4> |
|
|
<table> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>Status</th> |
|
|
<th>Field Name</th> |
|
|
<th>Actual Location</th> |
|
|
<th>Tier</th> |
|
|
<th>Type</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
{% set component_basic_fields = [ |
|
|
('name', 'Critical'), |
|
|
('type', 'Important'), |
|
|
('purl', 'Important'), |
|
|
('description', 'Important'), |
|
|
('licenses', 'Important') |
|
|
] %} |
|
|
{% for field, tier in component_basic_fields %} |
|
|
<tr> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
<span class="check-mark">β</span> |
|
|
{% else %} |
|
|
<span class="x-mark">β</span> |
|
|
{% endif %} |
|
|
</td> |
|
|
<td>{{ field }}</td> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
$.components[0].{{ field }} |
|
|
{% else %} |
|
|
{% if field == 'description' %} |
|
|
Not found in component level |
|
|
{% else %} |
|
|
Not found |
|
|
{% endif %} |
|
|
{% endif %} |
|
|
</td> |
|
|
<td><span class="field-tier tier-{{ tier.lower() }}"></span> {{ tier }}</td> |
|
|
<td>CDX</td> |
|
|
</tr> |
|
|
{% endfor %} |
|
|
</tbody> |
|
|
</table> |
|
|
<div class="category-result"> |
|
|
Result: {{ completeness_score.category_details.component_basic.present_fields if completeness_score.category_details else 'N/A' }}/{{ completeness_score.category_details.component_basic.total_fields if completeness_score.category_details else 'N/A' }} present |
|
|
({{ completeness_score.category_details.component_basic.percentage if completeness_score.category_details else 'N/A' }}%) = |
|
|
{{ completeness_score.section_scores.component_basic if completeness_score.section_scores else 'N/A' }}/20 points |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="category-table"> |
|
|
<h4>Component Model Card Category</h4> |
|
|
<table> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>Status</th> |
|
|
<th>Field Name</th> |
|
|
<th>Actual Location</th> |
|
|
<th>Tier</th> |
|
|
<th>Type</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
{% set model_card_fields = [ |
|
|
('energyConsumption', 'Important'), |
|
|
('hyperparameter', 'Important'), |
|
|
('limitation', 'Important'), |
|
|
('safetyRiskAssessment', 'Important'), |
|
|
('typeOfModel', 'Important'), |
|
|
('modelExplainability', 'Supplementary'), |
|
|
('energyQuantity', 'Supplementary'), |
|
|
('energyUnit', 'Supplementary'), |
|
|
('informationAboutTraining', 'Supplementary'), |
|
|
('informationAboutApplication', 'Supplementary'), |
|
|
('metric', 'Supplementary'), |
|
|
('metricDecisionThreshold', 'Supplementary'), |
|
|
('modelDataPreprocessing', 'Supplementary'), |
|
|
('useSensitivePersonalInformation', 'Supplementary') |
|
|
] %} |
|
|
{% for field, tier in model_card_fields %} |
|
|
<tr> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
<span class="check-mark">β</span> |
|
|
{% else %} |
|
|
<span class="x-mark">β</span> |
|
|
{% endif %} |
|
|
</td> |
|
|
<td>{{ field }}</td> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
{% if field == 'typeOfModel' %} |
|
|
$.metadata.properties[name="{{ field }}"] |
|
|
{% else %} |
|
|
$.components[0].modelCard.{{ field }} |
|
|
{% endif %} |
|
|
{% else %} |
|
|
Not found |
|
|
{% endif %} |
|
|
</td> |
|
|
<td><span class="field-tier tier-{{ tier.lower() }}"></span> {{ tier }}</td> |
|
|
<td>AI</td> |
|
|
</tr> |
|
|
{% endfor %} |
|
|
</tbody> |
|
|
</table> |
|
|
<div class="category-result"> |
|
|
Result: {{ completeness_score.category_details.component_model_card.present_fields if completeness_score.category_details else 'N/A' }}/{{ completeness_score.category_details.component_model_card.total_fields if completeness_score.category_details else 'N/A' }} present |
|
|
({{ completeness_score.category_details.component_model_card.percentage if completeness_score.category_details else 'N/A' }}%) = |
|
|
{{ completeness_score.section_scores.component_model_card if completeness_score.section_scores else 'N/A' }}/30 points |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="category-table"> |
|
|
<h4>External References Category</h4> |
|
|
<table> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>Status</th> |
|
|
<th>Field Name</th> |
|
|
<th>Actual Location</th> |
|
|
<th>Tier</th> |
|
|
<th>Type</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
{% set external_ref_fields = [('downloadLocation', 'Critical')] %} |
|
|
{% for field, tier in external_ref_fields %} |
|
|
<tr> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
<span class="check-mark">β</span> |
|
|
{% else %} |
|
|
<span class="x-mark">β</span> |
|
|
{% endif %} |
|
|
</td> |
|
|
<td>{{ field }}</td> |
|
|
<td> |
|
|
{% if completeness_score.field_checklist.get(field, '').startswith('β') %} |
|
|
$.externalReferences[type="distribution"] |
|
|
{% else %} |
|
|
Not found |
|
|
{% endif %} |
|
|
</td> |
|
|
<td><span class="field-tier tier-{{ tier.lower() }}"></span> {{ tier }}</td> |
|
|
<td>CDX</td> |
|
|
</tr> |
|
|
{% endfor %} |
|
|
</tbody> |
|
|
</table> |
|
|
<div class="category-result"> |
|
|
Result: {{ completeness_score.category_details.external_references.present_fields if completeness_score.category_details else 'N/A' }}/{{ completeness_score.category_details.external_references.total_fields if completeness_score.category_details else 'N/A' }} present |
|
|
({{ completeness_score.category_details.external_references.percentage if completeness_score.category_details else 'N/A' }}%) = |
|
|
{{ completeness_score.section_scores.external_references if completeness_score.section_scores else 'N/A' }}/10 points |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{% else %} |
|
|
<p>Field checklist data not available.</p> |
|
|
{% endif %} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="score-view" class="tab-content"> |
|
|
<div class="content-section"> |
|
|
<h3>π Completeness Score Report</h3> |
|
|
|
|
|
|
|
|
<div class="total-score-container"> |
|
|
<div class="total-score">{{ (completeness_score.total_score if completeness_score.total_score != "Undefined" else 0)|round(1) }}/100</div> |
|
|
<div class="total-progress"> |
|
|
<div class="progress-container"> |
|
|
{% set score_percent = completeness_score.total_score %} |
|
|
{% if score_percent >= 90 %} |
|
|
{% set score_class = 'progress-excellent' %} |
|
|
{% set score_label = 'Excellent' %} |
|
|
{% elif score_percent >= 70 %} |
|
|
{% set score_class = 'progress-good' %} |
|
|
{% set score_label = 'Good' %} |
|
|
{% elif score_percent >= 50 %} |
|
|
{% set score_class = 'progress-fair' %} |
|
|
{% set score_label = 'Fair' %} |
|
|
{% else %} |
|
|
{% set score_class = 'progress-poor' %} |
|
|
{% set score_label = 'Poor' %} |
|
|
{% endif %} |
|
|
|
|
|
<div class="progress-bar {{ score_class }}" style="width: {{ score_percent }}%"> |
|
|
{{ score_percent|int }}% {{ score_label }} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="note-box"> |
|
|
<h4>Your AIBOM Breakdown</h4> |
|
|
<p><strong>Model:</strong> {{ model_id }}</p> |
|
|
|
|
|
<table class="score-table"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>Category</th> |
|
|
<th>Fields Present</th> |
|
|
<th>Score</th> |
|
|
<th>Progress</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
{% if completeness_score.category_details and completeness_score.section_scores %} |
|
|
{% set categories = [ |
|
|
('Required Fields', 'required_fields', 20), |
|
|
('Metadata', 'metadata', 20), |
|
|
('Component Basic', 'component_basic', 20), |
|
|
('Model Card', 'component_model_card', 30), |
|
|
('External References', 'external_references', 10) |
|
|
] %} |
|
|
{% for display_name, key, max_score in categories %} |
|
|
<tr> |
|
|
<td>{{ display_name }}</td> |
|
|
<td>{{ completeness_score.category_details[key].present_fields }}/{{ completeness_score.category_details[key].total_fields }}</td> |
|
|
<td>{{ completeness_score.section_scores[key]|round(1) }}/{{ max_score }}</td> |
|
|
<td> |
|
|
<div class="progress-container"> |
|
|
{% set percentage = completeness_score.category_details[key].percentage %} |
|
|
{% if percentage >= 80 %} |
|
|
{% set progress_class = "progress-excellent" %} |
|
|
{% elif percentage >= 60 %} |
|
|
{% set progress_class = "progress-good" %} |
|
|
{% elif percentage >= 40 %} |
|
|
{% set progress_class = "progress-fair" %} |
|
|
{% else %} |
|
|
{% set progress_class = "progress-poor" %} |
|
|
{% endif %} |
|
|
<div class="progress-bar {{ progress_class }}" style="width: {{ percentage }}%">{{ percentage|round(0) }}%</div> |
|
|
</div> |
|
|
</td> |
|
|
</tr> |
|
|
{% endfor %} |
|
|
{% else %} |
|
|
<tr><td colspan="4">Breakdown data not available</td></tr> |
|
|
{% endif %} |
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
|
|
|
<p><strong>Calculation:</strong></p> |
|
|
<p>Subtotal: |
|
|
{% if completeness_score.section_scores %} |
|
|
{% for category, score in completeness_score.section_scores.items() %} |
|
|
{{ score|round(1) }}{% if not loop.last %} + {% endif %} |
|
|
{% endfor %} |
|
|
= <strong>{{ completeness_score.subtotal_score|round(1) }}/100</strong> |
|
|
{% else %} |
|
|
<strong>{{ completeness_score.subtotal_score|round(1) }}/100</strong> |
|
|
{% endif %} |
|
|
</p> |
|
|
|
|
|
{% if completeness_score.penalty_applied %} |
|
|
<p>Penalty Applied: <strong>-{{ completeness_score.penalty_percentage }}%</strong> ({{ completeness_score.penalty_reason }})</p> |
|
|
<p>Final Score: {{ completeness_score.subtotal_score|round(1) }} Γ {{ completeness_score.penalty_factor }} = <strong>{{ completeness_score.total_score|round(1) }}/100</strong></p> |
|
|
{% else %} |
|
|
<p>No penalties applied</p> |
|
|
<p>Final Score: <strong>{{ completeness_score.total_score|round(1) }}/100</strong></p> |
|
|
{% endif %} |
|
|
</div> |
|
|
|
|
|
|
|
|
{% if completeness_score.missing_counts %} |
|
|
<div class="missing-fields"> |
|
|
<h4>Missing Fields Summary</h4> |
|
|
<ul> |
|
|
<li><strong>Critical:</strong> {{ completeness_score.missing_counts.critical }} missing</li> |
|
|
<li><strong>Important:</strong> {{ completeness_score.missing_counts.important }} missing</li> |
|
|
<li><strong>Supplementary:</strong> {{ completeness_score.missing_counts.supplementary }} missing</li> |
|
|
</ul> |
|
|
|
|
|
{% if completeness_score.missing_counts.important >= 5 %} |
|
|
<p><strong>Impact:</strong> Missing multiple critical and/or important fields will incur penalties according to the Penalty Structure.</p> |
|
|
{% endif %} |
|
|
</div> |
|
|
{% endif %} |
|
|
|
|
|
|
|
|
{% if completeness_score.recommendations %} |
|
|
<div class="recommendations"> |
|
|
<h4>General Recommendations to Improve AIBOM Completeness</h4> |
|
|
<ul> |
|
|
<li><strong>Required Fields:</strong> Ensure the model is published with a clear name, version, and hosting platform information to allow proper SBOM structuring.</li> |
|
|
<li><strong>Metadata:</strong> Include author or organization name, purpose of the model, and relevant timestamps in the model repository or card.</li> |
|
|
<li><strong>Component Basic:</strong> Provide a descriptive model title, a meaningful description, a valid license, and a consistent version reference (e.g., tags or commits).</li> |
|
|
<li><strong>Model Card:</strong> Fill out structured sections for model parameters, evaluation metrics, limitations, and ethical considerations to enable full transparency.</li> |
|
|
<li><strong>External References:</strong> Add links to source code, datasets, documentation, and versioned download locations to support traceability and reproducibility.</li> |
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="scoring-rubric"> |
|
|
<h4>How AIBOM Completeness is Scored</h4> |
|
|
<p>The completeness score evaluates how well your AIBOM documents the model across five key categories:</p> |
|
|
<ul> |
|
|
<li><strong>Required Fields (20 points):</strong> Basic SBOM structure mandated by CycloneDX</li> |
|
|
<li><strong>Metadata (20 points):</strong> Information about the SBOM generation and model purpose</li> |
|
|
<li><strong>Component Basic (20 points):</strong> Essential model identification and licensing</li> |
|
|
<li><strong>Model Card (30 points):</strong> Detailed AI-specific documentation for transparency</li> |
|
|
<li><strong>External References (10 points):</strong> Links to model resources and documentation</li> |
|
|
</ul> |
|
|
|
|
|
<p><strong>Calculation Method:</strong></p> |
|
|
<p>Each category score = (Present Fields Γ· Total Fields) Γ Maximum Points</p> |
|
|
<p>Subtotal = Sum of all category scores</p> |
|
|
<p>Final Score = Subtotal Γ Penalty Factor (if applicable)</p> |
|
|
<h4>Penalty Structure:</h4> |
|
|
<p><strong>Critical Fields Missing:</strong></p> |
|
|
<ul> |
|
|
<li>0-1 missing: No penalty</li> |
|
|
<li>2-3 missing: 10% penalty (Γ0.9)</li> |
|
|
<li>4+ missing: 20% penalty (Γ0.8)</li> |
|
|
</ul> |
|
|
|
|
|
<p><strong>Important Fields Missing:</strong></p> |
|
|
<ul> |
|
|
<li>0-4 missing: No penalty</li> |
|
|
<li>5+ missing: 5% penalty (Γ0.95)</li> |
|
|
</ul> |
|
|
|
|
|
<p><strong>Note:</strong> Penalties are cumulative and applied to the subtotal. For example, if you have 3 critical fields missing AND 5 important fields missing, both penalties apply: Subtotal Γ 0.9 Γ 0.95 = Final Score.</p> |
|
|
|
|
|
</div> |
|
|
{% endif %} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="json-view" class="tab-content"> |
|
|
<div class="content-section"> |
|
|
<h3>π Raw JSON View</h3> |
|
|
<p>This is the complete AIBOM in CycloneDX JSON format:</p> |
|
|
<div class="json-view"> |
|
|
<pre>{{ aibom|tojson(indent=2) }}</pre> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="support-section"> |
|
|
<h3>π οΈ Need Help or Found an Issue?</h3> |
|
|
<p>If you encountered any problems, found a bug, or have suggestions for improvement, we'd love to hear from you!</p> |
|
|
<a href="https://github.com/aetheris-ai/aibom-generator/issues" target="_blank" class="github-button"> |
|
|
Report Issue on GitHub |
|
|
</a> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="content-section" style="text-align: center;"> |
|
|
<h3>π£οΈ Help Us Spread the Word</h3> |
|
|
<p>If you find this tool useful, share it with your network! <a href="https://genai.owasp.org" target="_blank" rel="noopener noreferrer">https://genai.owasp.org</a></p> |
|
|
<a href="https://www.linkedin.com/sharing/share-offsite/?url=https://huggingface.co/spaces/GenAISecurityProject/OWASP-AIBOM-Generator" target="_blank" rel="noopener noreferrer" style="text-decoration: none;"> |
|
|
<button style="background-color: #0077b5;">π Share on LinkedIn</button> |
|
|
</a> |
|
|
<p style="margin-top: 10px; font-size: 14px;"> |
|
|
Follow us for updates: |
|
|
<a href="https://www.linkedin.com/company/owasp-top-10-for-large-language-model-applications/" target="_blank" rel="noopener noreferrer">@OWASP GenAI Security Project</a> |
|
|
</p> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="content-section" style="text-align: center;"> |
|
|
|
|
|
<div class="sbom-count">π Generated AIBOMs using this tool: <strong>{{ sbom_count if sbom_count else 'N/A' }}</strong></div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="footer"> |
|
|
<p>Β© 2025 OWASP GenAI Security Project | OWASP AIBOM Generator</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
function switchTab(tabId) { |
|
|
|
|
|
var tabContents = document.getElementsByClassName('tab-content'); |
|
|
for (var i = 0; i < tabContents.length; i++) { |
|
|
tabContents[i].classList.remove('active'); |
|
|
} |
|
|
|
|
|
|
|
|
var tabs = document.getElementsByClassName('aibom-tab'); |
|
|
for (var i = 0; i < tabs.length; i++) { |
|
|
tabs[i].classList.remove('active'); |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById(tabId).classList.add('active'); |
|
|
var selectedTab = document.querySelector('.aibom-tab[onclick="switchTab(\'' + tabId + '\')"]'); |
|
|
selectedTab.classList.add('active'); |
|
|
} |
|
|
|
|
|
function toggleCollapsible(element) { |
|
|
element.classList.toggle('active'); |
|
|
var content = element.nextElementSibling; |
|
|
content.classList.toggle('active'); |
|
|
|
|
|
if (content.classList.contains('active')) { |
|
|
content.style.maxHeight = content.scrollHeight + 'px'; |
|
|
} else { |
|
|
content.style.maxHeight = '0'; |
|
|
} |
|
|
} |
|
|
|
|
|
function downloadJSON() { |
|
|
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify({{ aibom|tojson }}, null, 2)); |
|
|
var downloadAnchorNode = document.createElement('a'); |
|
|
downloadAnchorNode.setAttribute("href", dataStr); |
|
|
downloadAnchorNode.setAttribute("download", "{{ model_id|replace('/', '_') }}_aibom.json"); |
|
|
document.body.appendChild(downloadAnchorNode); |
|
|
downloadAnchorNode.click(); |
|
|
downloadAnchorNode.remove(); |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
var collapsibles = document.getElementsByClassName('collapsible'); |
|
|
for (var i = 0; i < collapsibles.length; i++) { |
|
|
toggleCollapsible(collapsibles[i]); |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|