a1c00l commited on
Commit
9cecae0
·
verified ·
1 Parent(s): c1fd39d

Update src/aibom_generator/utils.py

Browse files
Files changed (1) hide show
  1. src/aibom_generator/utils.py +84 -166
src/aibom_generator/utils.py CHANGED
@@ -1,21 +1,17 @@
1
  """
2
  Utility functions for the AIBOM Generator.
3
  """
 
4
  import json
5
  import logging
6
  import os
7
  import re
8
  import uuid
9
- import requests
10
- import jsonschema
11
  from typing import Dict, List, Optional, Any, Union, Tuple
12
  from enum import Enum
13
 
14
  logger = logging.getLogger(__name__)
15
 
16
- # CycloneDX schema URL for version 1.6
17
- CYCLONEDX_SCHEMA_URL = "https://raw.githubusercontent.com/CycloneDX/specification/master/schema/bom-1.6.schema.json"
18
-
19
  # Validation severity levels
20
  class ValidationSeverity(Enum):
21
  ERROR = "error"
@@ -107,73 +103,6 @@ def validate_spdx(license_entry):
107
  return license_entry in spdx_licenses
108
 
109
 
110
- def _load_cyclonedx_schema():
111
- """
112
- Load the CycloneDX JSON schema from the specified URL.
113
-
114
- Returns:
115
- dict: The loaded schema or a minimal schema if loading fails
116
- """
117
- try:
118
- response = requests.get(CYCLONEDX_SCHEMA_URL)
119
- response.raise_for_status()
120
- schema = response.json()
121
- logger.info(f"Successfully loaded CycloneDX schema from {CYCLONEDX_SCHEMA_URL}")
122
- return schema
123
- except Exception as e:
124
- logger.error(f"Failed to load CycloneDX schema: {e}")
125
- # Fallback to a minimal schema validation
126
- return {
127
- "type": "object",
128
- "required": ["bomFormat", "specVersion", "serialNumber", "version"]
129
- }
130
-
131
-
132
- def _validate_schema(aibom: Dict[str, Any], schema: Dict[str, Any]) -> List[Dict[str, Any]]:
133
- """
134
- Validate an AIBOM against the CycloneDX JSON schema.
135
-
136
- Args:
137
- aibom: The AIBOM to validate
138
- schema: The CycloneDX schema
139
-
140
- Returns:
141
- List of validation issues
142
- """
143
- issues = []
144
-
145
- if not schema:
146
- issues.append({
147
- "severity": ValidationSeverity.WARNING.value,
148
- "code": "SCHEMA_UNAVAILABLE",
149
- "message": "CycloneDX schema unavailable, performing minimal validation",
150
- "path": "$"
151
- })
152
- return issues
153
-
154
- try:
155
- jsonschema.validate(instance=aibom, schema=schema)
156
- except jsonschema.exceptions.ValidationError as e:
157
- # Extract the JSON path where the validation error occurred
158
- path = ".".join(str(p) for p in e.path) if e.path else "$"
159
-
160
- issues.append({
161
- "severity": ValidationSeverity.ERROR.value,
162
- "code": "SCHEMA_VALIDATION_ERROR",
163
- "message": str(e),
164
- "path": path
165
- })
166
- except Exception as e:
167
- issues.append({
168
- "severity": ValidationSeverity.ERROR.value,
169
- "code": "SCHEMA_VALIDATION_EXCEPTION",
170
- "message": f"Unexpected error during schema validation: {str(e)}",
171
- "path": "$"
172
- })
173
-
174
- return issues
175
-
176
-
177
  def _validate_ai_requirements(aibom: Dict[str, Any]) -> List[Dict[str, Any]]:
178
  """
179
  Validate AI-specific requirements for an AIBOM.
@@ -379,13 +308,94 @@ def _generate_validation_recommendations(issues: List[Dict[str, Any]]) -> List[s
379
  return recommendations
380
 
381
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
  def calculate_completeness_score(aibom: Dict[str, Any], validate: bool = True) -> Dict[str, Any]:
383
  """
384
- Calculate completeness score for an AIBOM and optionally validate against CycloneDX schema.
385
 
386
  Args:
387
  aibom: The AIBOM to score and validate
388
- validate: Whether to perform validation against CycloneDX schema
389
 
390
  Returns:
391
  Dictionary containing score and validation results
@@ -517,98 +527,6 @@ def calculate_completeness_score(aibom: Dict[str, Any], validate: bool = True) -
517
  return result
518
 
519
 
520
- def validate_aibom(aibom: Dict[str, Any]) -> Dict[str, Any]:
521
- """
522
- Validate an AIBOM against the CycloneDX schema and AI-specific requirements.
523
-
524
- Args:
525
- aibom: The AIBOM to validate
526
-
527
- Returns:
528
- Validation report with issues and recommendations
529
- """
530
- # Initialize validation report
531
- report = {
532
- "valid": True,
533
- "schema_valid": True,
534
- "ai_valid": True,
535
- "issues": [],
536
- "recommendations": [],
537
- "summary": {
538
- "error_count": 0,
539
- "warning_count": 0,
540
- "info_count": 0
541
- }
542
- }
543
-
544
- # Load schema
545
- schema = _load_cyclonedx_schema()
546
-
547
- # Validate against CycloneDX schema
548
- schema_issues = _validate_schema(aibom, schema)
549
- if schema_issues:
550
- report["schema_valid"] = False
551
- report["valid"] = False
552
- report["issues"].extend(schema_issues)
553
-
554
- # Validate AI-specific requirements
555
- ai_issues = _validate_ai_requirements(aibom)
556
- if ai_issues:
557
- report["ai_valid"] = False
558
- report["valid"] = False
559
- report["issues"].extend(ai_issues)
560
-
561
- # Generate recommendations
562
- report["recommendations"] = _generate_validation_recommendations(report["issues"])
563
-
564
- # Update summary counts
565
- for issue in report["issues"]:
566
- if issue["severity"] == ValidationSeverity.ERROR.value:
567
- report["summary"]["error_count"] += 1
568
- elif issue["severity"] == ValidationSeverity.WARNING.value:
569
- report["summary"]["warning_count"] += 1
570
- elif issue["severity"] == ValidationSeverity.INFO.value:
571
- report["summary"]["info_count"] += 1
572
-
573
- return report
574
-
575
-
576
- def get_validation_summary(report: Dict[str, Any]) -> str:
577
- """
578
- Get a human-readable summary of the validation report.
579
-
580
- Args:
581
- report: Validation report
582
-
583
- Returns:
584
- Human-readable summary
585
- """
586
- if report["valid"]:
587
- summary = "✅ AIBOM is valid and complies with CycloneDX schema and AI requirements.\n"
588
- else:
589
- summary = "❌ AIBOM validation failed.\n"
590
-
591
- summary += f"\nSummary:\n"
592
- summary += f"- Errors: {report['summary']['error_count']}\n"
593
- summary += f"- Warnings: {report['summary']['warning_count']}\n"
594
- summary += f"- Info: {report['summary']['info_count']}\n"
595
-
596
- if not report["valid"]:
597
- summary += "\nIssues:\n"
598
- for issue in report["issues"]:
599
- severity = issue["severity"].upper()
600
- code = issue["code"]
601
- message = issue["message"]
602
- path = issue["path"]
603
- summary += f"- [{severity}] {code}: {message} (at {path})\n"
604
-
605
- summary += "\nRecommendations:\n"
606
- for i, recommendation in enumerate(report["recommendations"], 1):
607
- summary += f"{i}. {recommendation}\n"
608
-
609
- return summary
610
-
611
-
612
  def merge_metadata(primary: Dict[str, Any], secondary: Dict[str, Any]) -> Dict[str, Any]:
613
  result = secondary.copy()
614
  for key, value in primary.items():
 
1
  """
2
  Utility functions for the AIBOM Generator.
3
  """
4
+
5
  import json
6
  import logging
7
  import os
8
  import re
9
  import uuid
 
 
10
  from typing import Dict, List, Optional, Any, Union, Tuple
11
  from enum import Enum
12
 
13
  logger = logging.getLogger(__name__)
14
 
 
 
 
15
  # Validation severity levels
16
  class ValidationSeverity(Enum):
17
  ERROR = "error"
 
103
  return license_entry in spdx_licenses
104
 
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  def _validate_ai_requirements(aibom: Dict[str, Any]) -> List[Dict[str, Any]]:
107
  """
108
  Validate AI-specific requirements for an AIBOM.
 
308
  return recommendations
309
 
310
 
311
+ def validate_aibom(aibom: Dict[str, Any]) -> Dict[str, Any]:
312
+ """
313
+ Validate an AIBOM against AI-specific requirements.
314
+
315
+ Args:
316
+ aibom: The AIBOM to validate
317
+
318
+ Returns:
319
+ Validation report with issues and recommendations
320
+ """
321
+ # Initialize validation report
322
+ report = {
323
+ "valid": True,
324
+ "ai_valid": True,
325
+ "issues": [],
326
+ "recommendations": [],
327
+ "summary": {
328
+ "error_count": 0,
329
+ "warning_count": 0,
330
+ "info_count": 0
331
+ }
332
+ }
333
+
334
+ # Validate AI-specific requirements
335
+ ai_issues = _validate_ai_requirements(aibom)
336
+ if ai_issues:
337
+ report["ai_valid"] = False
338
+ report["valid"] = False
339
+ report["issues"].extend(ai_issues)
340
+
341
+ # Generate recommendations
342
+ report["recommendations"] = _generate_validation_recommendations(report["issues"])
343
+
344
+ # Update summary counts
345
+ for issue in report["issues"]:
346
+ if issue["severity"] == ValidationSeverity.ERROR.value:
347
+ report["summary"]["error_count"] += 1
348
+ elif issue["severity"] == ValidationSeverity.WARNING.value:
349
+ report["summary"]["warning_count"] += 1
350
+ elif issue["severity"] == ValidationSeverity.INFO.value:
351
+ report["summary"]["info_count"] += 1
352
+
353
+ return report
354
+
355
+
356
+ def get_validation_summary(report: Dict[str, Any]) -> str:
357
+ """
358
+ Get a human-readable summary of the validation report.
359
+
360
+ Args:
361
+ report: Validation report
362
+
363
+ Returns:
364
+ Human-readable summary
365
+ """
366
+ if report["valid"]:
367
+ summary = "✅ AIBOM is valid and complies with AI requirements.\n"
368
+ else:
369
+ summary = "❌ AIBOM validation failed.\n"
370
+
371
+ summary += f"\nSummary:\n"
372
+ summary += f"- Errors: {report['summary']['error_count']}\n"
373
+ summary += f"- Warnings: {report['summary']['warning_count']}\n"
374
+ summary += f"- Info: {report['summary']['info_count']}\n"
375
+
376
+ if not report["valid"]:
377
+ summary += "\nIssues:\n"
378
+ for issue in report["issues"]:
379
+ severity = issue["severity"].upper()
380
+ code = issue["code"]
381
+ message = issue["message"]
382
+ path = issue["path"]
383
+ summary += f"- [{severity}] {code}: {message} (at {path})\n"
384
+
385
+ summary += "\nRecommendations:\n"
386
+ for i, recommendation in enumerate(report["recommendations"], 1):
387
+ summary += f"{i}. {recommendation}\n"
388
+
389
+ return summary
390
+
391
+
392
  def calculate_completeness_score(aibom: Dict[str, Any], validate: bool = True) -> Dict[str, Any]:
393
  """
394
+ Calculate completeness score for an AIBOM and optionally validate against AI requirements.
395
 
396
  Args:
397
  aibom: The AIBOM to score and validate
398
+ validate: Whether to perform validation
399
 
400
  Returns:
401
  Dictionary containing score and validation results
 
527
  return result
528
 
529
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
  def merge_metadata(primary: Dict[str, Any], secondary: Dict[str, Any]) -> Dict[str, Any]:
531
  result = secondary.copy()
532
  for key, value in primary.items():