diff --git a/tern/classes/package.py b/tern/classes/package.py
index c83423b..be36fad 100644
--- a/tern/classes/package.py
+++ b/tern/classes/package.py
@@ -202,3 +202,22 @@ class Package:
             if value != other_pkg_dict[key]:
                 return False
         return True
+
+    def merge(self, other):
+        '''Compare another Package object to this instance. If the name and
+        version are the same, we use the other object to fill in missing
+        metadata in the first one excluding the files and origins. This
+        method can be used in situations where an external scanner is used to
+        collect package data that we didn't find ourselves'''
+        if not isinstance(other, Package):
+            return False
+        if self.name == other.name and self.version == other.version:
+            other_pkg_dict = other.to_dict()
+            for key, value in self.to_dict().items():
+                if value == '' and other_pkg_dict[key] != '':
+                    setattr(self, key, other_pkg_dict[key])
+                    for lic in other.pkg_licenses:
+                        if lic not in self.pkg_licenses:
+                            self.pkg_licenses.append(lic)
+            return True
+        return False
diff --git a/tern/extensions/scancode/executor.py b/tern/extensions/scancode/executor.py
index 884759c..675e1e1 100644
--- a/tern/extensions/scancode/executor.py
+++ b/tern/extensions/scancode/executor.py
@@ -22,6 +22,7 @@ from tern.analyze.passthrough import get_filesystem_command
 from tern.analyze import common
 from tern.classes.notice import Notice
 from tern.classes.file_data import FileData
+from tern.classes.package import Package
 from tern.extensions.executor import Executor
 from tern.utils import constants
 from tern.utils import rootfs
@@ -74,6 +75,20 @@ def get_scancode_file(file_dict):
     return fd
 
 
+def get_scancode_package(package_dict):
+    '''Given a package dictionary from the scancode results, return a Package
+    object with the results'''
+    package = Package(package_dict['name'])
+    package.version = package_dict['version']
+    package.pkg_license = package_dict['declared_license']
+    package.copyright = package_dict['copyright']
+    package.proj_url = package_dict['repository_homepage_url']
+    package.download_url = package_dict['download_url']
+    package.licenses = [package_dict['declared_license'],
+                        package_dict['license_expression']]
+    return package
+
+
 def add_scancode_headers(layer_obj, headers):
     '''Given a list of headers from scancode data, add unique headers to
     the list of existing headers in the layer object'''
@@ -86,10 +101,12 @@ def add_scancode_headers(layer_obj, headers):
 
 def collect_layer_data(layer_obj):
     '''Use scancode to collect data from a layer filesystem. This function will
-    create a FileData object for every file found. After scanning, it will
-    return a list of FileData objects.
+    create FileData and Package objects for every File and Package found. After
+    scanning, it will return a tuple with a list of FileData and a list of
+    Package objects.
     '''
     files = []
+    packages = []
     # run scancode against a directory
     command = 'scancode -ilpcu --quiet --timeout 300 --json -'
     full_cmd = get_filesystem_command(layer_obj, command)
@@ -107,7 +124,9 @@ def collect_layer_data(layer_obj):
         for f in data['files']:
             if f['type'] == 'file' and f['size'] != 0:
                 files.append(get_scancode_file(f))
-    return files
+                for package in f['packages']:
+                    packages.append(get_scancode_package(package))
+    return files, packages
 
 
 def add_file_data(layer_obj, collected_files):
@@ -123,6 +142,17 @@ def add_file_data(layer_obj, collected_files):
                 break
 
 
+def add_package_data(layer_obj, collected_packages):
+    '''Use the package data collected with scancode to fill in the package data
+    for an ImageLayer object'''
+    for collected_package in collected_packages:
+        for package in layer_obj.packages:
+            if package.merge(collected_package):
+                break
+        # If the package wasn't in the layer, add it
+        layer_obj.packages.append(collected_package)
+
+
 class Scancode(Executor):
     '''Execute scancode'''
     def execute(self, image_obj, redo=False):
@@ -134,9 +164,10 @@ class Scancode(Executor):
             common.load_from_cache(layer)
             if redo or not layer.files_analyzed:
                 # the layer doesn't have analyzed files, so run analysis
-                file_list = collect_layer_data(layer)
+                file_list, package_list = collect_layer_data(layer)
                 if file_list:
                     add_file_data(layer, file_list)
                     layer.files_analyzed = True
+                add_package_data(layer, package_list)
         # save data to the cache
         common.save_to_cache(image_obj)

Include packages collected by scancode in reports
**Describe the Feature**
Currently, the scancode extension only collects file level data even though we also ask it to look for packages
```
# run scancode against a directory
command = 'scancode -ilpcu --quiet --timeout 300 --json -' <-- p stands for packages
```

**Use Cases**
Scancode is a complete static analysis method, so no mounting and and running a process is needed. We can use this method to address #755.

**Implementation Changes**
The executor needs to collect package information and store it in the `packages` list for `ImageLayer` objects.

diff --git a/tests/test_class_package.py b/tests/test_class_package.py
index 32648e7..ab21506 100644
--- a/tests/test_class_package.py
+++ b/tests/test_class_package.py
@@ -173,6 +173,23 @@ class TestClassPackage(unittest.TestCase):
         self.assertEqual(p.origins.origins[0].notices[2].message,
                          "No metadata for key: download_url")
 
+    def testMerge(self):
+        p1 = Package('p1')
+        p1.version = '1.0'
+        p1.pkg_licenses = ['license1']
+        p2 = Package('p1')
+        p2.version = '1.0'
+        p2.download_url = 'SomeUrl'
+        p2.checksum = 'abc'
+        p2.pkg_licenses = ['license2']
+        self.assertFalse(p1.merge('astring'))
+        self.assertTrue(p1.merge(p2))
+        self.assertEqual(p1.download_url, 'SomeUrl')
+        self.assertEqual(p1.checksum, 'abc')
+        self.assertEqual(p1.pkg_licenses, ['license1', 'license2'])
+        p2.version = '2.0'
+        self.assertFalse(p1.merge(p2))
+
 
 if __name__ == '__main__':
     unittest.main()

pip install -e.[dev]
parse_log_pytest
requirements.txt
pytest coverage
apt-get update apt-get install -y gcc
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astroid==2.15.8
attrs==24.2.0
bandit==1.7.5
cachetools==5.5.2
certifi @ file:///croot/certifi_1671487769961/work/certifi
chardet==5.2.0
charset-normalizer==3.4.1
colorama==0.4.6
coverage==7.2.7
debut==0.9.9
dill==0.3.7
distlib==0.3.9
docker==4.4.4
dockerfile-parse==0.0.18
dodgy==0.2.1
exceptiongroup==1.2.2
filelock==3.12.2
flake8==3.9.2
flake8-polyfill==1.0.2
gitdb2==2.0.6
GitPython==2.1.15
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
isort==5.11.5
lazy-object-proxy==1.9.0
markdown-it-py==2.2.0
mccabe==0.6.1
mdurl==0.1.2
packaging==24.0
pbr==6.1.1
pep8-naming==0.10.0
platformdirs==4.0.0
pluggy==1.2.0
prospector==1.7.7
pycodestyle==2.7.0
pydocstyle==6.1.1
pyflakes==2.3.1
Pygments==2.17.2
pylint==2.17.7
pylint-celery==0.3
pylint-django==2.5.3
pylint-flask==0.6
pylint-plugin-utils==0.7
pyproject-api==1.5.3
pytest==7.4.4
PyYAML==6.0.1
regex==2024.4.16
requests==2.31.0
requirements-detector==0.7
rich==13.8.1
setoptconf-tmp==0.3.1
six==1.17.0
smmap==5.0.2
smmap2==3.0.1
snowballstemmer==2.2.0
stevedore==3.5.2
-e git+https://github.com/tern-tools/tern.git@888fb74f8852491f4aa1d1edf3a00622a7077e6b#egg=tern
toml==0.10.2
tomli==2.0.1
tomlkit==0.12.5
tox==4.8.0
typed-ast==1.5.5
typing_extensions==4.7.1
urllib3==2.0.7
virtualenv==20.26.6
websocket-client==1.6.1
wrapt==1.16.0
zipp==3.15.0

name: tern
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astroid==2.15.8
      - attrs==24.2.0
      - bandit==1.7.5
      - cachetools==5.5.2
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - colorama==0.4.6
      - coverage==7.2.7
      - debut==0.9.9
      - dill==0.3.7
      - distlib==0.3.9
      - docker==4.4.4
      - dockerfile-parse==0.0.18
      - dodgy==0.2.1
      - exceptiongroup==1.2.2
      - filelock==3.12.2
      - flake8==3.9.2
      - flake8-polyfill==1.0.2
      - gitdb2==2.0.6
      - gitpython==2.1.15
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - isort==5.11.5
      - lazy-object-proxy==1.9.0
      - markdown-it-py==2.2.0
      - mccabe==0.6.1
      - mdurl==0.1.2
      - packaging==24.0
      - pbr==6.1.1
      - pep8-naming==0.10.0
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - prospector==1.7.7
      - pycodestyle==2.7.0
      - pydocstyle==6.1.1
      - pyflakes==2.3.1
      - pygments==2.17.2
      - pylint==2.17.7
      - pylint-celery==0.3
      - pylint-django==2.5.3
      - pylint-flask==0.6
      - pylint-plugin-utils==0.7
      - pyproject-api==1.5.3
      - pytest==7.4.4
      - pyyaml==6.0.1
      - regex==2024.4.16
      - requests==2.31.0
      - requirements-detector==0.7
      - rich==13.8.1
      - setoptconf-tmp==0.3.1
      - six==1.17.0
      - smmap==5.0.2
      - smmap2==3.0.1
      - snowballstemmer==2.2.0
      - stevedore==3.5.2
      - toml==0.10.2
      - tomli==2.0.1
      - tomlkit==0.12.5
      - tox==4.8.0
      - typed-ast==1.5.5
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - virtualenv==20.26.6
      - websocket-client==1.6.1
      - wrapt==1.16.0
      - zipp==3.15.0
prefix: /opt/conda/envs/tern

tests/test_class_package.py::TestClassPackage::testMerge
tests/test_class_package.py::TestClassPackage::testAddFile tests/test_class_package.py::TestClassPackage::testFill tests/test_class_package.py::TestClassPackage::testGetFilePaths tests/test_class_package.py::TestClassPackage::testGetters tests/test_class_package.py::TestClassPackage::testInstance tests/test_class_package.py::TestClassPackage::testIsEqual tests/test_class_package.py::TestClassPackage::testRemoveFile tests/test_class_package.py::TestClassPackage::testSetters tests/test_class_package.py::TestClassPackage::testToDict tests/test_class_package.py::TestClassPackage::testToDictTemplate
tern/classes/package.py tern/extensions/scancode/executor.py
tern/classes/package.py tern/extensions/scancode/executor.py