Repository: DataDog/guarddog

Issue:
Add Mach-O magic bytes to the bundled binary detector
The [bundled binary detector](https://github.com/DataDog/guarddog/blob/v2.1.0/guarddog/analyzer/metadata/bundled_binary.py#L17) currently detects **ELF** (Unix) and **exe** (Windows) files, but it does not account for the magic bytes for **Mach-O** (Mac) files. This could lead to malicious packages targeting Macs to go unnoticed.

For reference:

![Image](https://github.com/user-attachments/assets/7369b953-b3ea-4d0f-a536-70d84d2056ab)

See https://en.wikipedia.org/wiki/Mach-O#Mach-O_header for more info.

Discussion:
@ocku: Let me know if I can work on this :)@sobregosodd: Hello @ocku , the case you point out seems valid as well
Please, send the PR well look into it.

Thanks again!@ocku: ^ Created. Sorry for the delay! I had some issues with GitHub.

Solution patch:
diff --git a/guarddog/analyzer/metadata/bundled_binary.py b/guarddog/analyzer/metadata/bundled_binary.py
index 5b6e875..81ed040 100644
--- a/guarddog/analyzer/metadata/bundled_binary.py
+++ b/guarddog/analyzer/metadata/bundled_binary.py
@@ -14,7 +14,12 @@ class BundledBinary(Detector):
 
     # magic bytes are the first few bytes of a file that can be used to identify the file type
     # regardless of their extension
-    magic_bytes = {"exe": b"\x4D\x5A", "elf": b"\x7F\x45\x4C\x46"}
+    magic_bytes = {
+        "exe": b"\x4D\x5A",
+        "elf": b"\x7F\x45\x4C\x46",
+        "macho32": b"\xFE\xED\xFA\xCE",
+        "macho64": b"\xFE\xED\xFA\xCF",
+    }
 
     def __init__(self):
         super().__init__(


Test patch:
diff --git a/tests/analyzer/metadata/test_bundled_binary.py b/tests/analyzer/metadata/test_bundled_binary.py
index 07f1845..7a01283 100644
--- a/tests/analyzer/metadata/test_bundled_binary.py
+++ b/tests/analyzer/metadata/test_bundled_binary.py
@@ -19,6 +19,8 @@ class TestBundleBinary:
     binary_sample_elf = (
         b"\x7F\x45\x4C\x46" + b"0x90" * 10
     )  # elf magic number plus nop sled
+    binary_sample_macho32 = b"\xFE\xED\xFA\xCE" + b"0x90" * 10
+    binary_sample_macho64 = b"\xFE\xED\xFA\xCF" + b"0x90" * 10
 
     @pytest.mark.parametrize(
         "detector",
@@ -52,6 +54,38 @@ class TestBundleBinary:
             matches, _ = detector.detect({}, dir)
             assert matches
 
+    @pytest.mark.parametrize(
+        "detector",
+        [
+            (pypi_detector),
+            (npm_detector),
+        ],
+    )
+    def test_macho32(self, detector: BundledBinary):
+        with tempfile.TemporaryDirectory() as dir:
+            full_path = os.path.join(dir, "package")
+            os.mkdir(full_path)
+            with open(os.path.join(full_path, "linux.txt"), "wb") as f:
+                f.write(self.binary_sample_macho32)
+            matches, _ = detector.detect({}, dir)
+            assert matches
+
+    @pytest.mark.parametrize(
+        "detector",
+        [
+            (pypi_detector),
+            (npm_detector),
+        ],
+    )
+    def test_macho64(self, detector: BundledBinary):
+        with tempfile.TemporaryDirectory() as dir:
+            full_path = os.path.join(dir, "package")
+            os.mkdir(full_path)
+            with open(os.path.join(full_path, "linux.txt"), "wb") as f:
+                f.write(self.binary_sample_macho64)
+            matches, _ = detector.detect({}, dir)
+            assert matches
+
     @pytest.mark.parametrize(
         "detector",
         [