assemblyline-v4-service 4.6.1.dev231__py3-none-any.whl → 4.7.0.dev25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import logging
5
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, TextIO, Union
5
+ from enum import Enum
6
+ from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, TextIO, Union
6
7
 
7
8
  from assemblyline.common import log as al_log
8
9
  from assemblyline.common.attack_map import attack_map, group_map, revoke_map, software_map
@@ -39,7 +40,8 @@ BODY_FORMAT = StringTable('BODY_FORMAT', [
39
40
  ('MULTI', 9),
40
41
  ('DIVIDER', 10), # This is not a real result section and can only be use inside a multi section
41
42
  ('ORDERED_KEY_VALUE', 11),
42
- ('TIMELINE', 12)
43
+ ('TIMELINE', 12),
44
+ ('SANDBOX', 13),
43
45
  ])
44
46
 
45
47
  # This is a StringTable representation of the PROMOTE_TO set of keys in
@@ -410,6 +412,569 @@ class ProcessTreeSectionBody(SectionBody):
410
412
  self._data.append(process.as_primitives())
411
413
 
412
414
 
415
+ class SandboxMachineMetadata:
416
+ """Metadata about the machine where the sandbox analysis took place."""
417
+
418
+ def __init__(
419
+ self,
420
+ # The IP address of the analysis machine.
421
+ ip: Optional[str] = None,
422
+
423
+ # The hypervisor used by the analysis machine (e.g., VMware, KVM).
424
+ hypervisor: Optional[str] = None,
425
+
426
+ # The hostname of the machine used for analysis.
427
+ hostname: Optional[str] = None,
428
+
429
+ # The platform or operating system name (e.g., Windows, Linux, macOS).
430
+ platform: Optional[str] = None,
431
+
432
+ # The version of the operating system.
433
+ version: Optional[str] = None,
434
+
435
+ # The architecture of the operating system (e.g., x86, x64, ARM).
436
+ architecture: Optional[str] = None,
437
+ ):
438
+ self.ip = ip
439
+ self.hypervisor = hypervisor
440
+ self.hostname = hostname
441
+ self.platform = platform
442
+ self.version = version
443
+ self.architecture = architecture
444
+
445
+ def as_primitives(self) -> Dict:
446
+ """Return a JSON-serializable representation."""
447
+ return {
448
+ "ip": self.ip,
449
+ "hypervisor": self.hypervisor,
450
+ "hostname": self.hostname,
451
+ "platform": self.platform,
452
+ "version": self.version,
453
+ "architecture": self.architecture,
454
+ }
455
+
456
+ LookupType = Literal[
457
+ "A", "AAAA", "AFSDB", "APL", "CAA", "CDNSKEY", "CDS", "CERT", "CNAME", "CSYNC",
458
+ "DHCID", "DLV", "DNAME", "DNSKEY", "DS", "EUI48", "EUI64", "HINFO", "HIP",
459
+ "HTTPS", "IPSECKEY", "KEY", "KX", "LOC", "MX", "NAPTR", "NS", "NSEC", "NSEC3",
460
+ "NSEC3PARAM", "OPENPGPKEY", "PTR", "RRSIG", "RP", "SIG", "SMIMEA", "SOA",
461
+ "SRV", "SSHFP", "SVCB", "TA", "TKEY", "TLSA", "TSIG", "TXT", "URI", "ZONEMD"
462
+ ]
463
+
464
+ RequestMethod = Literal[
465
+ "GET", "POST", "PUT", "DELETE", "HEAD", "CONNECT", "OPTIONS", "TRACE", "PATCH",
466
+ "BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "COPY", "LOCK",
467
+ "MKCOL", "MOVE", "NOTIFY", "POLL", "PROPFIND", "PROPPATCH", "SEARCH",
468
+ "SUBSCRIBE", "UNLOCK", "UNSUBSCRIBE", "X-MS-ENUMATTS"
469
+ ]
470
+
471
+ ConnectionType = Literal["http", "dns", "tls", "smtp"]
472
+ ConnectionDirection = Literal["outbound", "inbound", "unknown"]
473
+ SignatureType = Literal["CUCKOO", "YARA", "SIGMA", "SURICATA"]
474
+
475
+ class SandboxMachineMetadata:
476
+ """Information about the sandbox machine used during analysis."""
477
+
478
+ def __init__(
479
+ self,
480
+ # The IP address of the machine used for analysis.
481
+ ip: Optional[str] = None,
482
+
483
+ # The hypervisor type of the machine used for analysis.
484
+ hypervisor: Optional[str] = None,
485
+
486
+ # The hostname of the machine used for analysis.
487
+ hostname: Optional[str] = None,
488
+
489
+ # The operating system platform of the machine (e.g., "Windows", "Linux").
490
+ platform: Optional[str] = None,
491
+
492
+ # The version of the operating system.
493
+ version: Optional[str] = None,
494
+
495
+ # The system architecture of the machine (e.g., "x64", "arm64").
496
+ architecture: Optional[str] = None,
497
+ ):
498
+ self.ip = ip
499
+ self.hypervisor = hypervisor
500
+ self.hostname = hostname
501
+ self.platform = platform
502
+ self.version = version
503
+ self.architecture = architecture
504
+
505
+ def as_primitives(self) -> Dict[str, Any]:
506
+ return {
507
+ "ip": self.ip,
508
+ "hypervisor": self.hypervisor,
509
+ "hostname": self.hostname,
510
+ "platform": self.platform,
511
+ "version": self.version,
512
+ "architecture": self.architecture,
513
+ }
514
+
515
+
516
+ class SandboxAnalysisMetadata:
517
+ """Metadata describing the context and configuration of a sandbox analysis."""
518
+
519
+ def __init__(
520
+ self,
521
+ # The unique identifier of the analysis task.
522
+ task_id: Optional[int] = None,
523
+
524
+ # The timestamp when the analysis started (ISO 8601 format).
525
+ start_time: str = "",
526
+
527
+ # The timestamp when the analysis ended (ISO 8601 format).
528
+ end_time: Optional[str] = None,
529
+
530
+ # The network routing used during analysis (e.g., "Spoofed", "Internet", "Tor", "VPN").
531
+ routing: Optional[str] = None,
532
+
533
+ # The screen resolution or window size used for the sandbox environment.
534
+ window_size: Optional[str] = None,
535
+
536
+ # Metadata describing the machine on which the analysis ran.
537
+ machine_metadata: Optional[SandboxMachineMetadata] = None,
538
+ ):
539
+ self.task_id = task_id
540
+ self.start_time = start_time
541
+ self.end_time = end_time
542
+ self.routing = routing
543
+ self.window_size = window_size
544
+ self.machine_metadata = machine_metadata
545
+
546
+ def as_primitives(self) -> Dict[str, Any]:
547
+ return {
548
+ "task_id": self.task_id,
549
+ "start_time": self.start_time,
550
+ "end_time": self.end_time,
551
+ "routing": self.routing,
552
+ "window_size": self.window_size,
553
+ "machine_metadata": (
554
+ self.machine_metadata.as_primitives() if self.machine_metadata else None
555
+ ),
556
+ }
557
+
558
+
559
+ class SandboxProcessItem:
560
+ """Information about a process observed during sandbox execution."""
561
+
562
+ def __init__(
563
+ self,
564
+ # The executable image name of the process. Default: "<unknown_image>".
565
+ image: str,
566
+
567
+ # The timestamp when the process started (ISO 8601 format).
568
+ start_time: str,
569
+
570
+ # The parent process ID (PPID).
571
+ ppid: Optional[int] = None,
572
+
573
+ # The process ID (PID).
574
+ pid: Optional[int] = None,
575
+
576
+ # The full command line used to start the process.
577
+ command_line: Optional[str] = None,
578
+
579
+ # The timestamp when the process terminated (ISO 8601 format).
580
+ end_time: Optional[str] = None,
581
+
582
+ # The integrity level of the process (e.g., "High", "Medium", "Low").
583
+ integrity_level: Optional[str] = None,
584
+
585
+ # The hash of the executable file for the process (e.g., SHA256).
586
+ image_hash: Optional[str] = None,
587
+
588
+ # The original file name as embedded in the binary metadata.
589
+ original_file_name: Optional[str] = None,
590
+
591
+ # Indicates whether this process was safelisted (whitelisted).
592
+ safelisted: Optional[bool] = False,
593
+
594
+ # The number of file I/O events associated with this process.
595
+ file_count: Optional[int] = 0,
596
+
597
+ # The number of registry modification events associated with this process.
598
+ registry_count: Optional[int] = 0,
599
+ ):
600
+ self.image = image or "<unknown_image>"
601
+ self.start_time = start_time
602
+ self.ppid = ppid
603
+ self.pid = pid
604
+ self.command_line = command_line
605
+ self.end_time = end_time
606
+ self.integrity_level = integrity_level
607
+ self.image_hash = image_hash
608
+ self.original_file_name = original_file_name
609
+ self.safelisted = safelisted
610
+ self.file_count = file_count
611
+ self.registry_count = registry_count
612
+
613
+ def as_primitives(self) -> Dict[str, Any]:
614
+ return {
615
+ "image": self.image,
616
+ "start_time": self.start_time,
617
+ "ppid": self.ppid,
618
+ "pid": self.pid,
619
+ "command_line": self.command_line,
620
+ "end_time": self.end_time,
621
+ "integrity_level": self.integrity_level,
622
+ "image_hash": self.image_hash,
623
+ "original_file_name": self.original_file_name,
624
+ "safelisted": self.safelisted,
625
+ "file_count": self.file_count,
626
+ "registry_count": self.registry_count,
627
+ }
628
+
629
+
630
+
631
+
632
+ class SandboxNetworkDNS:
633
+ """Details of a DNS query observed during sandbox execution."""
634
+
635
+ def __init__(
636
+ self,
637
+ # The domain name requested (queried).
638
+ domain: str,
639
+
640
+ # The DNS lookup type (e.g., "A", "AAAA", "MX").
641
+ lookup_type: LookupType,
642
+
643
+ # A list of IP addresses returned in the DNS response.
644
+ resolved_ips: Optional[List[str]] = None,
645
+
646
+ # A list of domain names returned in the DNS response (for CNAMEs, etc.).
647
+ resolved_domains: Optional[List[str]] = None,
648
+ ):
649
+ self.domain = domain
650
+ self.lookup_type = lookup_type
651
+ self.resolved_ips = resolved_ips or []
652
+ self.resolved_domains = resolved_domains or []
653
+
654
+ def as_primitives(self) -> Dict[str, Any]:
655
+ return {
656
+ "domain": self.domain,
657
+ "lookup_type": self.lookup_type,
658
+ "resolved_ips": self.resolved_ips,
659
+ "resolved_domains": self.resolved_domains,
660
+ }
661
+
662
+
663
+ class SandboxNetworkHTTP:
664
+ """Details of an HTTP request/response observed during sandbox execution."""
665
+
666
+ def __init__(
667
+ self,
668
+ # The URI requested by the process.
669
+ request_uri: str,
670
+
671
+ # Headers included in the HTTP request.
672
+ request_headers: Optional[Dict[str, Any]] = None,
673
+
674
+ # The HTTP request method (e.g., "GET", "POST").
675
+ request_method: Optional[RequestMethod] = None,
676
+
677
+ # Headers included in the HTTP response.
678
+ response_headers: Optional[Dict[str, Any]] = None,
679
+
680
+ # The raw body content of the HTTP request.
681
+ request_body: Optional[str] = None,
682
+
683
+ # The HTTP status code of the response (e.g., 200, 404).
684
+ response_status_code: Optional[int] = None,
685
+
686
+ # The raw body content of the HTTP response.
687
+ response_body: Optional[str] = None,
688
+
689
+ # Metadata about any file contained in the HTTP response body.
690
+ response_content_fileinfo: Optional[Dict[str, Any]] = None,
691
+
692
+ # The MIME type of the HTTP response content.
693
+ response_content_mimetype: Optional[str] = None,
694
+ ):
695
+ self.request_uri = request_uri
696
+ self.request_headers = request_headers or {}
697
+ self.request_method = request_method
698
+ self.response_headers = response_headers or {}
699
+ self.request_body = request_body
700
+ self.response_status_code = response_status_code
701
+ self.response_body = response_body
702
+ self.response_content_fileinfo = response_content_fileinfo
703
+ self.response_content_mimetype = response_content_mimetype
704
+
705
+ def as_primitives(self) -> Dict[str, Any]:
706
+ return {
707
+ "request_uri": self.request_uri,
708
+ "request_headers": self.request_headers,
709
+ "request_method": self.request_method,
710
+ "response_headers": self.response_headers,
711
+ "request_body": self.request_body,
712
+ "response_status_code": self.response_status_code,
713
+ "response_body": self.response_body,
714
+ "response_content_fileinfo": self.response_content_fileinfo,
715
+ "response_content_mimetype": self.response_content_mimetype,
716
+ }
717
+
718
+
719
+ class SandboxNetworkSMTP:
720
+ """Details of an SMTP email transaction observed during sandbox execution."""
721
+
722
+ def __init__(
723
+ self,
724
+ # The sender email address in the SMTP transaction.
725
+ mail_from: str,
726
+
727
+ # A list of recipient email addresses in the SMTP transaction.
728
+ mail_to: List[str],
729
+
730
+ # Information about any attachments transmitted via SMTP.
731
+ attachments: Optional[List[Dict[str, Any]]] = None,
732
+ ):
733
+ self.mail_from = mail_from
734
+ self.mail_to = mail_to
735
+ self.attachments = attachments or []
736
+
737
+ def as_primitives(self) -> Dict[str, Any]:
738
+ return {
739
+ "mail_from": self.mail_from,
740
+ "mail_to": self.mail_to,
741
+ "attachments": self.attachments,
742
+ }
743
+
744
+
745
+ class SandboxNetflowItem:
746
+ """Details of a network flow (connection) observed during sandbox execution."""
747
+
748
+ def __init__(
749
+ self,
750
+ # The destination IP address of the network connection.
751
+ destination_ip: Optional[str] = None,
752
+
753
+ # The destination port number of the connection.
754
+ destination_port: Optional[int] = None,
755
+
756
+ # The transport layer protocol used (e.g., "tcp", "udp").
757
+ transport_layer_protocol: Optional[Literal["tcp", "udp"]] = None,
758
+
759
+ # The direction of the network connection (e.g., "inbound", "outbound").
760
+ direction: Optional["ConnectionDirection"] = None,
761
+
762
+ # The process ID that initiated or owned the network connection.
763
+ process: Optional[int] = None,
764
+
765
+ # The source IP address of the connection.
766
+ source_ip: Optional[str] = None,
767
+
768
+ # The source port number of the connection.
769
+ source_port: Optional[int] = None,
770
+
771
+ # The timestamp when the network event was observed (ISO 8601 format).
772
+ time_observed: Optional[str] = None,
773
+
774
+ # Detailed HTTP request/response data, if the flow is HTTP-related.
775
+ http_details: Optional["SandboxNetworkHTTP"] = None,
776
+
777
+ # Detailed DNS query/response data, if the flow is DNS-related.
778
+ dns_details: Optional["SandboxNetworkDNS"] = None,
779
+
780
+ # Detailed SMTP email data, if the flow is SMTP-related.
781
+ smtp_details: Optional[SandboxNetworkSMTP] = None,
782
+
783
+ # The type or category of the connection (e.g., "download", "upload").
784
+ connection_type: Optional["ConnectionType"] = None,
785
+ ):
786
+ self.destination_ip = destination_ip
787
+ self.destination_port = destination_port
788
+ self.transport_layer_protocol = transport_layer_protocol
789
+ self.direction = direction
790
+ self.process = process
791
+ self.source_ip = source_ip
792
+ self.source_port = source_port
793
+ self.time_observed = time_observed
794
+ self.http_details = http_details
795
+ self.dns_details = dns_details
796
+ self.smtp_details = smtp_details
797
+ self.connection_type = connection_type
798
+
799
+ def as_primitives(self) -> Dict[str, Any]:
800
+ data: Dict[str, Any] = {
801
+ "destination_ip": self.destination_ip,
802
+ "destination_port": self.destination_port,
803
+ "transport_layer_protocol": self.transport_layer_protocol,
804
+ "direction": self.direction,
805
+ "process": self.process,
806
+ "source_ip": self.source_ip,
807
+ "source_port": self.source_port,
808
+ "time_observed": self.time_observed,
809
+ "connection_type": self.connection_type,
810
+ }
811
+
812
+ if self.http_details is not None:
813
+ data["http_details"] = self.http_details.as_primitives()
814
+ if self.dns_details is not None:
815
+ data["dns_details"] = self.dns_details.as_primitives()
816
+ if self.smtp_details is not None:
817
+ data["smtp_details"] = self.smtp_details.as_primitives()
818
+
819
+ return data
820
+
821
+
822
+ class SandboxAttackItem:
823
+ """Describes an ATT&CK technique or tactic detected during sandbox execution."""
824
+
825
+ def __init__(
826
+ self,
827
+ # The MITRE ATT&CK technique ID (e.g., "T1059.001").
828
+ attack_id: str,
829
+
830
+ # The name or pattern describing the attack behavior.
831
+ pattern: str,
832
+
833
+ # The list of categories or tactics associated with this attack.
834
+ categories: List[str] = [],
835
+ ):
836
+ self.attack_id = attack_id
837
+ self.pattern = pattern
838
+ self.categories = categories or []
839
+
840
+ def as_primitives(self) -> Dict[str, Any]:
841
+ return {
842
+ "attack_id": self.attack_id,
843
+ "pattern": self.pattern,
844
+ "categories": self.categories,
845
+ }
846
+
847
+
848
+ class SandboxSignatureItem:
849
+ """Represents a detection signature triggered during analysis."""
850
+
851
+ def __init__(
852
+ self,
853
+ # The name of the detection signature.
854
+ name: str,
855
+
856
+ # The source type of the signature (e.g., "CUCKOO", "YARA", "SIGMA", "SURICATA").
857
+ type: Literal["CUCKOO", "YARA", "SIGMA", "SURICATA"],
858
+
859
+ # The classification of the signature (e.g., "malicious", "benign").
860
+ classification: str,
861
+
862
+ # The list of ATT&CK patterns or related attack metadata linked to this signature.
863
+ attacks: Optional[List[SandboxAttackItem]] = None,
864
+
865
+ # The list of threat actors associated with this signature.
866
+ actors: Optional[List[str]] = None,
867
+
868
+ # The list of malware families linked to this signature.
869
+ malware_families: Optional[List[str]] = None,
870
+
871
+ # A human-readable description of what the signature represents.
872
+ description: Optional[str] = None,
873
+
874
+ # The list of process IDs (PIDs) that triggered the signature.
875
+ pid: Optional[List[int]] = None,
876
+
877
+ # The score or weight associated with the heuristic signature.
878
+ score: Optional[int] = None,
879
+ ):
880
+ self.name = name
881
+ self.type = type
882
+ self.classification = classification
883
+ self.attacks = attacks or []
884
+ self.actors = actors or []
885
+ self.malware_families = malware_families or []
886
+ self.description = description
887
+ self.pid = pid or []
888
+ self.score = score
889
+
890
+ def as_primitives(self) -> Dict[str, Any]:
891
+ return {
892
+ "name": self.name,
893
+ "type": self.type,
894
+ "classification": self.classification,
895
+ "attacks": [a.as_primitives() for a in self.attacks] if self.attacks else [],
896
+ "actors": self.actors,
897
+ "malware_families": self.malware_families,
898
+ "description": self.description,
899
+ "pid": self.pid,
900
+ "score": self.score,
901
+ }
902
+
903
+
904
+
905
+
906
+ class SandboxSectionBody(SectionBody):
907
+ """The main sandbox analysis body containing all observed data and metadata."""
908
+
909
+ def __init__(self) -> None:
910
+ super().__init__(BODY_FORMAT.SANDBOX, body={
911
+ "analysis_information": None,
912
+ "processes": [],
913
+ "network_connections": [],
914
+ "signatures": [],
915
+ })
916
+
917
+ def set_analysis_information(
918
+ self,
919
+ # The name of the sandbox used to perform the analysis.
920
+ sandbox_name: str,
921
+
922
+ # The version of the sandbox software.
923
+ sandbox_version: str,
924
+
925
+ # Metadata about when and how the analysis was executed.
926
+ analysis_metadata: SandboxAnalysisMetadata,
927
+ ) -> None:
928
+ """Set the general analysis information for the sandbox execution."""
929
+ self._data["analysis_information"] = {
930
+ "sandbox_name": sandbox_name,
931
+ "sandbox_version": sandbox_version,
932
+ "analysis_metadata": analysis_metadata.as_primitives(),
933
+ }
934
+
935
+ def add_process(self, process: SandboxProcessItem) -> None:
936
+ """Add a single process to the sandbox result."""
937
+ if not isinstance(process, SandboxProcessItem):
938
+ raise TypeError("Expected SandboxProcessItem")
939
+ self._data["processes"].append(process.as_primitives())
940
+
941
+ def add_processes(self, processes: List[SandboxProcessItem]) -> None:
942
+ """Add multiple processes to the sandbox result."""
943
+ for proc in processes:
944
+ self.add_process(proc)
945
+
946
+ def add_network_connection(self, connection: SandboxNetflowItem) -> None:
947
+ """Add a single network connection to the sandbox result."""
948
+ if not isinstance(connection, SandboxNetflowItem):
949
+ raise TypeError("Expected SandboxNetflowItem")
950
+ self._data["network_connections"].append(connection.as_primitives())
951
+
952
+ def add_network_connections(self, connections: List[SandboxNetflowItem]) -> None:
953
+ """Add multiple network connections to the sandbox result."""
954
+ for conn in connections:
955
+ self.add_network_connection(conn)
956
+
957
+ def add_signature(self, signature: SandboxSignatureItem) -> None:
958
+ """Add a single detection signature to the sandbox result."""
959
+ if not isinstance(signature, SandboxSignatureItem):
960
+ raise TypeError("Expected SandboxSignatureItem")
961
+ self._data["signatures"].append(signature.as_primitives())
962
+
963
+ def add_signatures(self, signatures: List[SandboxSignatureItem]) -> None:
964
+ """Add multiple detection signatures to the sandbox result."""
965
+ for sig in signatures:
966
+ self.add_signature(sig)
967
+
968
+ def as_primitives(self) -> Dict[str, Any]:
969
+ """Return a fully JSON-serializable structure."""
970
+ return {
971
+ "analysis_information": self._data["analysis_information"],
972
+ "processes": self._data["processes"],
973
+ "network_connections": self._data["network_connections"],
974
+ "signatures": self._data["signatures"],
975
+ }
976
+
977
+
413
978
  class TableRow(dict):
414
979
  def __init__(self, *args, **kwargs) -> None:
415
980
  data = {}
@@ -474,7 +1039,7 @@ class TimelineSectionBody(SectionBody):
474
1039
  def add_node(self, title: str, content: str, opposite_content: str,
475
1040
  icon: str = None, signatures: List[str] = [], score: int = 0) -> None:
476
1041
  self._data.append(dict(title=title, content=content, opposite_content=opposite_content,
477
- icon=icon, signatures=signatures, score=score))
1042
+ icon=icon, signatures=signatures, score=score))
478
1043
 
479
1044
 
480
1045
  class ResultSection:
@@ -795,6 +1360,54 @@ class ResultProcessTreeSection(TypeSpecificResultSection):
795
1360
  self.section_body.add_process(process)
796
1361
 
797
1362
 
1363
+ class ResultSandboxSection(TypeSpecificResultSection):
1364
+ """
1365
+ Represents a result section specifically designed for sandbox analysis data.
1366
+ Provides a typed interface to manipulate the underlying SandboxSectionBody.
1367
+ """
1368
+
1369
+ def __init__(self, title_text: Union[str, List], **kwargs):
1370
+ self.section_body: SandboxSectionBody
1371
+ super().__init__(title_text, SandboxSectionBody(), **kwargs)
1372
+
1373
+ def set_analysis_information(
1374
+ self,
1375
+ sandbox_name: Optional[str],
1376
+ sandbox_version: Optional[str],
1377
+ analysis_metadata: Optional[SandboxAnalysisMetadata],
1378
+ ) -> None:
1379
+ """Set the general analysis information for the sandbox execution."""
1380
+ self.section_body.set_analysis_information(
1381
+ sandbox_name,
1382
+ sandbox_version,
1383
+ analysis_metadata,
1384
+ )
1385
+
1386
+ def add_process(self, process: SandboxProcessItem) -> None:
1387
+ """Add a single process to the sandbox result."""
1388
+ self.section_body.add_process(process)
1389
+
1390
+ def add_processes(self, processes: List[SandboxProcessItem]) -> None:
1391
+ """Add multiple processes to the sandbox result."""
1392
+ self.section_body.add_processes(processes)
1393
+
1394
+ def add_network_connection(self, connection: SandboxNetflowItem) -> None:
1395
+ """Add a single network connection to the sandbox result."""
1396
+ self.section_body.add_network_connection(connection)
1397
+
1398
+ def add_network_connections(self, connections: List[SandboxNetflowItem]) -> None:
1399
+ """Add multiple network connections to the sandbox result."""
1400
+ self.section_body.add_network_connections(connections)
1401
+
1402
+ def add_signature(self, signature: SandboxSignatureItem) -> None:
1403
+ """Add a single detection signature to the sandbox result."""
1404
+ self.section_body.add_signature(signature)
1405
+
1406
+ def add_signatures(self, signatures: List[SandboxSignatureItem]) -> None:
1407
+ """Add multiple detection signatures to the sandbox result."""
1408
+ self.section_body.add_signatures(signatures)
1409
+
1410
+
798
1411
  class ResultTableSection(TypeSpecificResultSection):
799
1412
  def __init__(self, title_text: Union[str, List], **kwargs):
800
1413
  self.section_body: TableSectionBody
@@ -2,9 +2,9 @@ import json
2
2
  import logging
3
3
  import os
4
4
  import tempfile
5
- from typing import Any, Dict, List, Optional, Union
5
+ from typing import Any, Dict, List, Optional
6
6
 
7
- from assemblyline_v4_service.common.api import PrivilegedServiceAPI, ServiceAPI
7
+ from assemblyline_v4_service.common.api import ServiceAPI
8
8
  from assemblyline_v4_service.common.helper import get_service_manifest
9
9
  from assemblyline_v4_service.common.result import Result
10
10
 
@@ -76,7 +76,7 @@ class Task:
76
76
  self.service_config: Dict[str, Any] = dict(task.service_config)
77
77
  self.service_context: Optional[str] = None
78
78
  self.service_debug_info: Optional[str] = None
79
- self.service_default_result_classification = None
79
+ self.service_default_result_classification: Optional[str] = None
80
80
  self.service_name: str = task.service_name
81
81
  self.service_tool_version: Optional[str] = None
82
82
  self.service_version: Optional[str] = None
@@ -88,7 +88,7 @@ class Task:
88
88
  }
89
89
 
90
90
  def _add_file(self, path: str, name: str, description: str,
91
- classification: Optional[Classification] = None,
91
+ classification: Optional[str] = None,
92
92
  is_section_image: bool = False,
93
93
  is_supplementary: bool = False,
94
94
  allow_dynamic_recursion: bool = False,
@@ -131,7 +131,7 @@ class Task:
131
131
 
132
132
  def add_extracted(self, path: str, name: str, description: str,
133
133
  classification: Optional[Classification] = None,
134
- safelist_interface: Optional[Union[ServiceAPI, PrivilegedServiceAPI]] = None,
134
+ safelist_interface: Optional[ServiceAPI] = None,
135
135
  allow_dynamic_recursion: bool = False, parent_relation: str = PARENT_RELATION.EXTRACTED) -> bool:
136
136
 
137
137
  # Service-based safelisting of files has to be configured at the global configuration
@@ -301,7 +301,7 @@ class Task:
301
301
  def set_service_context(self, context: str) -> None:
302
302
  self.service_context = context
303
303
 
304
- def start(self, service_default_result_classification: Classification,
304
+ def start(self, service_default_result_classification: str,
305
305
  service_version: str, service_tool_version: Optional[str] = None) -> None:
306
306
  self.service_version = service_version
307
307
  self.service_tool_version = service_tool_version