1+ import re
2+
13import pytest
24from socketsecurity .core .messages import Messages
35from socketsecurity .core .classes import Diff , Issue
68class TestGitLabFormat :
79 """Test suite for GitLab Security Dashboard format generation"""
810
11+ # GitLab v15.0.0 schema: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$
12+ GITLAB_TIMESTAMP_RE = re .compile (r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$" )
13+
914 def test_gitlab_report_structure (self ):
10- """Test basic GitLab report structure is valid """
15+ """Test basic GitLab report structure matches v15.0.0 schema requirements """
1116 diff = Diff ()
1217 diff .new_alerts = []
1318 diff .id = "test-scan-id"
1419 diff .diff_url = "https://socket.dev/test"
1520
1621 report = Messages .create_security_comment_gitlab (diff )
1722
18- # Verify required top -level fields
23+ # All four root -level keys required by v15.0.0 schema
1924 assert "version" in report
2025 assert "scan" in report
2126 assert "vulnerabilities" in report
27+ assert "dependency_files" in report
2228
2329 # Verify scan structure
2430 assert report ["scan" ]["type" ] == "dependency_scanning"
@@ -28,6 +34,12 @@ def test_gitlab_report_structure(self):
2834 assert report ["scan" ]["scanner" ]["id" ] == "socket-cli"
2935 assert report ["scan" ]["status" ] == "success"
3036
37+ # Timestamps must match GitLab pattern (no microseconds, no trailing Z)
38+ assert self .GITLAB_TIMESTAMP_RE .match (report ["scan" ]["start_time" ]), \
39+ f"start_time '{ report ['scan' ]['start_time' ]} ' doesn't match GitLab pattern"
40+ assert self .GITLAB_TIMESTAMP_RE .match (report ["scan" ]["end_time" ]), \
41+ f"end_time '{ report ['scan' ]['end_time' ]} ' doesn't match GitLab pattern"
42+
3143 def test_vulnerability_mapping (self ):
3244 """Test Socket Issue maps correctly to GitLab vulnerability"""
3345 diff = Diff ()
@@ -391,3 +403,118 @@ def test_manifests_attribute_fallback(self):
391403 vuln = report ["vulnerabilities" ][0 ]
392404
393405 assert vuln ["location" ]["file" ] == "requirements.txt"
406+
407+ def test_dependency_files_populated_from_alerts (self ):
408+ """Test dependency_files is built from alert locations per v15.0.0 schema"""
409+ diff = Diff ()
410+ diff .id = "test-scan-id"
411+ diff .diff_url = "https://socket.dev/test"
412+
413+ diff .new_alerts = [
414+ Issue (
415+ pkg_name = "pkg-a" ,
416+ pkg_version = "1.0.0" ,
417+ type = "malware" ,
418+ severity = "high" ,
419+ title = "Alert A" ,
420+ manifests = "package.json" ,
421+ pkg_type = "npm" ,
422+ key = "key-a" ,
423+ purl = "pkg:npm/pkg-a@1.0.0"
424+ ),
425+ Issue (
426+ pkg_name = "pkg-b" ,
427+ pkg_version = "2.0.0" ,
428+ type = "vulnerability" ,
429+ severity = "medium" ,
430+ title = "Alert B" ,
431+ manifests = "requirements.txt" ,
432+ pkg_type = "pypi" ,
433+ key = "key-b" ,
434+ purl = "pkg:pypi/pkg-b@2.0.0"
435+ ),
436+ ]
437+
438+ report = Messages .create_security_comment_gitlab (diff )
439+
440+ assert "dependency_files" in report
441+ dep_files = report ["dependency_files" ]
442+ assert len (dep_files ) == 2
443+
444+ paths = {df ["path" ] for df in dep_files }
445+ assert "package.json" in paths
446+ assert "requirements.txt" in paths
447+
448+ for df in dep_files :
449+ assert "path" in df
450+ assert "package_manager" in df
451+ assert "dependencies" in df
452+ assert isinstance (df ["dependencies" ], list )
453+ assert len (df ["dependencies" ]) >= 1
454+ for dep in df ["dependencies" ]:
455+ assert "package" in dep
456+ assert "name" in dep ["package" ]
457+ assert "version" in dep
458+
459+ def test_dependency_files_groups_by_manifest (self ):
460+ """Test multiple alerts from the same manifest are grouped into one dependency_file"""
461+ diff = Diff ()
462+ diff .id = "test-scan-id"
463+ diff .diff_url = "https://socket.dev/test"
464+
465+ diff .new_alerts = [
466+ Issue (
467+ pkg_name = "pkg-a" , pkg_version = "1.0.0" , type = "malware" , severity = "high" ,
468+ title = "A" , manifests = "package.json" , pkg_type = "npm" , key = "k1" , purl = "pkg:npm/pkg-a@1.0.0"
469+ ),
470+ Issue (
471+ pkg_name = "pkg-b" , pkg_version = "2.0.0" , type = "vulnerability" , severity = "low" ,
472+ title = "B" , manifests = "package.json" , pkg_type = "npm" , key = "k2" , purl = "pkg:npm/pkg-b@2.0.0"
473+ ),
474+ ]
475+
476+ report = Messages .create_security_comment_gitlab (diff )
477+ dep_files = report ["dependency_files" ]
478+
479+ assert len (dep_files ) == 1
480+ assert dep_files [0 ]["path" ] == "package.json"
481+ assert dep_files [0 ]["package_manager" ] == "npm"
482+ assert len (dep_files [0 ]["dependencies" ]) == 2
483+
484+ def test_dependency_files_empty_when_no_alerts (self ):
485+ """Test dependency_files is an empty array when there are no alerts"""
486+ diff = Diff ()
487+ diff .id = "test-scan-id"
488+ diff .diff_url = "https://socket.dev/test"
489+ diff .new_alerts = []
490+
491+ report = Messages .create_security_comment_gitlab (diff )
492+ assert report ["dependency_files" ] == []
493+
494+ def test_dependency_files_skips_unknown_manifest (self ):
495+ """Test alerts with unknown manifest don't produce dependency_file entries"""
496+ diff = Diff ()
497+ diff .id = "test-scan-id"
498+ diff .diff_url = "https://socket.dev/test"
499+
500+ diff .new_alerts = [
501+ Issue (
502+ pkg_name = "pkg-a" , pkg_version = "1.0.0" , type = "malware" , severity = "high" ,
503+ title = "A" , pkg_type = "npm" , key = "k1" , purl = "pkg:npm/pkg-a@1.0.0"
504+ ),
505+ ]
506+
507+ report = Messages .create_security_comment_gitlab (diff )
508+ assert report ["dependency_files" ] == []
509+
510+ def test_pkg_type_to_package_manager_mapping (self ):
511+ """Test package manager mapping covers common ecosystems"""
512+ assert Messages ._pkg_type_to_package_manager ("npm" ) == "npm"
513+ assert Messages ._pkg_type_to_package_manager ("pypi" ) == "pip"
514+ assert Messages ._pkg_type_to_package_manager ("go" ) == "go"
515+ assert Messages ._pkg_type_to_package_manager ("maven" ) == "maven"
516+ assert Messages ._pkg_type_to_package_manager ("gem" ) == "bundler"
517+ assert Messages ._pkg_type_to_package_manager ("nuget" ) == "nuget"
518+ assert Messages ._pkg_type_to_package_manager ("cargo" ) == "cargo"
519+ assert Messages ._pkg_type_to_package_manager ("" ) == "unknown"
520+ assert Messages ._pkg_type_to_package_manager ("swift" ) == "swift"
0 commit comments