Repository: spdx/ntia-conformance-checker

Issue:
Update to new edition of minimum elements (2024)
- The 3rd edition of "Framing Software Component Transparency" document is published on October 2024.
  https://www.cisa.gov/resources-tools/resources/framing-software-component-transparency-2024
- We may like to update the conformance checker to aligned with the new edition

Discussion:
@jspeed-meyers: Thank you for pointing this out, @bact! I defer to @goneall and @kestewart. Do you want this tool to reflect the revised criteria in the new document cited above?

If so, that could be a good project for a Google Summer of Code intern.  @bact: Yes, I think that will be useful and help adoption of SPDX.
Although I'm not certain about the scope of SPDX versions we would like to support.

Currently, ntia-conformance-checker supports SPDX 2.3.
To have the support for the Third edition of minimum elements in SPDX 2.3 is one thing.
To have the support for the Third edition of minimum elements also in SPDX 3.0 is another thing.

Considering the experimental support of SPDX 3.0 in [spdx-tools](https://github.com/spdx/tools-python/) dependency, an SPDX 2.3 is probably more feasible in the near term. Although the additional SPDX 3.0 support will do more favour for 3.0 adoption.

--

There is also a similar document from the German Federal Office for Information Security (BSI) that we could consider together:
- [Technical Guideline TR-03183: Cyber Resilience Requirements for Manufacturers and Products Part 2: Software Bill of Materials (SBOM)](https://www.bsi.bund.de/EN/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/Technische-Richtlinien/TR-nach-Thema-sortiert/tr03183/TR-03183_node.html)

(and the Summer of Code is interesting)@jspeed-meyers: To be clear, I support updating the `ntia-conformance-checker` to implement the new edition of the minimum elements:

-  if (A) Gary and Kate support it
- if (B) there is someone or some group of people that can make the necessary changes to the code while ensuring the test suite is also updated properly.

I can't currently justify B personally (at least as a volunteer maintainer), though I would be glad to supervise a GSOC intern or outside contributor to do it, reviewing PRs as necessary and providing advice.@bact: Thank you. Having played around a bit [with tools-python SPDX model](https://github.com/spdx/tools-python/pull/829), I'm happy to look into this.

Let's see how Gary and Kate think about this.@goneall: I personally support updating the minimum elements.  For compatibility, I would suggest that we have a command line option to select which version of the minimum elements is used - the original NTIA or the 3rd edition of the framing document.
@jspeed-meyers: @bact or others: I'm glad to advise on how to implement this change. I suggest creating a design document first (perhaps a comment-only Google document or something similar) since there is potentially a lot to unpack and then arriving at a rough consensus before sending any PRs in.@bact: Thanks. I will try to come up with the design doc, maybe next week.@jspeed-meyers: Sounds good, @bact. And LMK if you want to brainstorm or if I can help in any way. @bact: @jspeed-meyers I have put some notes here: https://docs.google.com/document/d/1pueRxlxoM9n1eG9g6AihjLvybEBTd77m22mRYBQltpg/edit?usp=sharing

Note that [sbomqs](https://github.com/interlynk-io/sbomqs) already supports FSCT v3 on SPDX 2.x, so may need to see what is still missing.@jspeed-meyers: @bact: Nice document.

IIUC, you are proposing to expand this tool's mandate from checking the conformance of an SPDX SBOM with the NTIA minimum elements to checking SPDX SBOM conformance with a range of frameworks. Do I understand your intention correctly?

In general, I want this tool be broadly useful. So I support this motion in the abstract.

I do have two concerns. First, would any users besides yourself find this useful? I worry about expanding the mandate of the tool without clear and strong evidence that MANY users would find this helpful. Second, who will do the creation and maintenance of such a tool? I have become a co-maintainer of this tool (and the company where I work USED to use this tool internally), but there are no longer clear incentives for me to do anything other than basic maintenance. I worry about adding lots of functionality, which will inevitably have bugs, and there being no set of maintainers able to debug and propose fixes.

Anyways, nice document! I support the idea in general.

For me, the most important opinions are those of @goneall and @kestewart. I defer to them. 

@bact: Thank you. Very useful comments.

1) On the demand side

Frankly I don't know. I believe there will be an increasing demand due to regulations and business needs, counting on the existence of a tool like sbomqs.

But of course, as currently sbomqs support more standards and formats (SPDX 2 and CycloneDX), there's no good reason to switch to the tool in this repo.

Unless there's a featue that other tools don't have yet (and that feature is essential enough, which I don't know what it is).

Personally, I want to use this tool to check the conformance of an SBOM against requirements in EU AI Act. This will link to the supply side, the creation and maintenance in the next point.

2) On the supply side

I am willing to create some of these features.

My idea is to start somewhere with less moving parts, to understand how things work, so standard documents like NTIA and FSCTv3 came to my mind. They are quite established and I don't have to worry much about interpretation.

After I understand how to technically check SBOM conformance, I will then continue to apply it with EU AI Act requirements.

I have an incentive for the creation because it will help with my study at university. But of course, one can question the maintenance in the long run after I left university (which personally I hope happens soon).

Maybe to ease the concern, each addition of standard support should be developed in a way that detached from the main program. A feature should be removed easily when required (the feature is no longer maintained and has bug/dependencies that will affect the main program, or the standard document is deprecated/revoked).


@jspeed-meyers: > But of course, as currently sbomqs support more standards and formats (SPDX 2 and CycloneDX), there's no good reason to switch to the tool in this repo.

Yeah, I think `sbomqs` is a perfectly good tool. The advantage, IMO, of this tool, `ntia-conformance-checker`, is not technical but organizational: it is housed within the `spdx` GitHub organization as an officially-supported implementation of NTIA conformance checking for SPDX SBOM documents. But if a user prefers `sbomqs`, then go for it!

> Maybe to ease the concern, each addition of standard support should be developed in a way that detached from the main program.

I do think this would ease my concerns.

And, again, I'm glad to review PRs.

I still have the broader question though of: should this tool include SBOM quality standards beyond the NTIA minimum elements? I'm open to it. But I really do see this as a Gary and Kate question.

Also, I too originally got interested in this tool because of research (see [here](https://www.computer.org/csdl/magazine/sp/2023/06/10315783/1S2UwA5d6mI)), so I understand your situation :)@goneall: I have a couple of opinions on this topic:
- This tool is quite valuable in that it is available through the [online tools](https://tools.spdx.org/) making it quite easy for those in the SPDX community to check conformance.
- I foresee a lot of demand for the 3rd edition framing document conformance checking at the minimum level - the other standards suggested would be nice to have as well, but I have a feeling most people will just be checking for the minimum
- To keep the scope reasonable, I would focus on only well specified published standard (which the proposal seems to do).  Implementing a more general "quality" analysis would be quite a bit of work and seems to be well covered by the sbomqs work.@jspeed-meyers: @bact: Given @goneall's support, I would suggest, at least in the short term, a PR that focuses on the 3rd edition framing document conformance checking at the minimum level, implemented via a new option like the design document above. If you aren't totally opposed to more design document work, I would suggest sketching out what new functions need to be added to implement the 3rd edition framing document conformance checking. Once you and I have a rough consensus there, you can send in a PR.

And everything else can wait until a later day.

Does that satisfy your immediate research needs?@bact: @goneall @jspeed-meyers Thank you. Yes, I think it it sound to start from the "Minimum Expected" level of the FSCT v3.

Will sketching what needs to be added first as John suggested.@bact: @jspeed-meyers I have updated the sketch. Please look at the "Current design" and "New design proposals" sections. Thanks.@jspeed-meyers: I appreciate the sketch. I'm starting to understand the architecture. I asked some questions. I can tell you've thought about this more than I have. I admire your ability to create architectural options and describe them. Please review my questions at your convenience. Once you've answered them--and I suspect we're close to consensus--then I welcome a PR. Nice job.@bact: @jspeed-meyers Thanks a lot. Those questions made me think more. I have answered all (I think) in the Google Docs, some as comments, some as the "Example of Approach" sections. Please see if there's anything we can have more details.@jspeed-meyers: @bact: I think this design document answers all of my questions and explains your intent. Please proceed!

One note: I will be on paternity leave from the Wednesday of this week until January 6, 2025. I am afraid I will hardly be at my computer. @goneall can, of course, review PRs too, as his time allows. Anyways, I wanted you to know so that you didn't think I was avoiding any PRs. But when I return, I'm glad to help with PR reviews too.@bact: Oh. Don't worry. Spend the most out of your important break. Take care + congrats 🎉@bact: #224 is ready for initial (re)structural review before I will proceed into the compliance check implementation in more details.@jspeed-meyers: Looking good to me so far! Nice!

Solution patch:
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index 58228dc..11d4b6f 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -27,6 +27,7 @@ jobs:
       run: |
         python -m pip install --upgrade pip
         pip install pylint
+        pip install spdx-tools==0.8.3
     - name: Analysing the code with pylint
       run: |
         pylint $(git ls-files '*.py')
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 7890641..a7190d0 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -27,7 +27,7 @@ jobs:
     - name: Build package
       run: python -m build
     - name: Publish package
-      uses: pypa/gh-action-pypi-publish@15c56dba361d8335944d31a2ecd17d700fc7bcbc
+      uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70
       with:
         user: __token__
         password: ${{ secrets.PYPI_API_TOKEN }}
diff --git a/ntia_conformance_checker/__init__.py b/ntia_conformance_checker/__init__.py
index f23fae3..57fbc48 100644
--- a/ntia_conformance_checker/__init__.py
+++ b/ntia_conformance_checker/__init__.py
@@ -1,3 +1,12 @@
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
+
 """Export functions for usage as library."""
 
-from ntia_conformance_checker.sbom_checker import SbomChecker
+__all__ = ["BaseChecker", "FSCT3Checker", "NTIAChecker", "SbomChecker"]
+
+from .base_checker import BaseChecker
+from .fsct_checker import FSCT3Checker
+from .ntia_checker import NTIAChecker
+from .sbom_checker import SbomChecker
diff --git a/ntia_conformance_checker/base_checker.py b/ntia_conformance_checker/base_checker.py
new file mode 100644
index 0000000..5d939b6
--- /dev/null
+++ b/ntia_conformance_checker/base_checker.py
@@ -0,0 +1,358 @@
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
+
+"""Base checking functionality."""
+import logging
+import os
+import sys
+from abc import ABC, abstractmethod
+from typing import Any, Dict, List, Optional, Tuple, Union, cast
+
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.model.spdx_no_assertion import SpdxNoAssertion
+from spdx_tools.spdx.parser import parse_anything
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
+
+
+# pylint: disable=too-many-instance-attributes
+class BaseChecker(ABC):
+    """Base class for all compliance checkers.
+
+    This base class contains methods for common tasks like file loading
+    and parsing.
+
+    Any class inheriting from BaseChecker must implement its abstract methods,
+    such as `check_compliance` and `output_json`.
+    """
+
+    compliance_standard: str = ""
+
+    file: str = ""
+    doc: Optional[Document] = None
+
+    parsing_error: List[str] = []
+    validation_messages: Optional[List[str]] = None
+
+    sbom_name: str = ""
+    components_without_names: List[str] = []
+    components_without_versions: List[str] = []
+    components_without_suppliers: List[str] = []
+    components_without_identifiers: List[str] = []
+    components_without_concluded_licenses: List[str] = []
+    components_without_copyright_texts: List[str] = []
+
+    doc_version: bool = False
+    doc_author: bool = False
+    doc_timestamp: bool = False
+    dependency_relationships: bool = False
+
+    compliant: bool = False
+
+    # an alias of "compliant", for backward compatibility
+    ntia_minimum_elements_compliant: bool = compliant
+
+    @abstractmethod
+    def check_compliance(self) -> bool:
+        """Abstract method to check compliance."""
+
+    @abstractmethod
+    def check_doc_version(self) -> bool:
+        """Abstract method to check SBOM document version."""
+
+    @abstractmethod
+    def check_dependency_relationships(self) -> bool:
+        """Abstract method to check dependency relationship requirements."""
+
+    @abstractmethod
+    def print_components_missing_info(self) -> None:
+        """
+        Abstract method to print information about components that
+        are missing required details.
+
+        What is considered "missing" is determined by a compliance standard
+        and the method that implements this abstract method.
+
+        Returns:
+            None
+        """
+
+    @abstractmethod
+    def print_table_output(self) -> None:
+        """
+        Abstract method to print element-by-element result table.
+
+        Returns:
+            None
+        """
+
+    @abstractmethod
+    def output_json(self) -> Dict[str, Any]:
+        """
+        Abstract method to create a dict of results for outputting
+        to JSON.
+        """
+
+    @abstractmethod
+    def output_html(self) -> str:
+        """Abstract method to create a result in HTML format."""
+
+    def __init__(self, file, validate=True, compliance=""):
+        """
+        Initialize the BaseChecker.
+
+        Args:
+            file (str): The file to be checked.
+            validate (bool): Whether to validate the file.
+            compliance (str): The compliance standard to be used. Defaults to "ntia".
+        """
+        self.compliance_standard = compliance
+        self.file = file
+        self.doc = self.parse_file()  # Document or None
+
+        if self.doc:
+            self.doc = cast(Document, self.doc)
+            if validate:
+                self.validation_messages = validate_full_spdx_document(self.doc)
+            self.components_without_names = self.get_components_without_names()
+            self.components_without_versions = self.get_components_without_versions()
+            self.components_without_suppliers = self.get_components_without_suppliers()
+            self.components_without_identifiers = (
+                self.get_components_without_identifiers()
+            )
+            self.components_without_concluded_licenses = (
+                self.get_components_without_concluded_licenses()
+            )
+            self.components_without_copyright_texts = (
+                self.get_components_without_copyright_texts()
+            )
+
+    def get_components_without_concluded_licenses(
+        self, return_tuples=False
+    ) -> Union[List[str], List[Tuple[str, str]]]:
+        """
+        Retrieve names and/or SPDX IDs of components without concluded licenses.
+
+        Args:
+            return_tuples (bool): If True, return a list of tuples with
+                                  component names and SPDX IDs.
+                                  If False, return a list of component names.
+
+        Returns:
+            Union[List[str], List[Tuple[str, str]]]: A list of component names
+            or a list of tuples with component names and SPDX IDs.
+        """
+        # Note: concluded license is mandatory in SPDX-2.2 and SPDX-2.3
+        if not self.doc:
+            return []
+
+        if return_tuples:
+            components_name_id: List[Tuple[str, str]] = []
+            for package in self.doc.packages:
+                no_license = (
+                    package.license_concluded is None
+                    or isinstance(package.license_concluded, SpdxNoAssertion)
+                    or (
+                        isinstance(package.license_concluded, str)
+                        and package.license_concluded.strip() == ""
+                    )
+                )
+                if no_license:
+                    components_name_id.append((package.name, package.spdx_id))
+            return components_name_id
+
+        components_name: List[str] = []
+        for package in self.doc.packages:
+            no_license = (
+                package.license_concluded is None
+                or isinstance(package.license_concluded, SpdxNoAssertion)
+                or (
+                    isinstance(package.license_concluded, str)
+                    and package.license_concluded.strip() == ""
+                )
+            )
+            if no_license:
+                components_name.append(package.name)
+        return components_name
+
+    def get_components_without_copyright_texts(
+        self, return_tuples=False
+    ) -> Union[List[str], List[Tuple[str, str]]]:
+        """
+        Retrieve names and/or SPDX IDs of components without copyright texts.
+
+        Args:
+            return_tuples (bool): If True, return a list of tuples with
+                                  component names and SPDX IDs.
+                                  If False, return a list of component names.
+
+        Returns:
+            Union[List[str], List[Tuple[str, str]]]: A list of component names
+            or a list of tuples with component names and SPDX IDs.
+        """
+        if not self.doc:
+            return []
+
+        if return_tuples:
+            components_name_id: List[Tuple[str, str]] = []
+            for package in self.doc.packages:
+                no_license = (
+                    package.copyright_text is None
+                    or isinstance(package.copyright_text, SpdxNoAssertion)
+                    or (
+                        isinstance(package.copyright_text, str)
+                        and package.copyright_text.strip() == ""
+                    )
+                )
+                if no_license:
+                    components_name_id.append((package.name, package.spdx_id))
+            return components_name_id
+
+        components_name: List[str] = []
+        for package in self.doc.packages:
+            no_license = (
+                package.copyright_text is None
+                or isinstance(package.copyright_text, SpdxNoAssertion)
+                or (
+                    isinstance(package.copyright_text, str)
+                    and package.copyright_text.strip() == ""
+                )
+            )
+            if no_license:
+                components_name.append(package.name)
+        return components_name
+
+    def get_components_without_identifiers(self) -> list[str]:
+        """
+        Retrieve name of components without identifiers.
+
+        Returns:
+            List[str]: A list of component names that do not have identifiers.
+        """
+        if not self.doc:
+            return []
+
+        return [package.name for package in self.doc.packages if not package.spdx_id]
+
+    def get_components_without_names(self) -> list[str]:
+        """
+        Retrieve SPDX ID of components without names.
+
+        Args:
+            return_tuples (bool): If True, return a list of tuples with
+                                  component names and SPDX IDs.
+                                  If False, return a list of component names.
+
+        Returns:
+            Union[List[str], List[Tuple[str, str]]]: A list of component names
+            or a list of tuples with component names and SPDX IDs.
+        """
+        if not self.doc:
+            return []
+
+        components_without_names = []
+        for package in self.doc.packages:
+            if not package.name:
+                components_without_names.append(package.spdx_id)
+        return components_without_names
+
+    def get_components_without_suppliers(
+        self, return_tuples=False
+    ) -> Union[List[str], List[Tuple[str, str]]]:
+        """
+        Retrieve names and/or SPDX IDs of components without suppliers.
+
+        Args:
+            return_tuples (bool): If True, return a list of tuples with
+                                  component names and SPDX IDs.
+                                  If False, return a list of component names.
+
+        Returns:
+            Union[List[str], List[Tuple[str, str]]]: A list of component names
+            or a list of tuples with component names and SPDX IDs.
+        """
+        if not self.doc:
+            return []
+
+        if return_tuples:
+            components_name_id: List[Tuple[str, str]] = []
+            for package in self.doc.packages:
+                no_supplier = package.supplier is None or isinstance(
+                    package.supplier, SpdxNoAssertion
+                )
+                if no_supplier:
+                    components_name_id.append((package.name, package.spdx_id))
+            return components_name_id
+
+        components_name: List[str] = []
+        for package in self.doc.packages:
+            no_supplier = package.supplier is None or isinstance(
+                package.supplier, SpdxNoAssertion
+            )
+            if no_supplier:
+                components_name.append(package.name)
+        return components_name
+
+    def get_components_without_versions(
+        self, return_tuples=False
+    ) -> Union[List[str], List[Tuple[str, str]]]:
+        """
+        Retrieve name and/or SPDX ID of components without versions.
+
+        Args:
+            return_tuples (bool): If True, return a list of tuples with
+                                  component names and SPDX IDs.
+                                  If False, return a list of component names.
+
+        Returns:
+            Union[List[str], List[Tuple[str, str]]]: A list of component names
+            or a list of tuples with component names and SPDX IDs.
+        """
+        if not self.doc:
+            return []
+
+        if return_tuples:
+            components_name_id: List[Tuple[str, str]] = []
+            for package in self.doc.packages:
+                if not package.version:
+                    components_name_id.append((package.name, package.spdx_id))
+            return components_name_id
+
+        components_name: List[str] = []
+        for package in self.doc.packages:
+            if not package.version:
+                components_name.append(package.name)
+        return components_name
+
+    def get_total_number_components(self) -> int:
+        """
+        Retrieve total number of components.
+
+        Returns:
+            int: The total number of components.
+        """
+        if not self.doc:
+            return 0
+
+        return len(self.doc.packages)
+
+    def parse_file(self) -> Optional[Document]:
+        """
+        Parse SBOM document.
+
+        Returns:
+            Optional[Document]: The parsed SBOM document if successful,
+            otherwise None.
+        """
+        # check if file exists
+        if not os.path.exists(self.file):
+            logging.error("Filename %s not found.", self.file)
+            sys.exit(1)
+        try:
+            doc = parse_anything.parse_file(self.file)
+        except SPDXParsingError as err:
+            self.parsing_error.extend(err.get_messages())
+            return None
+
+        return cast(Document, doc)
diff --git a/ntia_conformance_checker/fsct_checker.py b/ntia_conformance_checker/fsct_checker.py
new file mode 100644
index 0000000..c224360
--- /dev/null
+++ b/ntia_conformance_checker/fsct_checker.py
@@ -0,0 +1,286 @@
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
+
+"""FSCT Common BOM checking functionality."""
+
+from spdx_tools.spdx.model import RelationshipType
+
+from .base_checker import BaseChecker
+
+
+class FSCT3Checker(BaseChecker):
+    """FSCT Common SBOM Third Edition checker.
+
+    A set of Baseline Attributes is defined in Section 2.2 of
+    Framing Software Component Transparency:
+    Establishing a Common Software Bill of Materials (SBOM) Third Edition.
+
+    There are three maturity levels (Minimum Expected, Recommended Practice,
+    and Aspirational Goal) for content provided in Attribute entries.
+
+    See:
+    https://www.cisa.gov/resources-tools/resources/framing-software-component-transparency-2024
+    """
+
+    def __init__(self, file, validate=True, compliance="fsct3-min"):
+        super().__init__(file=file, validate=validate)
+
+        if compliance != "fsct3-min":
+            raise ValueError("Only FSCTv3 Minimum Expected compliance is supported.")
+
+        if self.doc:
+            self.sbom_name = self.doc.creation_info.name
+            self.doc_version = self.check_doc_version()
+            self.doc_author = True  # Assume author is present?
+            self.doc_timestamp = True  # Assume timestamp is present?
+            self.dependency_relationships = self.check_dependency_relationships()
+            self.compliant = self.check_compliance()
+
+    def check_doc_version(self):
+        """Check for SPDX document version."""
+        if str(self.doc.creation_info.spdx_version) not in ["SPDX-2.2", "SPDX-2.3"]:
+            return False
+        return True
+
+    def check_dependency_relationships(self):
+        """Check that the document DESCRIBES at least one package."""
+        describes_relationships = [
+            rel
+            for rel in self.doc.relationships
+            if rel.relationship_type == RelationshipType.DESCRIBES
+        ]
+
+        # A set of all package spdx_ids for quick lookup
+        spdx_id_set = {package.spdx_id for package in self.doc.packages}
+
+        # Check if any of the "DESCRIBES" relationships describe a Package
+        describes_package = any(
+            rel.related_spdx_element_id in spdx_id_set
+            for rel in describes_relationships
+        )
+
+        return describes_package
+
+    def check_compliance(self):
+        """Check overall compliance with FSCTv3 Minimum Expected"""
+        return all(
+            [
+                self.doc_author,
+                self.doc_timestamp,
+                self.dependency_relationships,
+                not self.components_without_names,
+                not self.components_without_versions,
+                not self.components_without_identifiers,
+                not self.components_without_suppliers,
+                not self.components_without_concluded_licenses,
+                not self.components_without_copyright_texts,
+                not self.validation_messages,
+            ]
+        )
+
+    def print_components_missing_info(self):
+        """Print detailed info about which components are missing info."""
+        if not self.parsing_error:
+            if all(
+                [
+                    not self.components_without_names,
+                    not self.components_without_versions,
+                    not self.components_without_identifiers,
+                    not self.components_without_suppliers,
+                ]
+            ):
+                print("No components with missing information.")
+            if self.components_without_names:
+                print(
+                    "Components missing a name: "
+                    f"{', '.join(self.components_without_names)}"
+                )
+                print()
+            if self.components_without_versions:
+                print(
+                    "Components missing a version: "
+                    f"{', '.join(self.components_without_versions)}"
+                )
+                print()
+            if self.components_without_identifiers:
+                print(
+                    "Components missing an identifier: "
+                    f"{', '.join(self.components_without_identifiers)}"
+                )
+                print()
+            if self.components_without_suppliers:
+                print(
+                    "Components missing a supplier: "
+                    f"{', '.join(self.components_without_suppliers)}"
+                )
+                print()
+            if self.components_without_concluded_licenses:
+                print(
+                    "Components missing a license: "
+                    f"{', '.join(self.components_without_concluded_licenses)}"
+                )
+                print()
+            if self.components_without_copyright_texts:
+                print(
+                    "Components missing a copyright notice: "
+                    f"{', '.join(self.components_without_copyright_texts)}"
+                )
+                print()
+
+    def print_table_output(self):
+        """Print element-by-element result table."""
+        # pylint: disable=line-too-long
+        if self.parsing_error:
+            print(
+                f"\nIs this SBOM FSCTv3 Baseline Attributes conformant? {self.compliant}\n"
+            )
+            print(
+                "The provided document couldn't be parsed, check for FSCTv3 Baseline Attributes couldn't be performed.\n"
+            )
+            print("The following SPDXParsingError was raised:\n")
+            for error in self.parsing_error:
+                print(error)
+
+        else:
+            print(
+                f"\nIs this SBOM FSCTv3 Baseline Attributes conformant? {self.compliant}\n"
+            )
+            print("Individual elements                            | Status")
+            print("-------------------------------------------------------")
+            print(
+                f"All component names provided?                  | {not self.components_without_names}"
+            )
+            print(
+                f"All component versions provided?               | {not self.components_without_versions}"
+            )
+            print(
+                f"All component identifiers provided?            | {not self.components_without_identifiers}"
+            )
+            print(
+                f"All component suppliers provided?              | {not self.components_without_suppliers}"
+            )
+            print(f"SBOM author name provided?                     | {self.doc_author}")
+            print(
+                f"SBOM creation timestamp provided?              | {self.doc_timestamp}"
+            )
+            print(
+                f"Dependency relationships provided?             | {self.dependency_relationships}\n"
+            )
+            if self.validation_messages:
+                print(
+                    "The provided document is not valid according to the SPDX specification. "
+                    "The following errors were found:\n"
+                )
+                for message in self.validation_messages:
+                    print(message.validation_message)
+
+    def output_json(self):
+        """Create a dict of results for outputting to JSON."""
+        # instantiate dict and fields that have > 1 level
+        result = {}
+        result["complianceStandard"] = self.compliance_standard
+        result["parsingError"] = self.parsing_error
+        result["isConformant"] = self.compliant
+
+        result["sbomName"] = self.sbom_name
+        result["componentNames"] = {}
+        result["componentVersions"] = {}
+        result["componentIdentifiers"] = {}
+        result["componentSuppliers"] = {}
+
+        result["authorNameProvided"] = self.doc_author
+        result["timestampProvided"] = self.doc_timestamp
+        result["dependencyRelationshipsProvided"] = self.dependency_relationships
+
+        result["componentNames"][
+            "nonconformantComponents"
+        ] = self.components_without_names
+        result["componentNames"]["allProvided"] = not self.components_without_names
+
+        result["componentVersions"][
+            "nonconformantComponents"
+        ] = self.components_without_versions
+        result["componentVersions"][
+            "allProvided"
+        ] = not self.components_without_versions
+
+        result["componentIdentifiers"][
+            "nonconformantComponents"
+        ] = self.components_without_identifiers
+        result["componentIdentifiers"][
+            "allProvided"
+        ] = not self.components_without_identifiers
+
+        result["componentSuppliers"][
+            "nonconformantComponents"
+        ] = self.components_without_suppliers
+        result["componentSuppliers"][
+            "allProvided"
+        ] = not self.components_without_suppliers
+
+        result["componentConcludedLicenses"][
+            "nonconformantComponents"
+        ] = self.components_without_concluded_licenses
+        result["componentConcludedLicenses"][
+            "allProvided"
+        ] = not self.components_without_concluded_licenses
+
+        result["componentCopyrightText"][
+            "nonconformantComponents"
+        ] = self.components_without_copyright_texts
+        result["componentCopyrightText"][
+            "allProvided"
+        ] = not self.components_without_copyright_texts
+
+        result["totalNumberComponents"] = self.get_total_number_components()
+
+        result["validationMessages"] = []
+        if self.validation_messages:
+            result["validationMessages"] = list(map(str, self.validation_messages))
+
+        return result
+
+    def output_html(self):
+        """Create a HTML of results."""
+        if self.doc:
+            result = (
+                f" <h2>FSCTv3-Minimum Expected Conformance Results</h2> "
+                f"<h3>Conformant: {self.compliant} </h3>"
+                f"<table> <tr> "
+                f"<th>Individual Elements</th> <th>Conformant</th> </tr> "
+                f"<tr> <td>All component names provided</td>"
+                f" <td>{not self.components_without_names}</td> </tr> "
+                f"<tr> <td>All component versions provided</td>"
+                f" <td>{not self.components_without_versions}</td> </tr> "
+                f"<tr> <td>All component identifiers provided</td> "
+                f"<td>{not self.components_without_identifiers}</td> </tr> "
+                f"<tr> <td>All component suppliers provided</td> "
+                f"<td>{not self.components_without_suppliers}</td> "
+                f"</tr> <tr> <td>SBOM author name provided</td> "
+                f"<td>{self.doc_author}</td> </tr> "
+                f"<tr> <td>SBOM creation timestamp provided</td> "
+                f"<td>{self.doc_timestamp}</td> </tr> "
+                f"<tr> <td>Dependency relationships provided?</td> "
+                f"<td>{self.dependency_relationships}</td> </tr> "
+                f"</table>"
+            )
+            if self.validation_messages:
+                result += (
+                    "<p>The provided document is not valid according to the SPDX specification. "
+                    "The following errors were found:</p>\n"
+                )
+            for message in self.validation_messages:
+                result += f"<p>{message.validation_message}</p>\n"
+        else:
+            result = f"""
+            <h2>FSCTv3-Minimum Expected Conformance Results</h2>
+            <h3>Conformant: {self.compliant} </h3>
+            <p>The provided document couldn't be parsed, check for minimum elements couldn't be performed.</p>
+            <p>The following SPDXParsingError was raised:<p><ul>"""
+            for error in self.parsing_error:
+                result += f"""<li>{error}</li>"""
+
+            result += """</ul>"""
+
+        return result
diff --git a/ntia_conformance_checker/main.py b/ntia_conformance_checker/main.py
index 5ed7bda..603a269 100644
--- a/ntia_conformance_checker/main.py
+++ b/ntia_conformance_checker/main.py
@@ -1,21 +1,31 @@
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
+
 """Entrypoint for CLI."""
 
 import argparse
 import json
+import logging
 import sys
-
 from importlib.metadata import version
 
-from ntia_conformance_checker.sbom_checker import SbomChecker
+from .sbom_checker import SbomChecker
 
 
 def get_parsed_args():
     """Parse command line arguments"""
     parser = argparse.ArgumentParser(
-        prog="ntia-checker",
-        description="Check if SPDX SBOM complies with NTIA minimum elements",
+        description="Check if SPDX SBOM complies with NTIA minimum elements/"
+        "FSCT Common SBOM baseline attributes",
     )
     parser.add_argument("--file", help="Filepath for SPDX SBOM")
+    parser.add_argument(
+        "--comply",
+        choices=["fsct3-min", "ntia"],
+        default="ntia",
+        help="Specify which compliance standard to check against",
+    )
     parser.add_argument(
         "--output",
         choices=["print", "json", "html", "quiet"],
@@ -58,7 +68,24 @@ def main():
 
     args = get_parsed_args()
 
-    sbom = SbomChecker(args.file, validate=not args.skip_validation)
+    log_level = logging.DEBUG if args.verbose else logging.INFO
+    logging.basicConfig(level=log_level, format="%(levelname)s: %(message)s")
+
+    sbom = SbomChecker(
+        args.file, validate=not args.skip_validation, compliance=args.comply
+    )
+
+    # Log messages
+    logging.info("Checking SBOM: %s", args.file)
+    logging.info("Compliance standard: %s", args.comply)
+    logging.info(
+        "SPDX validation: %s", "enabled" if not args.skip_validation else "disabled"
+    )
+    logging.info("Parsing: %s", "OK" if not sbom.parsing_error else "Failed")
+    logging.info("Validation: %s", "OK" if not sbom.validation_messages else "Failed")
+    if not sbom.parsing_error:
+        logging.info("SBOM name: %s", sbom.sbom_name)
+
     if args.output == "print":
         sbom.print_table_output()
         if args.verbose:
@@ -74,7 +101,7 @@ def main():
         html_output = sbom.output_html()
         print(html_output)
     # 0 indicates success
-    sys.exit(0 if sbom.ntia_minimum_elements_compliant else 1)
+    sys.exit(0 if sbom.compliant else 1)
 
 
 if __name__ == "__main__":
diff --git a/ntia_conformance_checker/ntia_checker.py b/ntia_conformance_checker/ntia_checker.py
new file mode 100644
index 0000000..adfa404
--- /dev/null
+++ b/ntia_conformance_checker/ntia_checker.py
@@ -0,0 +1,248 @@
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
+
+"""NTIA minimum elements checking functionality."""
+
+from spdx_tools.spdx.model import RelationshipType
+
+from .base_checker import BaseChecker
+
+
+class NTIAChecker(BaseChecker):
+    """NTIA Minimum Elements check."""
+
+    def __init__(self, file, validate=True, compliance="ntia"):
+        super().__init__(file=file, validate=validate)
+
+        if self.doc:
+            self.sbom_name = self.doc.creation_info.name
+            self.doc_version = self.check_doc_version()
+            self.doc_author = True  # Assume author is present?
+            self.doc_timestamp = True  # Assume timestamp is present?
+            self.dependency_relationships = self.check_dependency_relationships()
+            self.compliant = self.check_compliance()
+
+            # for backward compatibility
+            self.ntia_minimum_elements_compliant = self.compliant
+
+    def check_doc_version(self):
+        """Check for SPDX document version."""
+        if str(self.doc.creation_info.spdx_version) not in ["SPDX-2.2", "SPDX-2.3"]:
+            return False
+        return True
+
+    def check_dependency_relationships(self):
+        """Check that the document DESCRIBES at least one package."""
+        describes_relationships = [
+            rel
+            for rel in self.doc.relationships
+            if rel.relationship_type == RelationshipType.DESCRIBES
+        ]
+
+        # A set of all package spdx_ids for quick lookup
+        spdx_id_set = {package.spdx_id for package in self.doc.packages}
+
+        # Check if any of the "DESCRIBES" relationships describe a Package
+        describes_package = any(
+            rel.related_spdx_element_id in spdx_id_set
+            for rel in describes_relationships
+        )
+
+        return describes_package
+
+    def check_compliance(self):
+        """Check overall compliance with NTIA minimum elements."""
+        return all(
+            [
+                self.doc_author,
+                self.doc_timestamp,
+                self.dependency_relationships,
+                not self.components_without_names,
+                not self.components_without_versions,
+                not self.components_without_identifiers,
+                not self.components_without_suppliers,
+                not self.validation_messages,
+            ]
+        )
+
+    def check_ntia_minimum_elements_compliance(self):
+        """Check overall compliance with NTIA minimum elements.
+
+        This method is kept for backward compatibility.
+        Please consider using check_compliance() instead."""
+        return self.check_compliance()
+
+    def print_components_missing_info(self):
+        """Print detailed info about which components are missing info."""
+        if not self.parsing_error:
+            if all(
+                [
+                    not self.components_without_names,
+                    not self.components_without_versions,
+                    not self.components_without_identifiers,
+                    not self.components_without_suppliers,
+                ]
+            ):
+                print("No components with missing information.")
+            if self.components_without_names:
+                print(
+                    f"Components missing a name: {', '.join(self.components_without_names)}"
+                )
+                print()
+            if self.components_without_versions:
+                print(
+                    f"Components missing a version: {', '.join(self.components_without_versions)}"
+                )
+                print()
+            if self.components_without_identifiers:
+                print(
+                    f"Components missing an identifier: "
+                    f"{', '.join(self.components_without_identifiers)}"
+                )
+                print()
+            if self.components_without_suppliers:
+                print(
+                    f"Components missing a supplier: {', '.join(self.components_without_suppliers)}"
+                )
+                print()
+
+    def print_table_output(self):
+        """Print element-by-element result table."""
+        # pylint: disable=line-too-long
+        if self.parsing_error:
+            print(f"\nIs this SBOM NTIA minimum element conformant? {self.compliant}\n")
+            print(
+                "The provided document couldn't be parsed, check for ntia minimum elements couldn't be performed.\n"
+            )
+            print("The following SPDXParsingError was raised:\n")
+            for error in self.parsing_error:
+                print(error)
+
+        else:
+            print(f"\nIs this SBOM NTIA minimum element conformant? {self.compliant}\n")
+            print("Individual elements                            | Status")
+            print("-------------------------------------------------------")
+            print(
+                f"All component names provided?                  | {not self.components_without_names}"
+            )
+            print(
+                f"All component versions provided?               | {not self.components_without_versions}"
+            )
+            print(
+                f"All component identifiers provided?            | {not self.components_without_identifiers}"
+            )
+            print(
+                f"All component suppliers provided?              | {not self.components_without_suppliers}"
+            )
+            print(f"SBOM author name provided?                     | {self.doc_author}")
+            print(
+                f"SBOM creation timestamp provided?              | {self.doc_timestamp}"
+            )
+            print(
+                f"Dependency relationships provided?             | {self.dependency_relationships}\n"
+            )
+            if self.validation_messages:
+                print(
+                    "The provided document is not valid according to the SPDX specification. "
+                    "The following errors were found:\n"
+                )
+                for message in self.validation_messages:
+                    print(message.validation_message)
+
+    def output_json(self):
+        """Create a dict of results for outputting to JSON."""
+        # instantiate dict and fields that have > 1 level
+        result = {}
+        result["complianceStandard"] = self.compliance_standard
+        result["parsingError"] = self.parsing_error
+        result["isConformant"] = self.compliant
+        result["isNtiaConformant"] = self.compliant  # for backward compatibility
+
+        result["sbomName"] = self.sbom_name
+        result["componentNames"] = {}
+        result["componentVersions"] = {}
+        result["componentIdentifiers"] = {}
+        result["componentSuppliers"] = {}
+
+        result["authorNameProvided"] = self.doc_author
+        result["timestampProvided"] = self.doc_timestamp
+        result["dependencyRelationshipsProvided"] = self.dependency_relationships
+
+        result["componentNames"][
+            "nonconformantComponents"
+        ] = self.components_without_names
+        result["componentNames"]["allProvided"] = not self.components_without_names
+
+        result["componentVersions"][
+            "nonconformantComponents"
+        ] = self.components_without_versions
+        result["componentVersions"][
+            "allProvided"
+        ] = not self.components_without_versions
+
+        result["componentIdentifiers"][
+            "nonconformantComponents"
+        ] = self.components_without_identifiers
+        result["componentIdentifiers"][
+            "allProvided"
+        ] = not self.components_without_identifiers
+
+        result["componentSuppliers"][
+            "nonconformantComponents"
+        ] = self.components_without_suppliers
+        result["componentSuppliers"][
+            "allProvided"
+        ] = not self.components_without_suppliers
+
+        result["totalNumberComponents"] = self.get_total_number_components()
+
+        result["validationMessages"] = []
+        if self.validation_messages:
+            result["validationMessages"] = list(map(str, self.validation_messages))
+
+        return result
+
+    def output_html(self):
+        """Create a HTML of results."""
+        if self.doc:
+            result = (
+                f" <h2>NTIA Conformance Results</h2> "
+                f"<h3>Conformant: {self.compliant} </h3>"
+                f"<table> <tr> "
+                f"<th>Individual Elements</th> <th>Conformant</th> </tr> "
+                f"<tr> <td>All component names provided</td>"
+                f" <td>{not self.components_without_names}</td> </tr> "
+                f"<tr> <td>All component versions provided</td>"
+                f" <td>{not self.components_without_versions}</td> </tr> "
+                f"<tr> <td>All component identifiers provided</td> "
+                f"<td>{not self.components_without_identifiers}</td> </tr> "
+                f"<tr> <td>All component suppliers provided</td> "
+                f"<td>{not self.components_without_suppliers}</td> "
+                f"</tr> <tr> <td>SBOM author name provided</td> "
+                f"<td>{self.doc_author}</td> </tr> "
+                f"<tr> <td>SBOM creation timestamp provided</td> "
+                f"<td>{self.doc_timestamp}</td> </tr> "
+                f"<tr> <td>Dependency relationships provided?</td> "
+                f"<td>{self.dependency_relationships}</td> </tr> "
+                f"</table>"
+            )
+            if self.validation_messages:
+                result += (
+                    "<p>The provided document is not valid according to the SPDX specification. "
+                    "The following errors were found:</p>\n"
+                )
+            for message in self.validation_messages:
+                result += f"<p>{message.validation_message}</p>\n"
+        else:
+            result = f"""
+            <h2>NTIA Conformance Results</h2>
+            <h3>Conformant: {self.compliant} </h3>
+            <p>The provided document couldn't be parsed, check for ntia minimum elements couldn't be performed.</p>
+            <p>The following SPDXParsingError was raised:<p><ul>"""
+            for error in self.parsing_error:
+                result += f"""<li>{error}</li>"""
+
+            result += """</ul>"""
+
+        return result
diff --git a/ntia_conformance_checker/sbom_checker.py b/ntia_conformance_checker/sbom_checker.py
index a3f2daa..0d96c47 100644
--- a/ntia_conformance_checker/sbom_checker.py
+++ b/ntia_conformance_checker/sbom_checker.py
@@ -1,310 +1,77 @@
-"""Main minimum elements checking functionality."""
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
 
-# pylint: disable=import-error
+"""Main checking functionality."""
 
-import logging
-import os
-import sys
+from .base_checker import BaseChecker
 
-from spdx_tools.spdx.model import RelationshipType
-from spdx_tools.spdx.parser import parse_anything
-from spdx_tools.spdx.model.spdx_no_assertion import SpdxNoAssertion
-from spdx_tools.spdx.parser.error import SPDXParsingError
-from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
 
+class SbomChecker(BaseChecker):
+    """
+    SBOM checker factory.
 
-# pylint: disable=too-many-instance-attributes
-class SbomChecker:
-    """SBOM minimum elements check."""
+    Post-v3.0.2, SbomChecker acts like a factory that returns
+    a subclass of BaseChecker based on the given "compliance" argument
+    during instantiation.
 
-    def __init__(self, file, validate=True):
-        self.file = file
-        self.parsing_error = []
-        self.doc = self.parse_file()
-        if not self.doc:
-            self.ntia_minimum_elements_compliant = False
-        else:
-            self.validation_messages = None
-            if validate:
-                self.validation_messages = validate_full_spdx_document(self.doc)
-            self.sbom_name = self.doc.creation_info.name
-            self.doc_version = self.check_doc_version()
-            self.doc_author = True
-            self.doc_timestamp = True
-            self.dependency_relationships = self.check_dependency_relationships()
-            self.components_without_names = self.get_components_without_names()
-            self.components_without_versions = self.get_components_without_versions()
-            self.components_without_suppliers = self.get_components_without_suppliers()
-            self.components_without_identifiers = (
-                self.get_components_without_identifiers()
-            )
-            self.ntia_minimum_elements_compliant = (
-                self.check_ntia_minimum_elements_compliance()
-            )
+    Currently there are two compliance standards available:
+    - "ntia" (default), returns an instance of NTIAChecker
+      - NTIAChecker has the same behavior as the original SbomChecker
+    - "fsct3-min", returns an instance of FSCT3Checker
+      - FSCT3Checker is a new checker for FSCT 3rd Edition Baseline Attributes
 
-    def parse_file(self):
-        """Parse SBOM document."""
-        # check if file exists
-        if not os.path.exists(self.file):
-            logging.error("Filename %s not found.", self.file)
-            sys.exit(1)
-        try:
-            doc = parse_anything.parse_file(self.file)
-        except SPDXParsingError as err:
-            self.parsing_error.extend(err.get_messages())
-            return None
-        return doc
+    If "compliance" is not recognized, SbomChecker raises a ValueError.
+    """
 
-    def check_doc_version(self):
-        """Check for SPDX document version."""
-        if str(self.doc.creation_info.spdx_version) not in ["SPDX-2.2", "SPDX-2.3"]:
-            return False
-        return True
+    # Note that all NTIA-specific functionalities are moved to
+    # .ntia_checker.NTIAChecker, and common functionalities that can be shared
+    # among checkers of different compliance standards are moved to
+    # .base_checker.BaseChecker.
 
-    def check_dependency_relationships(self):
-        """Check that the document DESCRIBES at least one package."""
-        describes_relationships = [
-            rel
-            for rel in self.doc.relationships
-            if rel.relationship_type == RelationshipType.DESCRIBES
-        ]
+    def __new__(cls, file, validate=True, compliance="ntia"):
+        """
+        Returns an instance of a specific compliance checker.
 
-        # A set of all package spdx_ids for quick lookup
-        spdx_id_set = {package.spdx_id for package in self.doc.packages}
+        Args:
+            file (str): The file to be checked.
+            validate (bool): Whether to validate the file.
+            compliance (str): The compliance standard to be used. Defaults to "ntia".
 
-        # Check if any of the "DESCRIBES" relationships describe a Package
-        describes_package = any(
-            rel.related_spdx_element_id in spdx_id_set
-            for rel in describes_relationships
-        )
+        Returns:
+            BaseChecker: An instance of a specific compliance checker.
+        """
+        if compliance == "ntia":
+            # pylint: disable=import-outside-toplevel
+            from .ntia_checker import NTIAChecker
 
-        return describes_package
+            return NTIAChecker(file, validate)
 
-    def get_components_without_names(self):
-        """Retrieve SPDX ID of components without names."""
-        components_without_names = []
-        for package in self.doc.packages:
-            if not package.name:
-                components_without_names.append(package.spdx_id)
-        return components_without_names
+        if compliance.startswith("fsct3"):
+            # pylint: disable=import-outside-toplevel
+            from .fsct_checker import FSCT3Checker
 
-    def get_components_without_versions(self, return_tuples=False):
-        """Retrieve name and/or SPDX ID of components without versions."""
-        components_without_versions = []
-        for package in self.doc.packages:
-            if not package.version:
-                if return_tuples:
-                    components_without_versions.append((package.name, package.spdx_id))
-                else:
-                    components_without_versions.append(package.name)
-        return components_without_versions
+            return FSCT3Checker(file, validate)
 
-    def get_components_without_suppliers(self, return_tuples=False):
-        """Retrieve name and/or SPDX ID of components without suppliers."""
-        components_without_suppliers = []
-        for package in self.doc.packages:
-            no_supplier = package.supplier is None or isinstance(
-                package.supplier, SpdxNoAssertion
-            )
-            if no_supplier:
-                if return_tuples:
-                    components_without_suppliers.append((package.name, package.spdx_id))
-                else:
-                    components_without_suppliers.append(package.name)
+        raise ValueError(f"Unknown compliance standard: {compliance}")
 
-        return components_without_suppliers
+    def check_compliance(self) -> bool:
+        raise NotImplementedError("This method should be implemented by subclasses.")
 
-    def get_components_without_identifiers(self):
-        """Retrieve name of components without identifiers."""
-        return [package.name for package in self.doc.packages if not package.spdx_id]
+    def check_doc_version(self) -> bool:
+        raise NotImplementedError("This method should be implemented by subclasses.")
 
-    def check_ntia_minimum_elements_compliance(self):
-        """Check overall compliance with NTIA minimum elements."""
-        return all(
-            [
-                self.doc_author,
-                self.doc_timestamp,
-                self.dependency_relationships,
-                not self.components_without_names,
-                not self.components_without_versions,
-                not self.components_without_identifiers,
-                not self.components_without_suppliers,
-                not self.validation_messages,
-            ]
-        )
+    def check_dependency_relationships(self) -> bool:
+        raise NotImplementedError("This method should be implemented by subclasses.")
 
-    def get_total_number_components(self):
-        """Retrieve total number of components."""
-        return len(self.doc.packages)
+    def print_components_missing_info(self) -> None:
+        raise NotImplementedError("This method should be implemented by subclasses.")
 
-    def print_table_output(self):
-        """Print element-by-element result table."""
-        # pylint: disable=line-too-long
-        if self.parsing_error:
-            print(
-                f"\nIs this SBOM NTIA minimum element conformant? {self.ntia_minimum_elements_compliant}\n"
-            )
-            print(
-                "The provided document couldn't be parsed, check for ntia minimum elements couldn't be performed.\n"
-            )
-            print("The following SPDXParsingError was raised:\n")
-            for error in self.parsing_error:
-                print(error)
+    def print_table_output(self) -> None:
+        raise NotImplementedError("This method should be implemented by subclasses.")
 
-        else:
-            print(
-                f"\nIs this SBOM NTIA minimum element conformant? {self.ntia_minimum_elements_compliant}\n"
-            )
-            print("Individual elements                            | Status")
-            print("-------------------------------------------------------")
-            print(
-                f"All component names provided?                  | {not self.components_without_names}"
-            )
-            print(
-                f"All component versions provided?               | {not self.components_without_versions}"
-            )
-            print(
-                f"All component identifiers provided?            | {not self.components_without_identifiers}"
-            )
-            print(
-                f"All component suppliers provided?              | {not self.components_without_suppliers}"
-            )
-            print(f"SBOM author name provided?                     | {self.doc_author}")
-            print(
-                f"SBOM creation timestamp provided?              | {self.doc_timestamp}"
-            )
-            print(
-                f"Dependency relationships provided?             | {self.dependency_relationships}\n"
-            )
-            if self.validation_messages:
-                print(
-                    "The provided document is not valid according to the SPDX specification. "
-                    "The following errors were found:\n"
-                )
-                for message in self.validation_messages:
-                    print(message.validation_message)
+    def output_json(self) -> dict:
+        raise NotImplementedError("This method should be implemented by subclasses.")
 
-    def print_components_missing_info(self):
-        """Print detailed info about which components are missing info."""
-        if not self.parsing_error:
-            if all(
-                [
-                    not self.components_without_names,
-                    not self.components_without_versions,
-                    not self.components_without_identifiers,
-                    not self.components_without_suppliers,
-                ]
-            ):
-                print("No components with missing information.")
-            if self.components_without_names:
-                print(
-                    f"Components missing a name: {','.join(self.components_without_names)}"
-                )
-                print()
-            if self.components_without_versions:
-                print(
-                    f"Components missing a version: {','.join(self.components_without_versions)}"
-                )
-                print()
-            if self.components_without_identifiers:
-                print(
-                    f"Components missing an identifier: "
-                    f"{','.join(self.components_without_identifiers)}"
-                )
-                print()
-            if self.components_without_suppliers:
-                print(
-                    f"Components missing a supplier: {','.join(self.components_without_suppliers)}"
-                )
-                print()
-
-    def output_json(self):
-        """Create a dict of results for outputting to JSON."""
-        # instantiate dict and fields that have > 1 level
-        result = {}
-        if not self.parsing_error:
-            result["sbomName"] = self.sbom_name
-            result["componentNames"] = {}
-            result["componentVersions"] = {}
-            result["componentIdentifiers"] = {}
-            result["componentSuppliers"] = {}
-
-            result["authorNameProvided"] = self.doc_author
-            result["timestampProvided"] = self.doc_timestamp
-            result["dependencyRelationshipsProvided"] = self.dependency_relationships
-
-            result["componentNames"][
-                "nonconformantComponents"
-            ] = self.components_without_names
-            result["componentNames"]["allProvided"] = not self.components_without_names
-            result["componentVersions"][
-                "nonconformantComponents"
-            ] = self.components_without_versions
-            result["componentVersions"][
-                "allProvided"
-            ] = not self.components_without_versions
-            result["componentIdentifiers"][
-                "nonconformantComponents"
-            ] = self.components_without_identifiers
-            result["componentIdentifiers"][
-                "allProvided"
-            ] = not self.components_without_identifiers
-            result["componentSuppliers"][
-                "nonconformantComponents"
-            ] = self.components_without_suppliers
-            result["componentSuppliers"][
-                "allProvided"
-            ] = not self.components_without_suppliers
-            result["totalNumberComponents"] = self.get_total_number_components()
-            if self.validation_messages:
-                result["validationMessages"] = list(map(str, self.validation_messages))
-        else:
-            result["parsingError"] = self.parsing_error
-
-        result["isNtiaConformant"] = self.ntia_minimum_elements_compliant
-
-        return result
-
-    def output_html(self):
-        """Print HTML of output."""
-        if self.doc:
-            result = (
-                f" <h2>NTIA Conformance Results</h2> "
-                f"<h3>Conformant: {self.ntia_minimum_elements_compliant} </h3>"
-                f"<table> <tr> "
-                f"<th>Individual Elements</th> <th>Conformant</th> </tr> "
-                f"<tr> <td>All component names provided</td>"
-                f" <td>{not self.components_without_names}</td> </tr> "
-                f"<tr> <td>All component versions provided</td>"
-                f" <td>{not self.components_without_versions}</td> </tr> "
-                f"<tr> <td>All component identifiers provided</td> "
-                f"<td>{not self.components_without_identifiers}</td> </tr> "
-                f"<tr> <td>All component suppliers provided</td> "
-                f"<td>{not self.components_without_suppliers}</td> "
-                f"</tr> <tr> <td>SBOM author name provided</td> "
-                f"<td>{self.doc_author}</td> </tr> "
-                f"<tr> <td>SBOM creation timestamp provided</td> "
-                f"<td>{self.doc_timestamp}</td> </tr> "
-                f"<tr> <td>Dependency relationships provided?</td> "
-                f"<td>{self.dependency_relationships}</td> </tr> "
-                f"</table>"
-            )
-            if self.validation_messages:
-                result += (
-                    "<p>The provided document is not valid according to the SPDX specification. "
-                    "The following errors were found:</p>\n"
-                )
-            for message in self.validation_messages:
-                result += f"<p>{message.validation_message}</p>\n"
-        else:
-            result = f"""
-            <h2>NTIA Conformance Results</h2>
-            <h3>Conformant: {self.ntia_minimum_elements_compliant} </h3>
-            <p>The provided document couldn't be parsed, check for ntia minimum elements couldn't be performed.</p>
-            <p>The following SPDXParsingError was raised:<p><ul>"""
-            for error in self.parsing_error:
-                result += f"""<li>{error}</li>"""
-
-            result += """</ul>"""
-
-        return result
+    def output_html(self) -> str:
+        raise NotImplementedError("This method should be implemented by subclasses.")
diff --git a/pyproject.toml b/pyproject.toml
index 9f34b38..1a8c296 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,7 @@
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
+
 [build-system]
 requires = ["setuptools>=61.2", "setuptools_scm[toml]>=3.4.3"]
 build-backend = "setuptools.build_meta"
@@ -6,17 +10,18 @@ build-backend = "setuptools.build_meta"
 name = "ntia_conformance_checker"
 version = "3.0.2"
 authors = [
-    {name = "Josh Lin", email = "linynjosh@gmail.com"},
-    {name = "John Speed Meyers", email = "johnmeyersster@gmail.com"}
+    { name = "Josh Lin", email = "linynjosh@gmail.com" },
+    { name = "John Speed Meyers", email = "johnmeyersster@gmail.com" },
+    { name = "Arthit Suriyawongkul", email = "arthit@gmail.com" },
 ]
 maintainers = [
-    {name = "John Speed Meyers", email = "johnmeyersster@gmail.com"},
-    {name = "Gary O'Neall", email = "gary@sourceauditor.com"},
-    {name = "Josh Lin", email = "linynjosh@gmail.com"},
-    {name = "SPDX group at the Linux Foundation and others", email = "spdx-implementers+owner@lists.spdx.org"},
+    { name = "John Speed Meyers", email = "johnmeyersster@gmail.com" },
+    { name = "Gary O'Neall", email = "gary@sourceauditor.com" },
+    { name = "Josh Lin", email = "linynjosh@gmail.com" },
+    { name = "SPDX group at the Linux Foundation and others", email = "spdx-implementers+owner@lists.spdx.org" },
 ]
-license = {text = "Apache-2.0"}
-description = "Check SPDX SBOM for NTIA minimum elements"
+license = { text = "Apache-2.0" }
+description = "Check SPDX SBOM for NTIA minimum elements and common SBOM baseline attributes"
 readme = "README.md"
 classifiers = [
     "Intended Audience :: Developers",
@@ -26,17 +31,39 @@ classifiers = [
     "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
+    "Topic :: Security",
+    "Topic :: Software Development :: Libraries :: Python Modules",
+    "Topic :: System :: Systems Administration",
 ]
-urls = {Homepage = "https://github.com/spdx/ntia-conformance-checker"}
+urls = { Homepage = "https://github.com/spdx/ntia-conformance-checker" }
 requires-python = ">=3.8"
-keywords = ["spdx", "sbom", "ntia"]
-dependencies = [ "spdx-tools==0.8.3"]
+keywords = [
+    "spdx",
+    "sbom",
+    "ntia",
+    "cisa",
+    "minimum elements",
+    "baseline attributes",
+    "software bill of materials",
+    "bill of materials",
+    "common SBOM",
+    "software package data exchange",
+    "software component transparency",
+]
+dependencies = ["spdx-tools==0.8.3"]
 
 [project.optional-dependencies]
 test = ["pytest"]
 
-[tool.setuptools]
-packages = ["ntia_conformance_checker"]
-
 [project.scripts]
+# Both "ntia-checker" and "sbomcheck" are identical.
+# "ntia-checker" is kept for backward compatibility.
+# While "sbomcheck" is introduced for a more generic name,
+# to accommodate other compliance standards.
 ntia-checker = "ntia_conformance_checker.main:main"
+sbomcheck = "ntia_conformance_checker.main:main"
+
+[tool.setuptools]
+packages = ["ntia_conformance_checker"]
diff --git a/setup.py b/setup.py
index b6787b7..5d8ff09 100755
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,9 @@
 #!/usr/bin/python
+
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
+
 """module for building and distributing this project as a python package"""
 
 from setuptools import setup


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29..625bbaa 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
diff --git a/tests/data/missing_concluded_license/SPDXJSONExample-v2.3.spdx.json b/tests/data/missing_concluded_license/SPDXJSONExample-v2.3.spdx.json
new file mode 100644
index 0000000..f1a64ae
--- /dev/null
+++ b/tests/data/missing_concluded_license/SPDXJSONExample-v2.3.spdx.json
@@ -0,0 +1,239 @@
+{
+  "SPDXID" : "SPDXRef-DOCUMENT",
+  "spdxVersion" : "SPDX-2.3",
+  "creationInfo" : {
+    "comment" : "This package has been shipped in source and binary form.\nThe binaries were created with gcc 4.5.1 and expect to link to\ncompatible system run time libraries.",
+    "created" : "2010-01-29T18:30:22Z",
+    "creators" : [ "Tool: LicenseFind-1.0", "Organization: ExampleCodeInspect ()", "Person: Jane Doe ()" ],
+    "licenseListVersion" : "3.17"
+  },
+  "name" : "SPDX-Tools-v2.0",
+  "dataLicense" : "CC0-1.0",
+  "comment" : "This document was created using SPDX 2.0 using licenses from the web site.",
+  "externalDocumentRefs" : [ {
+    "externalDocumentId" : "DocumentRef-spdx-tool-1.2",
+    "checksum" : {
+      "algorithm" : "SHA1",
+      "checksumValue" : "d6a770ba38583ed4bb4525bd96e50461655d2759"
+    },
+    "spdxDocument" : "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"
+  } ],
+  "hasExtractedLicensingInfos" : [ {
+    "licenseId" : "LicenseRef-1",
+    "extractedText" : "/*\n * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n *    derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/"
+  }, {
+    "licenseId" : "LicenseRef-2",
+    "extractedText" : "This package includes the GRDDL parser developed by Hewlett Packard under the following license:\n© Copyright 2007 Hewlett-Packard Development Company, LP\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \nThe name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. \nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+  }, {
+    "licenseId" : "LicenseRef-4",
+    "extractedText" : "/*\n * (c) Copyright 2009 University of Bristol\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n *    derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/"
+  }, {
+    "licenseId" : "LicenseRef-Beerware-4.2",
+    "comment" : "The beerware license has a couple of other standard variants.",
+    "extractedText" : "\"THE BEER-WARE LICENSE\" (Revision 42):\nphk@FreeBSD.ORG wrote this file. As long as you retain this notice you\ncan do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp",
+    "name" : "Beer-Ware License (Version 42)",
+    "seeAlsos" : [ "http://people.freebsd.org/~phk/" ]
+  }, {
+    "licenseId" : "LicenseRef-3",
+    "comment" : "This is tye CyperNeko License",
+    "extractedText" : "The CyberNeko Software License, Version 1.0\n\n \n(C) Copyright 2002-2005, Andy Clark.  All rights reserved.\n \nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer. \n\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in\n   the documentation and/or other materials provided with the\n   distribution.\n\n3. The end-user documentation included with the redistribution,\n   if any, must include the following acknowledgment:  \n     \"This product includes software developed by Andy Clark.\"\n   Alternately, this acknowledgment may appear in the software itself,\n   if and wherever such third-party acknowledgments normally appear.\n\n4. The names \"CyberNeko\" and \"NekoHTML\" must not be used to endorse\n   or promote products derived from this software without prior \n   written permission. For written permission, please contact \n   andyc@cyberneko.net.\n\n5. Products derived from this software may not be called \"CyberNeko\",\n   nor may \"CyberNeko\" appear in their name, without prior written\n   permission of the author.\n\nTHIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\nWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, \nOR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT \nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR \nBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE \nOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, \nEVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
+    "name" : "CyberNeko License",
+    "seeAlsos" : [ "http://people.apache.org/~andyc/neko/LICENSE", "http://justasample.url.com" ]
+  } ],
+  "annotations" : [ {
+    "annotationDate" : "2010-01-29T18:30:22Z",
+    "annotationType" : "OTHER",
+    "annotator" : "Person: Jane Doe ()",
+    "comment" : "Document level annotation"
+  }, {
+    "annotationDate" : "2010-02-10T00:00:00Z",
+    "annotationType" : "REVIEW",
+    "annotator" : "Person: Joe Reviewer",
+    "comment" : "This is just an example.  Some of the non-standard licenses look like they are actually BSD 3 clause licenses"
+  }, {
+    "annotationDate" : "2011-03-13T00:00:00Z",
+    "annotationType" : "REVIEW",
+    "annotator" : "Person: Suzanne Reviewer",
+    "comment" : "Another example reviewer."
+  } ],
+  "documentDescribes" : [ "SPDXRef-File", "SPDXRef-Package" ],
+  "documentNamespace" : "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301",
+  "packages" : [ {
+    "SPDXID" : "SPDXRef-Package",
+    "annotations" : [ {
+      "annotationDate" : "2011-01-29T18:30:22Z",
+      "annotationType" : "OTHER",
+      "annotator" : "Person: Package Commenter",
+      "comment" : "Package level annotation"
+    } ],
+    "attributionTexts" : [ "The GNU C Library is free software.  See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed.  License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually." ],
+    "builtDate" : "2011-01-29T18:30:22Z",
+    "checksums" : [ {
+      "algorithm" : "MD5",
+      "checksumValue" : "624c1abb3664f4b35547e7c73864ad24"
+    }, {
+      "algorithm" : "SHA1",
+      "checksumValue" : "85ed0817af83a24ad8da68c2b5094de69833983c"
+    }, {
+      "algorithm" : "SHA256",
+      "checksumValue" : "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"
+    }, {
+      "algorithm" : "BLAKE2b-384",
+      "checksumValue" : "aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706"
+    } ],
+    "copyrightText" : "Copyright 2008-2010 John Smith",
+    "description" : "The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems.",
+    "downloadLocation" : "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz",
+    "externalRefs" : [ {
+      "referenceCategory" : "SECURITY",
+      "referenceLocator" : "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*",
+      "referenceType" : "cpe23Type"
+    }, {
+      "comment" : "This is the external ref for Acme",
+      "referenceCategory" : "OTHER",
+      "referenceLocator" : "acmecorp/acmenator/4.1.3-alpha",
+      "referenceType" : "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge"
+    } ],
+    "filesAnalyzed" : true,
+    "homepage" : "http://ftp.gnu.org/gnu/glibc",
+    "licenseComments" : "The license for this project changed with the release of version x.y.  The version of the project included here post-dates the license change.",
+    "licenseConcluded" : "",
+    "licenseDeclared" : "(LGPL-2.0-only AND LicenseRef-3)",
+    "licenseInfoFromFiles" : [ "GPL-2.0-only", "LicenseRef-2", "LicenseRef-1" ],
+    "name" : "glibc",
+    "originator" : "Organization: ExampleCodeInspect (contact@example.com)",
+    "packageFileName" : "glibc-2.11.1.tar.gz",
+    "packageVerificationCode" : {
+      "packageVerificationCodeExcludedFiles" : [ "./package.spdx" ],
+      "packageVerificationCodeValue" : "d6a770ba38583ed4bb4525bd96e50461655d2758"
+    },
+    "primaryPackagePurpose" : "SOURCE",
+    "hasFiles" : [ "SPDXRef-Specification", "SPDXRef-Specification", "SPDXRef-CommonsLangSrc", "SPDXRef-Specification", "SPDXRef-CommonsLangSrc", "SPDXRef-JenaLib", "SPDXRef-Specification", "SPDXRef-CommonsLangSrc", "SPDXRef-JenaLib", "SPDXRef-DoapSource", "SPDXRef-Specification", "SPDXRef-CommonsLangSrc", "SPDXRef-JenaLib", "SPDXRef-DoapSource" ],
+    "releaseDate" : "2012-01-29T18:30:22Z",
+    "sourceInfo" : "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.",
+    "summary" : "GNU C library.",
+    "supplier" : "Organization: ExampleCodeInspect (contact@example.com)",
+    "validUntilDate" : "2014-01-29T18:30:22Z",
+    "versionInfo" : "2.11.1"
+  } ],
+  "files" : [ {
+    "SPDXID" : "SPDXRef-DoapSource",
+    "checksums" : [ {
+      "algorithm" : "SHA1",
+      "checksumValue" : "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
+    } ],
+    "copyrightText" : "Copyright 2010, 2011 Source Auditor Inc.",
+    "fileContributors" : [ "Protecode Inc.", "SPDX Technical Team Members", "Open Logic Inc.", "Source Auditor Inc.", "Black Duck Software In.c" ],
+    "fileName" : "./src/org/spdx/parser/DOAPProject.java",
+    "fileTypes" : [ "SOURCE" ],
+    "licenseConcluded" : "Apache-2.0",
+    "licenseInfoInFiles" : [ "Apache-2.0" ]
+  }, {
+    "SPDXID" : "SPDXRef-CommonsLangSrc",
+    "checksums" : [ {
+      "algorithm" : "SHA1",
+      "checksumValue" : "c2b4e1c67a2d28fced849ee1bb76e7391b93f125"
+    } ],
+    "comment" : "This file is used by Jena",
+    "copyrightText" : "Copyright 2001-2011 The Apache Software Foundation",
+    "fileContributors" : [ "Apache Software Foundation" ],
+    "fileName" : "./lib-source/commons-lang3-3.1-sources.jar",
+    "fileTypes" : [ "ARCHIVE" ],
+    "licenseConcluded" : "Apache-2.0",
+    "licenseInfoInFiles" : [ "Apache-2.0" ],
+    "noticeText" : "Apache Commons Lang\nCopyright 2001-2011 The Apache Software Foundation\n\nThis product includes software developed by\nThe Apache Software Foundation (http://www.apache.org/).\n\nThis product includes software from the Spring Framework,\nunder the Apache License 2.0 (see: StringUtils.containsWhitespace())"
+  }, {
+    "SPDXID" : "SPDXRef-JenaLib",
+    "checksums" : [ {
+      "algorithm" : "SHA1",
+      "checksumValue" : "3ab4e1c67a2d28fced849ee1bb76e7391b93f125"
+    } ],
+    "comment" : "This file belongs to Jena",
+    "copyrightText" : "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP",
+    "fileContributors" : [ "Apache Software Foundation", "Hewlett Packard Inc." ],
+    "fileName" : "./lib-source/jena-2.6.3-sources.jar",
+    "fileTypes" : [ "ARCHIVE" ],
+    "licenseComments" : "This license is used by Jena",
+    "licenseConcluded" : "LicenseRef-1",
+    "licenseInfoInFiles" : [ "LicenseRef-1" ]
+  }, {
+    "SPDXID" : "SPDXRef-Specification",
+    "checksums" : [ {
+      "algorithm" : "SHA1",
+      "checksumValue" : "fff4e1c67a2d28fced849ee1bb76e7391b93f125"
+    } ],
+    "comment" : "Specification Documentation",
+    "fileName" : "./docs/myspec.pdf",
+    "fileTypes" : [ "DOCUMENTATION" ]
+  }, {
+    "SPDXID" : "SPDXRef-File",
+    "annotations" : [ {
+      "annotationDate" : "2011-01-29T18:30:22Z",
+      "annotationType" : "OTHER",
+      "annotator" : "Person: File Commenter",
+      "comment" : "File level annotation"
+    } ],
+    "checksums" : [ {
+      "algorithm" : "SHA1",
+      "checksumValue" : "d6a770ba38583ed4bb4525bd96e50461655d2758"
+    }, {
+      "algorithm" : "MD5",
+      "checksumValue" : "624c1abb3664f4b35547e7c73864ad24"
+    } ],
+    "comment" : "The concluded license was taken from the package level that the file was included in.\nThis information was found in the COPYING.txt file in the xyz directory.",
+    "copyrightText" : "Copyright 2008-2010 John Smith",
+    "fileContributors" : [ "The Regents of the University of California", "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation" ],
+    "fileName" : "./package/foo.c",
+    "fileTypes" : [ "SOURCE" ],
+    "licenseComments" : "The concluded license was taken from the package level that the file was included in.",
+    "licenseConcluded" : "(LGPL-2.0-only OR LicenseRef-2)",
+    "licenseInfoInFiles" : [ "GPL-2.0-only", "LicenseRef-2" ],
+    "noticeText" : "Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: \nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
+  } ],
+  "snippets" : [ {
+    "SPDXID" : "SPDXRef-Snippet",
+    "comment" : "This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0.",
+    "copyrightText" : "Copyright 2008-2010 John Smith",
+    "licenseComments" : "The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.",
+    "licenseConcluded" : "GPL-2.0-only",
+    "licenseInfoInSnippets" : [ "GPL-2.0-only" ],
+    "name" : "from linux kernel",
+    "ranges" : [ {
+      "endPointer" : {
+        "offset" : 420,
+        "reference" : "SPDXRef-DoapSource"
+      },
+      "startPointer" : {
+        "offset" : 310,
+        "reference" : "SPDXRef-DoapSource"
+      }
+    }, {
+      "endPointer" : {
+        "lineNumber" : 23,
+        "reference" : "SPDXRef-DoapSource"
+      },
+      "startPointer" : {
+        "lineNumber" : 5,
+        "reference" : "SPDXRef-DoapSource"
+      }
+    } ],
+    "snippetFromFile" : "SPDXRef-DoapSource"
+  } ],
+  "relationships" : [ {
+    "spdxElementId" : "SPDXRef-DOCUMENT",
+    "relationshipType" : "CONTAINS",
+    "relatedSpdxElement" : "SPDXRef-Package"
+  }, {
+    "spdxElementId" : "SPDXRef-DOCUMENT",
+    "relationshipType" : "COPY_OF",
+    "relatedSpdxElement" : "DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement"
+  }, {
+    "spdxElementId" : "SPDXRef-CommonsLangSrc",
+    "relationshipType" : "GENERATED_FROM",
+    "relatedSpdxElement" : "NOASSERTION"
+  }, {
+    "spdxElementId" : "SPDXRef-JenaLib",
+    "relationshipType" : "CONTAINS",
+    "relatedSpdxElement" : "SPDXRef-Package"
+  } ]
+}
\ No newline at end of file
diff --git a/tests/data/missing_concluded_license/SPDXRdfExample-v2.3.spdx.rdf.xml b/tests/data/missing_concluded_license/SPDXRdfExample-v2.3.spdx.rdf.xml
new file mode 100644
index 0000000..8bf3ce8
--- /dev/null
+++ b/tests/data/missing_concluded_license/SPDXRdfExample-v2.3.spdx.rdf.xml
@@ -0,0 +1,506 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rdf:RDF
+  xmlns:ptr="http://www.w3.org/2009/pointers#"
+  xmlns:spdx="http://spdx.org/rdf/terms#"
+  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+  xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+  xmlns:doap="http://usefulinc.com/ns/doap#"
+>
+  <spdx:SpdxDocument rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DOCUMENT">
+    <spdx:hasExtractedLicensingInfo>
+      <spdx:ExtractedLicensingInfo rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-3">
+        <spdx:name>CyberNeko License</spdx:name>
+        <spdx:extractedText>The CyberNeko Software License, Version 1.0
+
+ 
+(C) Copyright 2002-2005, Andy Clark.  All rights reserved.
+ 
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer. 
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+3. The end-user documentation included with the redistribution,
+   if any, must include the following acknowledgment:  
+     "This product includes software developed by Andy Clark."
+   Alternately, this acknowledgment may appear in the software itself,
+   if and wherever such third-party acknowledgments normally appear.
+
+4. The names "CyberNeko" and "NekoHTML" must not be used to endorse
+   or promote products derived from this software without prior 
+   written permission. For written permission, please contact 
+   andyc@cyberneko.net.
+
+5. Products derived from this software may not be called "CyberNeko",
+   nor may "CyberNeko" appear in their name, without prior written
+   permission of the author.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</spdx:extractedText>
+        <rdfs:seeAlso>http://people.apache.org/~andyc/neko/LICENSE</rdfs:seeAlso>
+        <rdfs:seeAlso>http://justasample.url.com</rdfs:seeAlso>
+        <spdx:licenseId>LicenseRef-3</spdx:licenseId>
+        <rdfs:comment>This is tye CyperNeko License</rdfs:comment>
+      </spdx:ExtractedLicensingInfo>
+    </spdx:hasExtractedLicensingInfo>
+    <spdx:hasExtractedLicensingInfo>
+      <spdx:ExtractedLicensingInfo rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-4">
+        <spdx:licenseId>LicenseRef-4</spdx:licenseId>
+        <spdx:extractedText>/*
+ * (c) Copyright 2009 University of Bristol
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/</spdx:extractedText>
+      </spdx:ExtractedLicensingInfo>
+    </spdx:hasExtractedLicensingInfo>
+    <spdx:hasExtractedLicensingInfo>
+      <spdx:ExtractedLicensingInfo rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-Beerware-4.2">
+        <spdx:licenseId>LicenseRef-Beerware-4.2</spdx:licenseId>
+        <spdx:name>Beer-Ware License (Version 42)</spdx:name>
+        <spdx:extractedText>"THE BEER-WARE LICENSE" (Revision 42):
+phk@FreeBSD.ORG wrote this file. As long as you retain this notice you
+can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp</spdx:extractedText>
+        <rdfs:seeAlso>http://people.freebsd.org/~phk/</rdfs:seeAlso>
+        <rdfs:comment>The beerware license has a couple of other standard variants.</rdfs:comment>
+      </spdx:ExtractedLicensingInfo>
+    </spdx:hasExtractedLicensingInfo>
+    <spdx:hasExtractedLicensingInfo>
+      <spdx:ExtractedLicensingInfo rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-2">
+        <spdx:licenseId>LicenseRef-2</spdx:licenseId>
+        <spdx:extractedText>This package includes the GRDDL parser developed by Hewlett Packard under the following license:
+© Copyright 2007 Hewlett-Packard Development Company, LP
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 
+The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. 
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</spdx:extractedText>
+      </spdx:ExtractedLicensingInfo>
+    </spdx:hasExtractedLicensingInfo>
+    <spdx:hasExtractedLicensingInfo>
+      <spdx:ExtractedLicensingInfo rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-1">
+        <spdx:licenseId>LicenseRef-1</spdx:licenseId>
+        <spdx:extractedText>/*
+ * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/</spdx:extractedText>
+      </spdx:ExtractedLicensingInfo>
+    </spdx:hasExtractedLicensingInfo>
+    <spdx:dataLicense rdf:resource="http://spdx.org/licenses/CC0-1.0"/>
+    <spdx:annotation>
+      <spdx:Annotation rdf:nodeID="N587cdf977fc747849d9928b9e807bb29">
+        <spdx:annotator>Person: Jane Doe</spdx:annotator>
+        <rdfs:comment>Document level annotation</rdfs:comment>
+        <spdx:annotationDate>2010-01-29T18:30:22Z</spdx:annotationDate>
+        <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_other"/>
+      </spdx:Annotation>
+    </spdx:annotation>
+    <spdx:annotation>
+      <spdx:Annotation rdf:nodeID="Nd7e44ddf90ff42cfb98e56e86c36fb6b">
+        <spdx:annotator>Person: Joe Reviewer</spdx:annotator>
+        <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_review"/>
+        <rdfs:comment>This is just an example.  Some of the non-standard licenses look like they are actually BSD 3 clause licenses</rdfs:comment>
+        <spdx:annotationDate>2010-02-10T00:00:00Z</spdx:annotationDate>
+      </spdx:Annotation>
+    </spdx:annotation>
+    <spdx:annotation>
+      <spdx:Annotation rdf:nodeID="Nd408ec3ff9a4477292353709052eadd6">
+        <spdx:annotator>Person: Suzanne Reviewer</spdx:annotator>
+        <rdfs:comment>Another example reviewer.</rdfs:comment>
+        <spdx:annotationDate>2011-03-13T00:00:00Z</spdx:annotationDate>
+        <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_review"/>
+      </spdx:Annotation>
+    </spdx:annotation>
+    <spdx:relationship>
+      <spdx:Relationship rdf:nodeID="N59a1edcde1e7430b81013a61ed4172a0">
+        <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes"/>
+        <spdx:relatedSpdxElement>
+          <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Package">
+            <spdx:releaseDate>2012-01-29T18:30:22Z</spdx:releaseDate>
+            <spdx:sourceInfo>uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.</spdx:sourceInfo>
+            <spdx:licenseComments>The license for this project changed with the release of version x.y.  The version of the project included here post-dates the license change.</spdx:licenseComments>
+            <spdx:annotation>
+              <spdx:Annotation rdf:nodeID="N32d6284286f3470a89108a760a331604">
+                <spdx:annotationDate>2011-01-29T18:30:22Z</spdx:annotationDate>
+                <rdfs:comment>Package level annotation</rdfs:comment>
+                <spdx:annotator>Person: Package Commenter</spdx:annotator>
+                <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_other"/>
+              </spdx:Annotation>
+            </spdx:annotation>
+            <spdx:relationship>
+              <spdx:Relationship rdf:nodeID="N8a8201fb12d74c588d46a03a1884c74b">
+                <spdx:relatedSpdxElement>
+                  <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Specification">
+                    <spdx:fileName>./docs/myspec.pdf</spdx:fileName>
+                    <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_documentation"/>
+                    <spdx:checksum>
+                      <spdx:Checksum rdf:nodeID="N71ff8b2723724c6587dc280395a04174">
+                        <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+                        <spdx:checksumValue>fff4e1c67a2d28fced849ee1bb76e7391b93f125</spdx:checksumValue>
+                      </spdx:Checksum>
+                    </spdx:checksum>
+                    <rdfs:comment>Specification Documentation</rdfs:comment>
+                  </spdx:File>
+                </spdx:relatedSpdxElement>
+                <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_contains"/>
+              </spdx:Relationship>
+            </spdx:relationship>
+            <spdx:relationship>
+              <spdx:Relationship rdf:nodeID="N3661e93a2b8b477793d2e6b311d75fdf">
+                <spdx:relatedSpdxElement>
+                  <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource">
+                    <spdx:copyrightText>Copyright 2010, 2011 Source Auditor Inc.</spdx:copyrightText>
+                    <spdx:fileContributor>SPDX Technical Team Members</spdx:fileContributor>
+                    <spdx:fileContributor>Black Duck Software In.c</spdx:fileContributor>
+                    <spdx:fileContributor>Source Auditor Inc.</spdx:fileContributor>
+                    <spdx:fileContributor>Protecode Inc.</spdx:fileContributor>
+                    <spdx:fileContributor>Open Logic Inc.</spdx:fileContributor>
+                    <spdx:licenseConcluded rdf:resource="http://spdx.org/licenses/Apache-2.0"/>
+                    <spdx:licenseInfoInFile rdf:resource="http://spdx.org/licenses/Apache-2.0"/>
+                    <spdx:fileName>./src/org/spdx/parser/DOAPProject.java</spdx:fileName>
+                    <spdx:checksum>
+                      <spdx:Checksum rdf:nodeID="N3cdfba11fa574681a9062dd14f5ccb10">
+                        <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+                        <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+                      </spdx:Checksum>
+                    </spdx:checksum>
+                    <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_source"/>
+                  </spdx:File>
+                </spdx:relatedSpdxElement>
+                <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_contains"/>
+              </spdx:Relationship>
+            </spdx:relationship>
+            <spdx:relationship>
+              <spdx:Relationship rdf:nodeID="N983eadaf97ee4818a9da54bd328a8faf">
+                <spdx:relatedSpdxElement>
+                  <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-JenaLib">
+                    <spdx:fileContributor>Apache Software Foundation</spdx:fileContributor>
+                    <spdx:fileContributor>Hewlett Packard Inc.</spdx:fileContributor>
+                    <spdx:licenseConcluded rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-1"/>
+                    <rdfs:comment>This file belongs to Jena</rdfs:comment>
+                    <spdx:licenseInfoInFile rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-1"/>
+                    <spdx:copyrightText>(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP</spdx:copyrightText>
+                    <spdx:relationship>
+                      <spdx:Relationship rdf:nodeID="N8b72644677a249e88cbc4a55f63534c1">
+                        <spdx:relatedSpdxElement rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Package"/>
+                        <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_contains"/>
+                      </spdx:Relationship>
+                    </spdx:relationship>
+                    <spdx:fileName>./lib-source/jena-2.6.3-sources.jar</spdx:fileName>
+                    <spdx:checksum>
+                      <spdx:Checksum rdf:nodeID="Nb7775189646741418e6862aaa49bc526">
+                        <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+                        <spdx:checksumValue>3ab4e1c67a2d28fced849ee1bb76e7391b93f125</spdx:checksumValue>
+                      </spdx:Checksum>
+                    </spdx:checksum>
+                    <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_archive"/>
+                    <spdx:licenseComments>This license is used by Jena</spdx:licenseComments>
+                  </spdx:File>
+                </spdx:relatedSpdxElement>
+                <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_contains"/>
+              </spdx:Relationship>
+            </spdx:relationship>
+            <spdx:relationship>
+              <spdx:Relationship rdf:nodeID="N83f60bbddf014e2b8e8bfc42d0312faa">
+                <spdx:relatedSpdxElement>
+                  <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-CommonsLangSrc">
+                    <spdx:licenseConcluded rdf:resource="http://spdx.org/licenses/Apache-2.0"/>
+                    <spdx:licenseInfoInFile rdf:resource="http://spdx.org/licenses/Apache-2.0"/>
+                    <spdx:fileName>./lib-source/commons-lang3-3.1-sources.jar</spdx:fileName>
+                    <spdx:fileContributor>Apache Software Foundation</spdx:fileContributor>
+                    <spdx:relationship>
+                      <spdx:Relationship rdf:nodeID="N7bc27d28ee304392a00d13353c650b5e">
+                        <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_generatedFrom"/>
+                        <spdx:relatedSpdxElement rdf:resource="http://spdx.org/rdf/terms#noassertion"/>
+                      </spdx:Relationship>
+                    </spdx:relationship>
+                    <spdx:copyrightText>Copyright 2001-2011 The Apache Software Foundation</spdx:copyrightText>
+                    <spdx:noticeText>Apache Commons Lang
+Copyright 2001-2011 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software from the Spring Framework,
+under the Apache License 2.0 (see: StringUtils.containsWhitespace())</spdx:noticeText>
+                    <rdfs:comment>This file is used by Jena</rdfs:comment>
+                    <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_archive"/>
+                    <spdx:checksum>
+                      <spdx:Checksum rdf:nodeID="Nb10071355b754cc0926059286ecc76f1">
+                        <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+                        <spdx:checksumValue>c2b4e1c67a2d28fced849ee1bb76e7391b93f125</spdx:checksumValue>
+                      </spdx:Checksum>
+                    </spdx:checksum>
+                  </spdx:File>
+                </spdx:relatedSpdxElement>
+                <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_contains"/>
+              </spdx:Relationship>
+            </spdx:relationship>
+            <spdx:attributionText>The GNU C Library is free software.  See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed.  License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually.</spdx:attributionText>
+            <spdx:externalRef>
+              <spdx:ExternalRef rdf:nodeID="N87abaa44d87b47bfb7b93cf5391d552b">
+                <spdx:referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</spdx:referenceLocator>
+                <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_security"/>
+                <spdx:referenceType rdf:resource="http://spdx.org/rdf/references/cpe23Type"/>
+              </spdx:ExternalRef>
+            </spdx:externalRef>
+            <spdx:externalRef>
+              <spdx:ExternalRef rdf:nodeID="N6f077514d2c24828bb45bff322ba61b3">
+                <spdx:referenceType rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge"/>
+                <rdfs:comment>This is the external ref for Acme</rdfs:comment>
+                <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_other"/>
+                <spdx:referenceLocator>acmecorp/acmenator/4.1.3-alpha</spdx:referenceLocator>
+              </spdx:ExternalRef>
+            </spdx:externalRef>
+            <spdx:supplier>Organization: ExampleCodeInspect (contact@example.com)</spdx:supplier>
+            <spdx:checksum>
+              <spdx:Checksum rdf:nodeID="N98510266a7284a1686da4e459dd5e58b">
+                <spdx:checksumValue>aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706</spdx:checksumValue>
+                <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_blake2b384"/>
+              </spdx:Checksum>
+            </spdx:checksum>
+            <spdx:checksum>
+              <spdx:Checksum rdf:nodeID="N1de0422a91114c13adf5e143050e5641">
+                <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_md5"/>
+                <spdx:checksumValue>624c1abb3664f4b35547e7c73864ad24</spdx:checksumValue>
+              </spdx:Checksum>
+            </spdx:checksum>
+            <spdx:checksum>
+              <spdx:Checksum rdf:nodeID="N212df2dbb92c4370b4b5ea88e832cdd4">
+                <spdx:checksumValue>85ed0817af83a24ad8da68c2b5094de69833983c</spdx:checksumValue>
+                <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+              </spdx:Checksum>
+            </spdx:checksum>
+            <spdx:checksum>
+              <spdx:Checksum rdf:nodeID="N0777d2ae8c6342779bc6a146ef9fb822">
+                <spdx:checksumValue>11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd</spdx:checksumValue>
+                <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha256"/>
+              </spdx:Checksum>
+            </spdx:checksum>
+            <spdx:licenseInfoFromFiles rdf:resource="http://spdx.org/licenses/GPL-2.0-only"/>
+            <spdx:licenseInfoFromFiles rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-2"/>
+            <spdx:licenseInfoFromFiles rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-1"/>
+            <spdx:licenseConcluded>
+            </spdx:licenseConcluded>
+            <spdx:validUntilDate>2014-01-29T18:30:22Z</spdx:validUntilDate>
+            <spdx:copyrightText>Copyright 2008-2010 John Smith</spdx:copyrightText>
+            <spdx:description>The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems.</spdx:description>
+            <spdx:packageVerificationCode>
+              <spdx:PackageVerificationCode rdf:nodeID="Ne806fa90587c4eb7a4b5e7139d58671f">
+                <spdx:packageVerificationCodeExcludedFile>./package.spdx</spdx:packageVerificationCodeExcludedFile>
+                <spdx:packageVerificationCodeValue>d6a770ba38583ed4bb4525bd96e50461655d2758</spdx:packageVerificationCodeValue>
+              </spdx:PackageVerificationCode>
+            </spdx:packageVerificationCode>
+            <spdx:summary>GNU C library.</spdx:summary>
+            <spdx:originator>Organization: ExampleCodeInspect (contact@example.com)</spdx:originator>
+            <spdx:builtDate>2011-01-29T18:30:22Z</spdx:builtDate>
+            <spdx:licenseDeclared>
+              <spdx:ConjunctiveLicenseSet rdf:nodeID="N45c3c34ce93d4d769c3edee387495c95">
+                <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0-only"/>
+                <spdx:member rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-3"/>
+              </spdx:ConjunctiveLicenseSet>
+            </spdx:licenseDeclared>
+            <doap:homepage>http://ftp.gnu.org/gnu/glibc</doap:homepage>
+            <spdx:downloadLocation>http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz</spdx:downloadLocation>
+            <spdx:versionInfo>2.11.1</spdx:versionInfo>
+            <spdx:name>glibc</spdx:name>
+            <spdx:packageFileName>glibc-2.11.1.tar.gz</spdx:packageFileName>
+            <spdx:filesAnalyzed rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">true</spdx:filesAnalyzed>
+            <spdx:primaryPackagePurpose rdf:resource="http://spdx.org/rdf/terms#purpose_source"/>
+          </spdx:Package>
+        </spdx:relatedSpdxElement>
+      </spdx:Relationship>
+    </spdx:relationship>
+    <spdx:relationship>
+      <spdx:Relationship rdf:nodeID="N569b0a0405134969b4d53d45de54290c">
+        <spdx:relatedSpdxElement>
+          <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+            <spdx:checksum>
+              <spdx:Checksum rdf:nodeID="Nd9be45253e7d45b2aed0504c25645b71">
+                <spdx:checksumValue>624c1abb3664f4b35547e7c73864ad24</spdx:checksumValue>
+                <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_md5"/>
+              </spdx:Checksum>
+            </spdx:checksum>
+            <spdx:checksum>
+              <spdx:Checksum rdf:nodeID="N3d452d34d00f46609115cd461c59d67e">
+                <spdx:checksumValue>d6a770ba38583ed4bb4525bd96e50461655d2758</spdx:checksumValue>
+                <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+              </spdx:Checksum>
+            </spdx:checksum>
+            <spdx:noticeText>Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</spdx:noticeText>
+            <spdx:copyrightText>Copyright 2008-2010 John Smith</spdx:copyrightText>
+            <spdx:fileName>./package/foo.c</spdx:fileName>
+            <rdfs:comment>The concluded license was taken from the package level that the file was included in.
+This information was found in the COPYING.txt file in the xyz directory.</rdfs:comment>
+            <spdx:licenseInfoInFile rdf:resource="http://spdx.org/licenses/GPL-2.0-only"/>
+            <spdx:licenseInfoInFile rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-2"/>
+            <spdx:fileContributor>The Regents of the University of California</spdx:fileContributor>
+            <spdx:fileContributor>IBM Corporation</spdx:fileContributor>
+            <spdx:fileContributor>Modified by Paul Mundt lethal@linux-sh.org</spdx:fileContributor>
+            <spdx:licenseConcluded>
+              <spdx:DisjunctiveLicenseSet rdf:nodeID="Nfa2820a6ea6f4d3083b157bfdad00a8d">
+                <spdx:member rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LicenseRef-2"/>
+                <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0-only"/>
+              </spdx:DisjunctiveLicenseSet>
+            </spdx:licenseConcluded>
+            <spdx:annotation>
+              <spdx:Annotation rdf:nodeID="Nd73b0f6add9441da92a53386278b9944">
+                <spdx:annotator>Person: File Commenter</spdx:annotator>
+                <rdfs:comment>File level annotation</rdfs:comment>
+                <spdx:annotationDate>2011-01-29T18:30:22Z</spdx:annotationDate>
+                <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_other"/>
+              </spdx:Annotation>
+            </spdx:annotation>
+            <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_source"/>
+            <spdx:licenseComments>The concluded license was taken from the package level that the file was included in.</spdx:licenseComments>
+          </spdx:File>
+        </spdx:relatedSpdxElement>
+        <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes"/>
+      </spdx:Relationship>
+    </spdx:relationship>
+    <spdx:relationship>
+      <spdx:Relationship rdf:nodeID="N7e39b8cd620a476d835939a8f6de8de0">
+        <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_contains"/>
+        <spdx:relatedSpdxElement rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Package"/>
+      </spdx:Relationship>
+    </spdx:relationship>
+    <spdx:relationship>
+      <spdx:Relationship rdf:nodeID="Ndc8299a8ad7f448d8cdab59b16a4b707">
+        <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_copyOf"/>
+        <spdx:relatedSpdxElement rdf:resource="http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-ToolsElement"/>
+      </spdx:Relationship>
+    </spdx:relationship>
+    <rdfs:comment>This document was created using SPDX 2.0 using licenses from the web site.</rdfs:comment>
+    <spdx:creationInfo>
+      <spdx:CreationInfo rdf:nodeID="Nfae1ed9de42b47798925e8f2e7946592">
+        <rdfs:comment>This package has been shipped in source and binary form.
+The binaries were created with gcc 4.5.1 and expect to link to
+compatible system run time libraries.</rdfs:comment>
+        <spdx:creator>Tool: LicenseFind-1.0</spdx:creator>
+        <spdx:creator>Person: Jane Doe</spdx:creator>
+        <spdx:creator>Organization: ExampleCodeInspect</spdx:creator>
+        <spdx:created>2010-01-29T18:30:22Z</spdx:created>
+        <spdx:licenseListVersion>3.17</spdx:licenseListVersion>
+      </spdx:CreationInfo>
+    </spdx:creationInfo>
+    <spdx:externalDocumentRef>
+      <spdx:ExternalDocumentRef rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#DocumentRef-spdx-tool-1.2">
+        <spdx:spdxDocument rdf:resource="http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"/>
+        <spdx:checksum>
+          <spdx:Checksum rdf:nodeID="Na58ea60c4ca44224a70c81be0b3e4570">
+            <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+            <spdx:checksumValue>d6a770ba38583ed4bb4525bd96e50461655d2759</spdx:checksumValue>
+          </spdx:Checksum>
+        </spdx:checksum>
+      </spdx:ExternalDocumentRef>
+    </spdx:externalDocumentRef>
+    <spdx:name>SPDX-Tools-v2.0</spdx:name>
+    <spdx:specVersion>SPDX-2.3</spdx:specVersion>
+  </spdx:SpdxDocument>
+  <spdx:Snippet rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Snippet">
+    <rdfs:comment>This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0.</rdfs:comment>
+    <spdx:snippetFromFile rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+    <spdx:name>from linux kernel</spdx:name>
+    <spdx:copyrightText>Copyright 2008-2010 John Smith</spdx:copyrightText>
+    <spdx:range>
+      <ptr:StartEndPointer rdf:nodeID="Nb5b8220f6cf04bc0ba8133a209bf2629">
+        <ptr:startPointer>
+          <ptr:ByteOffsetPointer rdf:nodeID="N38be817558cc4e6b9919c5cdb813dac7">
+            <ptr:offset rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">310</ptr:offset>
+            <ptr:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+          </ptr:ByteOffsetPointer>
+        </ptr:startPointer>
+        <ptr:endPointer>
+          <ptr:ByteOffsetPointer rdf:nodeID="Ndea621fd62dd419f91009c19b5fb8bc6">
+            <ptr:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+            <ptr:offset rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">420</ptr:offset>
+          </ptr:ByteOffsetPointer>
+        </ptr:endPointer>
+      </ptr:StartEndPointer>
+    </spdx:range>
+    <spdx:range>
+      <ptr:StartEndPointer rdf:nodeID="N75ff44777b7d442da9585c5e9268ecbc">
+        <ptr:endPointer>
+          <ptr:LineCharPointer rdf:nodeID="Ndb35feccd0a94855b9c78e85bbe4cb59">
+            <ptr:lineNumber rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">23</ptr:lineNumber>
+            <ptr:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+          </ptr:LineCharPointer>
+        </ptr:endPointer>
+        <ptr:startPointer>
+          <ptr:LineCharPointer rdf:nodeID="Ne236d24535e14c5ba09f8ecaca06ba3a">
+            <ptr:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+            <ptr:lineNumber rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">5</ptr:lineNumber>
+          </ptr:LineCharPointer>
+        </ptr:startPointer>
+      </ptr:StartEndPointer>
+    </spdx:range>
+    <spdx:licenseInfoInSnippet rdf:resource="http://spdx.org/licenses/GPL-2.0-only"/>
+    <spdx:licenseComments>The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.</spdx:licenseComments>
+    <spdx:licenseConcluded rdf:resource="http://spdx.org/licenses/GPL-2.0-only"/>
+  </spdx:Snippet>
+</rdf:RDF>
diff --git a/tests/data/missing_concluded_license/SPDXXMLExample-v2.3.spdx.xml b/tests/data/missing_concluded_license/SPDXXMLExample-v2.3.spdx.xml
new file mode 100644
index 0000000..9bef0ac
--- /dev/null
+++ b/tests/data/missing_concluded_license/SPDXXMLExample-v2.3.spdx.xml
@@ -0,0 +1,417 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Document>
+	<SPDXID>SPDXRef-DOCUMENT</SPDXID>
+	<annotations>
+		<annotationDate>2010-01-29T18:30:22Z</annotationDate>
+		<annotationType>OTHER</annotationType>
+		<annotator>Person: Jane Doe</annotator>
+		<comment>Document level annotation</comment>
+	</annotations>
+	<annotations>
+		<annotationDate>2010-02-10T00:00:00Z</annotationDate>
+		<annotationType>REVIEW</annotationType>
+		<annotator>Person: Joe Reviewer</annotator>
+		<comment>This is just an example.  Some of the non-standard licenses look like they are actually BSD 3 clause licenses</comment>
+	</annotations>
+	<annotations>
+		<annotationDate>2011-03-13T00:00:00Z</annotationDate>
+		<annotationType>REVIEW</annotationType>
+		<annotator>Person: Suzanne Reviewer</annotator>
+		<comment>Another example reviewer.</comment>
+	</annotations>
+	<comment>This document was created using SPDX 2.0 using licenses from the web site.</comment>
+	<creationInfo>
+		<created>2010-01-29T18:30:22Z</created>
+		<creators>Tool: LicenseFind-1.0</creators>
+		<creators>Organization: ExampleCodeInspect</creators>
+		<creators>Person: Jane Doe</creators>
+		<licenseListVersion>3.17</licenseListVersion>
+		<comment>This package has been shipped in source and binary form.
+The binaries were created with gcc 4.5.1 and expect to link to
+compatible system run time libraries.</comment>
+	</creationInfo>
+	<dataLicense>CC0-1.0</dataLicense>
+	<externalDocumentRefs>
+		<externalDocumentId>DocumentRef-spdx-tool-1.2</externalDocumentId>
+		<spdxDocument>http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301</spdxDocument>
+		<checksum>
+			<algorithm>SHA1</algorithm>
+			<checksumValue>d6a770ba38583ed4bb4525bd96e50461655d2759</checksumValue>
+		</checksum>
+	</externalDocumentRefs>
+	<hasExtractedLicensingInfos>
+		<extractedText>/*
+ * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/</extractedText>
+		<licenseId>LicenseRef-1</licenseId>
+	</hasExtractedLicensingInfos>
+	<hasExtractedLicensingInfos>
+		<extractedText>This package includes the GRDDL parser developed by Hewlett Packard under the following license:
+© Copyright 2007 Hewlett-Packard Development Company, LP
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 
+The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. 
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</extractedText>
+		<licenseId>LicenseRef-2</licenseId>
+	</hasExtractedLicensingInfos>
+	<hasExtractedLicensingInfos>
+		<extractedText>/*
+ * (c) Copyright 2009 University of Bristol
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/</extractedText>
+		<licenseId>LicenseRef-4</licenseId>
+	</hasExtractedLicensingInfos>
+	<hasExtractedLicensingInfos>
+		<comment>The beerware license has a couple of other standard variants.</comment>
+		<extractedText>"THE BEER-WARE LICENSE" (Revision 42):
+phk@FreeBSD.ORG wrote this file. As long as you retain this notice you
+can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp</extractedText>
+		<licenseId>LicenseRef-Beerware-4.2</licenseId>
+		<name>Beer-Ware License (Version 42)</name>
+		<seeAlsos>http://people.freebsd.org/~phk/</seeAlsos>
+	</hasExtractedLicensingInfos>
+	<hasExtractedLicensingInfos>
+		<comment>This is tye CyperNeko License</comment>
+		<extractedText>The CyberNeko Software License, Version 1.0
+
+ 
+(C) Copyright 2002-2005, Andy Clark.  All rights reserved.
+ 
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer. 
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+3. The end-user documentation included with the redistribution,
+   if any, must include the following acknowledgment:  
+     "This product includes software developed by Andy Clark."
+   Alternately, this acknowledgment may appear in the software itself,
+   if and wherever such third-party acknowledgments normally appear.
+
+4. The names "CyberNeko" and "NekoHTML" must not be used to endorse
+   or promote products derived from this software without prior 
+   written permission. For written permission, please contact 
+   andyc@cyberneko.net.
+
+5. Products derived from this software may not be called "CyberNeko",
+   nor may "CyberNeko" appear in their name, without prior written
+   permission of the author.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</extractedText>
+		<licenseId>LicenseRef-3</licenseId>
+		<name>CyberNeko License</name>
+		<seeAlsos>http://people.apache.org/~andyc/neko/LICENSE</seeAlsos>
+		<seeAlsos>http://justasample.url.com</seeAlsos>
+	</hasExtractedLicensingInfos>
+	<name>SPDX-Tools-v2.0</name>
+	<spdxVersion>SPDX-2.3</spdxVersion>
+	<documentNamespace>http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301</documentNamespace>
+	<packages>
+		<SPDXID>SPDXRef-Package</SPDXID>
+		<annotations>
+			<annotationDate>2011-01-29T18:30:22Z</annotationDate>
+			<annotationType>OTHER</annotationType>
+			<annotator>Person: Package Commenter</annotator>
+			<comment>Package level annotation</comment>
+		</annotations>
+		<attributionTexts>The GNU C Library is free software.  See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed.  License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually.</attributionTexts>
+		<builtDate>2011-01-29T18:30:22Z</builtDate>
+		<checksums>
+			<algorithm>MD5</algorithm>
+			<checksumValue>624c1abb3664f4b35547e7c73864ad24</checksumValue>
+		</checksums>
+		<checksums>
+			<algorithm>SHA1</algorithm>
+			<checksumValue>85ed0817af83a24ad8da68c2b5094de69833983c</checksumValue>
+		</checksums>
+		<checksums>
+			<algorithm>SHA256</algorithm>
+			<checksumValue>11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd</checksumValue>
+		</checksums>
+		<checksums>
+			<algorithm>BLAKE2b-384</algorithm>
+			<checksumValue>aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706</checksumValue>
+		</checksums>
+		<copyrightText>Copyright 2008-2010 John Smith</copyrightText>
+		<description>The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems.</description>
+		<downloadLocation>http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz</downloadLocation>
+		<externalRefs>
+			<referenceCategory>SECURITY</referenceCategory>
+			<referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</referenceLocator>
+			<referenceType>cpe23Type</referenceType>
+		</externalRefs>
+		<externalRefs>
+			<comment>This is the external ref for Acme</comment>
+			<referenceCategory>OTHER</referenceCategory>
+			<referenceLocator>acmecorp/acmenator/4.1.3-alpha</referenceLocator>
+			<referenceType>http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge</referenceType>
+		</externalRefs>
+		<filesAnalyzed>true</filesAnalyzed>
+		<homepage>http://ftp.gnu.org/gnu/glibc</homepage>
+		<licenseComments>The license for this project changed with the release of version x.y.  The version of the project included here post-dates the license change.</licenseComments>
+		<licenseConcluded></licenseConcluded>
+		<licenseDeclared>LGPL-2.0-only AND LicenseRef-3</licenseDeclared>
+		<licenseInfoFromFiles>GPL-2.0-only</licenseInfoFromFiles>
+		<licenseInfoFromFiles>LicenseRef-2</licenseInfoFromFiles>
+		<licenseInfoFromFiles>LicenseRef-1</licenseInfoFromFiles>
+		<name>glibc</name>
+		<originator>Organization: ExampleCodeInspect (contact@example.com)</originator>
+		<packageFileName>glibc-2.11.1.tar.gz</packageFileName>
+		<packageVerificationCode>
+			<packageVerificationCodeExcludedFiles>./package.spdx</packageVerificationCodeExcludedFiles>
+			<packageVerificationCodeValue>d6a770ba38583ed4bb4525bd96e50461655d2758</packageVerificationCodeValue>
+		</packageVerificationCode>
+		<primaryPackagePurpose>SOURCE</primaryPackagePurpose>
+		<releaseDate>2012-01-29T18:30:22Z</releaseDate>
+		<sourceInfo>uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.</sourceInfo>
+		<summary>GNU C library.</summary>
+		<supplier>Organization: ExampleCodeInspect (contact@example.com)</supplier>
+		<validUntilDate>2014-01-29T18:30:22Z</validUntilDate>
+		<versionInfo>2.11.1</versionInfo>
+	</packages>
+	<files>
+		<SPDXID>SPDXRef-DoapSource</SPDXID>
+		<checksums>
+			<algorithm>SHA1</algorithm>
+			<checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</checksumValue>
+		</checksums>
+		<copyrightText>Copyright 2010, 2011 Source Auditor Inc.</copyrightText>
+		<fileContributors>Protecode Inc.</fileContributors>
+		<fileContributors>SPDX Technical Team Members</fileContributors>
+		<fileContributors>Open Logic Inc.</fileContributors>
+		<fileContributors>Source Auditor Inc.</fileContributors>
+		<fileContributors>Black Duck Software In.c</fileContributors>
+		<fileName>./src/org/spdx/parser/DOAPProject.java</fileName>
+		<fileTypes>SOURCE</fileTypes>
+		<licenseConcluded>Apache-2.0</licenseConcluded>
+		<licenseInfoInFiles>Apache-2.0</licenseInfoInFiles>
+	</files>
+	<files>
+		<SPDXID>SPDXRef-CommonsLangSrc</SPDXID>
+		<checksums>
+			<algorithm>SHA1</algorithm>
+			<checksumValue>c2b4e1c67a2d28fced849ee1bb76e7391b93f125</checksumValue>
+		</checksums>
+		<comment>This file is used by Jena</comment>
+		<copyrightText>Copyright 2001-2011 The Apache Software Foundation</copyrightText>
+		<fileContributors>Apache Software Foundation</fileContributors>
+		<fileName>./lib-source/commons-lang3-3.1-sources.jar</fileName>
+		<fileTypes>ARCHIVE</fileTypes>
+		<licenseConcluded>Apache-2.0</licenseConcluded>
+		<licenseInfoInFiles>Apache-2.0</licenseInfoInFiles>
+		<noticeText>Apache Commons Lang
+Copyright 2001-2011 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software from the Spring Framework,
+under the Apache License 2.0 (see: StringUtils.containsWhitespace())</noticeText>
+	</files>
+	<files>
+		<SPDXID>SPDXRef-JenaLib</SPDXID>
+		<checksums>
+			<algorithm>SHA1</algorithm>
+			<checksumValue>3ab4e1c67a2d28fced849ee1bb76e7391b93f125</checksumValue>
+		</checksums>
+		<comment>This file belongs to Jena</comment>
+		<copyrightText>(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP</copyrightText>
+		<fileContributors>Apache Software Foundation</fileContributors>
+		<fileContributors>Hewlett Packard Inc.</fileContributors>
+		<fileName>./lib-source/jena-2.6.3-sources.jar</fileName>
+		<fileTypes>ARCHIVE</fileTypes>
+		<licenseComments>This license is used by Jena</licenseComments>
+		<licenseConcluded>LicenseRef-1</licenseConcluded>
+		<licenseInfoInFiles>LicenseRef-1</licenseInfoInFiles>
+	</files>
+	<files>
+		<SPDXID>SPDXRef-Specification</SPDXID>
+		<checksums>
+			<algorithm>SHA1</algorithm>
+			<checksumValue>fff4e1c67a2d28fced849ee1bb76e7391b93f125</checksumValue>
+		</checksums>
+		<comment>Specification Documentation</comment>
+		<fileName>./docs/myspec.pdf</fileName>
+		<fileTypes>DOCUMENTATION</fileTypes>
+	</files>
+	<files>
+		<SPDXID>SPDXRef-File</SPDXID>
+		<annotations>
+			<annotationDate>2011-01-29T18:30:22Z</annotationDate>
+			<annotationType>OTHER</annotationType>
+			<annotator>Person: File Commenter</annotator>
+			<comment>File level annotation</comment>
+		</annotations>
+		<checksums>
+			<algorithm>SHA1</algorithm>
+			<checksumValue>d6a770ba38583ed4bb4525bd96e50461655d2758</checksumValue>
+		</checksums>
+		<checksums>
+			<algorithm>MD5</algorithm>
+			<checksumValue>624c1abb3664f4b35547e7c73864ad24</checksumValue>
+		</checksums>
+		<comment>The concluded license was taken from the package level that the file was included in.
+This information was found in the COPYING.txt file in the xyz directory.</comment>
+		<copyrightText>Copyright 2008-2010 John Smith</copyrightText>
+		<fileContributors>The Regents of the University of California</fileContributors>
+		<fileContributors>Modified by Paul Mundt lethal@linux-sh.org</fileContributors>
+		<fileContributors>IBM Corporation</fileContributors>
+		<fileName>./package/foo.c</fileName>
+		<fileTypes>SOURCE</fileTypes>
+		<licenseComments>The concluded license was taken from the package level that the file was included in.</licenseComments>
+		<licenseConcluded>LGPL-2.0-only OR LicenseRef-2</licenseConcluded>
+		<licenseInfoInFiles>GPL-2.0-only</licenseInfoInFiles>
+		<licenseInfoInFiles>LicenseRef-2</licenseInfoInFiles>
+		<noticeText>Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</noticeText>
+	</files>
+	<snippets>
+		<SPDXID>SPDXRef-Snippet</SPDXID>
+		<comment>This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0.</comment>
+		<copyrightText>Copyright 2008-2010 John Smith</copyrightText>
+		<licenseComments>The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.</licenseComments>
+		<licenseConcluded>GPL-2.0-only</licenseConcluded>
+		<licenseInfoInSnippets>GPL-2.0-only</licenseInfoInSnippets>
+		<name>from linux kernel</name>
+		<ranges>
+			<startPointer>
+				<reference>SPDXRef-DoapSource</reference>
+				<offset>310</offset>
+			</startPointer>
+			<endPointer>
+				<reference>SPDXRef-DoapSource</reference>
+				<offset>420</offset>
+			</endPointer>
+		</ranges>
+		<ranges>
+			<startPointer>
+				<reference>SPDXRef-DoapSource</reference>
+				<lineNumber>5</lineNumber>
+			</startPointer>
+			<endPointer>
+				<reference>SPDXRef-DoapSource</reference>
+				<lineNumber>23</lineNumber>
+			</endPointer>
+		</ranges>
+		<snippetFromFile>SPDXRef-DoapSource</snippetFromFile>
+	</snippets>
+	<relationships>
+		<spdxElementId>SPDXRef-DOCUMENT</spdxElementId>
+		<relatedSpdxElement>SPDXRef-Package</relatedSpdxElement>
+		<relationshipType>CONTAINS</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-DOCUMENT</spdxElementId>
+		<relatedSpdxElement>DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement</relatedSpdxElement>
+		<relationshipType>COPY_OF</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-CommonsLangSrc</spdxElementId>
+		<relatedSpdxElement>NOASSERTION</relatedSpdxElement>
+		<relationshipType>GENERATED_FROM</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-JenaLib</spdxElementId>
+		<relatedSpdxElement>SPDXRef-Package</relatedSpdxElement>
+		<relationshipType>CONTAINS</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-DOCUMENT</spdxElementId>
+		<relatedSpdxElement>SPDXRef-File</relatedSpdxElement>
+		<relationshipType>DESCRIBES</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-DOCUMENT</spdxElementId>
+		<relatedSpdxElement>SPDXRef-Package</relatedSpdxElement>
+		<relationshipType>DESCRIBES</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-Package</spdxElementId>
+		<relatedSpdxElement>SPDXRef-Specification</relatedSpdxElement>
+		<relationshipType>CONTAINS</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-Package</spdxElementId>
+		<relatedSpdxElement>SPDXRef-CommonsLangSrc</relatedSpdxElement>
+		<relationshipType>CONTAINS</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-Package</spdxElementId>
+		<relatedSpdxElement>SPDXRef-JenaLib</relatedSpdxElement>
+		<relationshipType>CONTAINS</relationshipType>
+	</relationships>
+	<relationships>
+		<spdxElementId>SPDXRef-Package</spdxElementId>
+		<relatedSpdxElement>SPDXRef-DoapSource</relatedSpdxElement>
+		<relationshipType>CONTAINS</relationshipType>
+	</relationships>
+</Document>
\ No newline at end of file
diff --git a/tests/data/missing_concluded_license/SPDXYAMLExample-v2.3.spdx.yaml b/tests/data/missing_concluded_license/SPDXYAMLExample-v2.3.spdx.yaml
new file mode 100644
index 0000000..6792373
--- /dev/null
+++ b/tests/data/missing_concluded_license/SPDXYAMLExample-v2.3.spdx.yaml
@@ -0,0 +1,377 @@
+SPDXID: SPDXRef-DOCUMENT
+annotations:
+- annotationDate: '2010-01-29T18:30:22Z'
+  annotationType: OTHER
+  annotator: 'Person: Jane Doe'
+  comment: Document level annotation
+- annotationDate: '2010-02-10T00:00:00Z'
+  annotationType: REVIEW
+  annotator: 'Person: Joe Reviewer'
+  comment: This is just an example.  Some of the non-standard licenses look like they
+    are actually BSD 3 clause licenses
+- annotationDate: '2011-03-13T00:00:00Z'
+  annotationType: REVIEW
+  annotator: 'Person: Suzanne Reviewer'
+  comment: Another example reviewer.
+comment: This document was created using SPDX 2.0 using licenses from the web site.
+creationInfo:
+  comment: 'This package has been shipped in source and binary form.
+
+    The binaries were created with gcc 4.5.1 and expect to link to
+
+    compatible system run time libraries.'
+  created: '2010-01-29T18:30:22Z'
+  creators:
+  - 'Tool: LicenseFind-1.0'
+  - 'Organization: ExampleCodeInspect'
+  - 'Person: Jane Doe'
+  licenseListVersion: '3.17'
+dataLicense: CC0-1.0
+documentNamespace: http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301
+externalDocumentRefs:
+- checksum:
+    algorithm: SHA1
+    checksumValue: d6a770ba38583ed4bb4525bd96e50461655d2759
+  externalDocumentId: DocumentRef-spdx-tool-1.2
+  spdxDocument: http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301
+files:
+- SPDXID: SPDXRef-DoapSource
+  checksums:
+  - algorithm: SHA1
+    checksumValue: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
+  copyrightText: Copyright 2010, 2011 Source Auditor Inc.
+  fileContributors:
+  - Protecode Inc.
+  - SPDX Technical Team Members
+  - Open Logic Inc.
+  - Source Auditor Inc.
+  - Black Duck Software In.c
+  fileName: ./src/org/spdx/parser/DOAPProject.java
+  fileTypes:
+  - SOURCE
+  licenseConcluded: Apache-2.0
+  licenseInfoInFiles:
+  - Apache-2.0
+- SPDXID: SPDXRef-CommonsLangSrc
+  checksums:
+  - algorithm: SHA1
+    checksumValue: c2b4e1c67a2d28fced849ee1bb76e7391b93f125
+  comment: This file is used by Jena
+  copyrightText: Copyright 2001-2011 The Apache Software Foundation
+  fileContributors:
+  - Apache Software Foundation
+  fileName: ./lib-source/commons-lang3-3.1-sources.jar
+  fileTypes:
+  - ARCHIVE
+  licenseConcluded: Apache-2.0
+  licenseInfoInFiles:
+  - Apache-2.0
+  noticeText: 'Apache Commons Lang
+
+    Copyright 2001-2011 The Apache Software Foundation
+
+
+    This product includes software developed by
+
+    The Apache Software Foundation (http://www.apache.org/).
+
+
+    This product includes software from the Spring Framework,
+
+    under the Apache License 2.0 (see: StringUtils.containsWhitespace())'
+- SPDXID: SPDXRef-JenaLib
+  checksums:
+  - algorithm: SHA1
+    checksumValue: 3ab4e1c67a2d28fced849ee1bb76e7391b93f125
+  comment: This file belongs to Jena
+  copyrightText: (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+    2009 Hewlett-Packard Development Company, LP
+  fileContributors:
+  - Apache Software Foundation
+  - Hewlett Packard Inc.
+  fileName: ./lib-source/jena-2.6.3-sources.jar
+  fileTypes:
+  - ARCHIVE
+  licenseComments: This license is used by Jena
+  licenseConcluded: LicenseRef-1
+  licenseInfoInFiles:
+  - LicenseRef-1
+- SPDXID: SPDXRef-Specification
+  checksums:
+  - algorithm: SHA1
+    checksumValue: fff4e1c67a2d28fced849ee1bb76e7391b93f125
+  comment: Specification Documentation
+  fileName: ./docs/myspec.pdf
+  fileTypes:
+  - DOCUMENTATION
+- SPDXID: SPDXRef-File
+  annotations:
+  - annotationDate: '2011-01-29T18:30:22Z'
+    annotationType: OTHER
+    annotator: 'Person: File Commenter'
+    comment: File level annotation
+  checksums:
+  - algorithm: SHA1
+    checksumValue: d6a770ba38583ed4bb4525bd96e50461655d2758
+  - algorithm: MD5
+    checksumValue: 624c1abb3664f4b35547e7c73864ad24
+  comment: 'The concluded license was taken from the package level that the file was
+    included in.
+
+    This information was found in the COPYING.txt file in the xyz directory.'
+  copyrightText: Copyright 2008-2010 John Smith
+  fileContributors:
+  - The Regents of the University of California
+  - Modified by Paul Mundt lethal@linux-sh.org
+  - IBM Corporation
+  fileName: ./package/foo.c
+  fileTypes:
+  - SOURCE
+  licenseComments: The concluded license was taken from the package level that the
+    file was included in.
+  licenseConcluded: LGPL-2.0-only OR LicenseRef-2
+  licenseInfoInFiles:
+  - GPL-2.0-only
+  - LicenseRef-2
+  noticeText: "Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com\n\nPermission is\
+    \ hereby granted, free of charge, to any person obtaining a copy of this software\
+    \ and associated documentation files (the \"Software\"), to deal in the Software\
+    \ without restriction, including without limitation the rights to use, copy, modify,\
+    \ merge, publish, distribute, sublicense, and/or sell copies of the Software,\
+    \ and to permit persons to whom the Software is furnished to do so, subject to\
+    \ the following conditions: \nThe above copyright notice and this permission notice\
+    \ shall be included in all copies or substantial portions of the Software.\n\n\
+    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\
+    \ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR\
+    \ A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR\
+    \ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\
+    \ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\
+    \ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
+hasExtractedLicensingInfos:
+- extractedText: "/*\n * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,\
+    \ 2008, 2009 Hewlett-Packard Development Company, LP\n * All rights reserved.\n\
+    \ *\n * Redistribution and use in source and binary forms, with or without\n *\
+    \ modification, are permitted provided that the following conditions\n * are met:\n\
+    \ * 1. Redistributions of source code must retain the above copyright\n *    notice,\
+    \ this list of conditions and the following disclaimer.\n * 2. Redistributions\
+    \ in binary form must reproduce the above copyright\n *    notice, this list of\
+    \ conditions and the following disclaimer in the\n *    documentation and/or other\
+    \ materials provided with the distribution.\n * 3. The name of the author may\
+    \ not be used to endorse or promote products\n *    derived from this software\
+    \ without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED\
+    \ BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING,\
+    \ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS\
+    \ FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE\
+    \ LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\
+    \ DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\
+    \ OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\
+    \ CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\
+    \ OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\
+    \ USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
+    */"
+  licenseId: LicenseRef-1
+- extractedText: "This package includes the GRDDL parser developed by Hewlett Packard\
+    \ under the following license:\n\xA9 Copyright 2007 Hewlett-Packard Development\
+    \ Company, LP\n\nRedistribution and use in source and binary forms, with or without\
+    \ modification, are permitted provided that the following conditions are met:\
+    \ \n\nRedistributions of source code must retain the above copyright notice, this\
+    \ list of conditions and the following disclaimer. \nRedistributions in binary\
+    \ form must reproduce the above copyright notice, this list of conditions and\
+    \ the following disclaimer in the documentation and/or other materials provided\
+    \ with the distribution. \nThe name of the author may not be used to endorse or\
+    \ promote products derived from this software without specific prior written permission.\
+    \ \nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\
+    \ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY\
+    \ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR\
+    \ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\
+    \ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\
+    \ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\
+    \ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\
+    \ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\
+    \ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+  licenseId: LicenseRef-2
+- extractedText: "/*\n * (c) Copyright 2009 University of Bristol\n * All rights reserved.\n\
+    \ *\n * Redistribution and use in source and binary forms, with or without\n *\
+    \ modification, are permitted provided that the following conditions\n * are met:\n\
+    \ * 1. Redistributions of source code must retain the above copyright\n *    notice,\
+    \ this list of conditions and the following disclaimer.\n * 2. Redistributions\
+    \ in binary form must reproduce the above copyright\n *    notice, this list of\
+    \ conditions and the following disclaimer in the\n *    documentation and/or other\
+    \ materials provided with the distribution.\n * 3. The name of the author may\
+    \ not be used to endorse or promote products\n *    derived from this software\
+    \ without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED\
+    \ BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING,\
+    \ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS\
+    \ FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE\
+    \ LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\
+    \ DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\
+    \ OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\
+    \ CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\
+    \ OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\
+    \ USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
+    */"
+  licenseId: LicenseRef-4
+- comment: The beerware license has a couple of other standard variants.
+  extractedText: '"THE BEER-WARE LICENSE" (Revision 42):
+
+    phk@FreeBSD.ORG wrote this file. As long as you retain this notice you
+
+    can do whatever you want with this stuff. If we meet some day, and you think this
+    stuff is worth it, you can buy me a beer in return Poul-Henning Kamp'
+  licenseId: LicenseRef-Beerware-4.2
+  name: Beer-Ware License (Version 42)
+  seeAlsos:
+  - http://people.freebsd.org/~phk/
+- comment: This is tye CyperNeko License
+  extractedText: "The CyberNeko Software License, Version 1.0\n\n \n(C) Copyright\
+    \ 2002-2005, Andy Clark.  All rights reserved.\n \nRedistribution and use in source\
+    \ and binary forms, with or without\nmodification, are permitted provided that\
+    \ the following conditions\nare met:\n\n1. Redistributions of source code must\
+    \ retain the above copyright\n   notice, this list of conditions and the following\
+    \ disclaimer. \n\n2. Redistributions in binary form must reproduce the above copyright\n\
+    \   notice, this list of conditions and the following disclaimer in\n   the documentation\
+    \ and/or other materials provided with the\n   distribution.\n\n3. The end-user\
+    \ documentation included with the redistribution,\n   if any, must include the\
+    \ following acknowledgment:  \n     \"This product includes software developed\
+    \ by Andy Clark.\"\n   Alternately, this acknowledgment may appear in the software\
+    \ itself,\n   if and wherever such third-party acknowledgments normally appear.\n\
+    \n4. The names \"CyberNeko\" and \"NekoHTML\" must not be used to endorse\n  \
+    \ or promote products derived from this software without prior \n   written permission.\
+    \ For written permission, please contact \n   andyc@cyberneko.net.\n\n5. Products\
+    \ derived from this software may not be called \"CyberNeko\",\n   nor may \"CyberNeko\"\
+    \ appear in their name, without prior written\n   permission of the author.\n\n\
+    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\nWARRANTIES,\
+    \ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND\
+    \ FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED.  IN NO EVENT SHALL THE AUTHOR\
+    \ OR OTHER CONTRIBUTORS\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\
+    \ EXEMPLARY, \nOR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\
+    \ \nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR \nBUSINESS\
+    \ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \nWHETHER IN CONTRACT,\
+    \ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE \nOR OTHERWISE) ARISING IN ANY\
+    \ WAY OUT OF THE USE OF THIS SOFTWARE, \nEVEN IF ADVISED OF THE POSSIBILITY OF\
+    \ SUCH DAMAGE."
+  licenseId: LicenseRef-3
+  name: CyberNeko License
+  seeAlsos:
+  - http://people.apache.org/~andyc/neko/LICENSE
+  - http://justasample.url.com
+name: SPDX-Tools-v2.0
+packages:
+- SPDXID: SPDXRef-Package
+  annotations:
+  - annotationDate: '2011-01-29T18:30:22Z'
+    annotationType: OTHER
+    annotator: 'Person: Package Commenter'
+    comment: Package level annotation
+  attributionTexts:
+  - The GNU C Library is free software.  See the file COPYING.LIB for copying conditions,
+    and LICENSES for notices about a few contributions that require these additional
+    notices to be distributed.  License copyright years may be listed using range
+    notation, e.g., 1996-2015, indicating that every year in the range, inclusive,
+    is a copyrightable year that would otherwise be listed individually.
+  builtDate: '2011-01-29T18:30:22Z'
+  checksums:
+  - algorithm: MD5
+    checksumValue: 624c1abb3664f4b35547e7c73864ad24
+  - algorithm: SHA1
+    checksumValue: 85ed0817af83a24ad8da68c2b5094de69833983c
+  - algorithm: SHA256
+    checksumValue: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd
+  - algorithm: BLAKE2b-384
+    checksumValue: aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706
+  copyrightText: Copyright 2008-2010 John Smith
+  description: The GNU C Library defines functions that are specified by the ISO C
+    standard, as well as additional features specific to POSIX and other derivatives
+    of the Unix operating system, and extensions specific to GNU systems.
+  downloadLocation: http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz
+  externalRefs:
+  - referenceCategory: SECURITY
+    referenceLocator: cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*
+    referenceType: cpe23Type
+  - comment: This is the external ref for Acme
+    referenceCategory: OTHER
+    referenceLocator: acmecorp/acmenator/4.1.3-alpha
+    referenceType: http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge
+  filesAnalyzed: true
+  homepage: http://ftp.gnu.org/gnu/glibc
+  licenseComments: The license for this project changed with the release of version
+    x.y.  The version of the project included here post-dates the license change.
+  licenseConcluded:
+  licenseDeclared: LGPL-2.0-only AND LicenseRef-3
+  licenseInfoFromFiles:
+  - GPL-2.0-only
+  - LicenseRef-2
+  - LicenseRef-1
+  name: glibc
+  originator: 'Organization: ExampleCodeInspect (contact@example.com)'
+  packageFileName: glibc-2.11.1.tar.gz
+  packageVerificationCode:
+    packageVerificationCodeExcludedFiles:
+    - ./package.spdx
+    packageVerificationCodeValue: d6a770ba38583ed4bb4525bd96e50461655d2758
+  primaryPackagePurpose: SOURCE
+  releaseDate: '2012-01-29T18:30:22Z'
+  sourceInfo: uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.
+  summary: GNU C library.
+  supplier: 'Organization: ExampleCodeInspect (contact@example.com)'
+  validUntilDate: '2014-01-29T18:30:22Z'
+  versionInfo: 2.11.1
+relationships:
+- relatedSpdxElement: SPDXRef-Package
+  relationshipType: CONTAINS
+  spdxElementId: SPDXRef-DOCUMENT
+- relatedSpdxElement: DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement
+  relationshipType: COPY_OF
+  spdxElementId: SPDXRef-DOCUMENT
+- relatedSpdxElement: NOASSERTION
+  relationshipType: GENERATED_FROM
+  spdxElementId: SPDXRef-CommonsLangSrc
+- relatedSpdxElement: SPDXRef-Package
+  relationshipType: CONTAINS
+  spdxElementId: SPDXRef-JenaLib
+- relatedSpdxElement: SPDXRef-File
+  relationshipType: DESCRIBES
+  spdxElementId: SPDXRef-DOCUMENT
+- relatedSpdxElement: SPDXRef-Package
+  relationshipType: DESCRIBES
+  spdxElementId: SPDXRef-DOCUMENT
+- relatedSpdxElement: SPDXRef-Specification
+  relationshipType: CONTAINS
+  spdxElementId: SPDXRef-Package
+- relatedSpdxElement: SPDXRef-CommonsLangSrc
+  relationshipType: CONTAINS
+  spdxElementId: SPDXRef-Package
+- relatedSpdxElement: SPDXRef-JenaLib
+  relationshipType: CONTAINS
+  spdxElementId: SPDXRef-Package
+- relatedSpdxElement: SPDXRef-DoapSource
+  relationshipType: CONTAINS
+  spdxElementId: SPDXRef-Package
+snippets:
+- SPDXID: SPDXRef-Snippet
+  comment: This snippet was identified as significant and highlighted in this Apache-2.0
+    file, when a commercial scanner identified it as being derived from file foo.c
+    in package xyz which is licensed under GPL-2.0.
+  copyrightText: Copyright 2008-2010 John Smith
+  licenseComments: The concluded license was taken from package xyz, from which the
+    snippet was copied into the current file. The concluded license information was
+    found in the COPYING.txt file in package xyz.
+  licenseConcluded: GPL-2.0-only
+  licenseInfoInSnippets:
+  - GPL-2.0-only
+  name: from linux kernel
+  ranges:
+  - endPointer:
+      offset: 420
+      reference: SPDXRef-DoapSource
+    startPointer:
+      offset: 310
+      reference: SPDXRef-DoapSource
+  - endPointer:
+      lineNumber: 23
+      reference: SPDXRef-DoapSource
+    startPointer:
+      lineNumber: 5
+      reference: SPDXRef-DoapSource
+  snippetFromFile: SPDXRef-DoapSource
+spdxVersion: SPDX-2.3
diff --git a/tests/test_checker.py b/tests/test_checker.py
index a6c81f7..1672047 100644
--- a/tests/test_checker.py
+++ b/tests/test_checker.py
@@ -1,3 +1,7 @@
+# SPDX-FileCopyrightText: 2024 SPDX contributors
+# SPDX-FileType: SOURCE
+# SPDX-License-Identifier: Apache-2.0
+
 """Tests using pytest framework."""
 
 # pylint: disable=missing-function-docstring,import-error,consider-using-from-import
@@ -8,13 +12,17 @@ from unittest import TestCase
 import pytest
 
 import ntia_conformance_checker.sbom_checker as sbom_checker
+from ntia_conformance_checker import FSCT3Checker, NTIAChecker
+
+### Test no element missing
 
 dirname = os.path.join(os.path.dirname(__file__), "data", "no_elements_missing")
 test_files = [os.path.join(dirname, fn) for fn in os.listdir(dirname)]
 
 
 @pytest.mark.parametrize("test_file", test_files)
-def test_sbomchecker_no_errors(test_file):
+def test_sbomchecker_ntia_no_errors(test_file):
+    # No compliance argument is given to SbomChecker; Default is "ntia"
     sbom = sbom_checker.SbomChecker(test_file)
     assert sbom.file == test_file
     assert sbom.doc_version
@@ -25,9 +33,60 @@ def test_sbomchecker_no_errors(test_file):
     assert not sbom.components_without_versions
     assert not sbom.components_without_suppliers
     assert not sbom.components_without_identifiers
+    assert sbom.compliant
     assert sbom.ntia_minimum_elements_compliant
 
 
+@pytest.mark.parametrize("test_file", test_files)
+def test_sbomchecker_fsct3_no_errors(test_file):
+    sbom = sbom_checker.SbomChecker(test_file, compliance="fsct3-min")
+    assert sbom.file == test_file
+    assert sbom.doc_version
+    assert sbom.doc_author
+    assert sbom.doc_timestamp
+    assert sbom.dependency_relationships
+    assert not sbom.components_without_names
+    assert not sbom.components_without_versions
+    assert not sbom.components_without_suppliers
+    assert not sbom.components_without_identifiers
+    assert not sbom.components_without_concluded_licenses
+    assert sbom.compliant
+
+
+@pytest.mark.parametrize("test_file", test_files)
+def test_ntiachecker_no_errors(test_file):
+    sbom = NTIAChecker(test_file)
+    assert sbom.file == test_file
+    assert sbom.doc_version
+    assert sbom.doc_author
+    assert sbom.doc_timestamp
+    assert sbom.dependency_relationships
+    assert not sbom.components_without_names
+    assert not sbom.components_without_versions
+    assert not sbom.components_without_suppliers
+    assert not sbom.components_without_identifiers
+    assert sbom.compliant
+    assert sbom.ntia_minimum_elements_compliant
+
+
+@pytest.mark.parametrize("test_file", test_files)
+def test_fsct3checker_no_errors(test_file):
+    sbom = FSCT3Checker(test_file)
+    assert sbom.file == test_file
+    assert sbom.doc_version
+    assert sbom.doc_author
+    assert sbom.doc_timestamp
+    assert sbom.dependency_relationships
+    assert not sbom.components_without_names
+    assert not sbom.components_without_versions
+    assert not sbom.components_without_suppliers
+    assert not sbom.components_without_identifiers
+    assert not sbom.components_without_concluded_licenses
+    assert sbom.compliant
+
+
+### Test missing author name
+
 dirname = os.path.join(os.path.dirname(__file__), "data", "missing_author_name")
 test_files_missing_author_name = [
     os.path.join(dirname, fn) for fn in os.listdir(dirname)
@@ -44,6 +103,8 @@ def test_sbomchecker_missing_author_name(test_file):
     assert sbom_check.parsing_error
 
 
+### Test missing timestamp
+
 dirname = os.path.join(os.path.dirname(__file__), "data", "missing_timestamp")
 test_files_missing_timestamp = [os.path.join(dirname, fn) for fn in os.listdir(dirname)]
 
@@ -58,6 +119,24 @@ def test_sbomchecker_missing_timestamp(test_file):
     assert sbom_check.parsing_error
 
 
+### Test missing concluded licenses
+
+dirname = os.path.join(os.path.dirname(__file__), "data", "missing_concluded_license")
+test_files_missing_concluded_license = [
+    os.path.join(dirname, fn) for fn in os.listdir(dirname)
+]
+
+
+@pytest.mark.parametrize("test_file", test_files_missing_concluded_license)
+def test_sbomchecker_missing_concluded_license(test_file):
+    sbom_check = FSCT3Checker(test_file)
+
+    assert sbom_check.components_without_concluded_licenses
+    assert not sbom_check.compliant
+
+
+### Test missing dependency relationships
+
 dirname = os.path.join(
     os.path.dirname(__file__), "data", "missing_dependency_relationships"
 )
@@ -78,9 +157,12 @@ def test_sbomchecker_missing_dependency_relationships(test_file):
     assert not sbom.components_without_versions
     assert not sbom.components_without_suppliers
     assert not sbom.components_without_identifiers
+    assert not sbom.compliant
     assert not sbom.ntia_minimum_elements_compliant
 
 
+### Test missing component version
+
 dirname = os.path.join(os.path.dirname(__file__), "data", "missing_component_version")
 test_files_missing_component_version = [
     os.path.join(dirname, fn) for fn in os.listdir(dirname)
@@ -99,9 +181,12 @@ def test_sbomchecker_missing_component_version(test_file):
     TestCase().assertCountEqual(sbom.components_without_versions, ["glibc"])
     assert not sbom.components_without_suppliers
     assert not sbom.components_without_identifiers
+    assert not sbom.compliant
     assert not sbom.ntia_minimum_elements_compliant
 
 
+### Test missing supplier name
+
 dirname = os.path.join(os.path.dirname(__file__), "data", "missing_supplier_name")
 files = [os.path.join(dirname, fn) for fn in os.listdir(dirname)]
 
@@ -120,9 +205,12 @@ def test_sbomchecker_missing_supplier_name(test_file):
         sbom.components_without_suppliers, ["glibc", "Jena", "Saxon"]
     )
     assert not sbom.components_without_identifiers
+    assert not sbom.compliant
     assert not sbom.ntia_minimum_elements_compliant
 
 
+### Test missing unique identifiers
+
 dirname = os.path.join(os.path.dirname(__file__), "data", "missing_unique_identifiers")
 test_files_missing_unique_identifiers = [
     os.path.join(dirname, fn) for fn in os.listdir(dirname)
@@ -135,10 +223,14 @@ def test_sbomchecker_missing_unique_identifiers(test_file):
     the document contains an element without SPDXID."""
     sbom_check = sbom_checker.SbomChecker(test_file)
 
+    assert not sbom_check.compliant
     assert not sbom_check.ntia_minimum_elements_compliant
     assert sbom_check.parsing_error
 
 
+### Test SBOM example from various sources
+
+
 def test_sbomchecker_tern_photon_example():
     """Check that SBOM from Tern for Photon has an author."""
     test_file = os.path.join(
@@ -177,6 +269,7 @@ def test_sbomchecker_chainguard_example():
         "chainguard.spdx.json",
     )
     sbom = sbom_checker.SbomChecker(test_file)
+    assert sbom.compliant
     assert sbom.ntia_minimum_elements_compliant
 
 
@@ -193,6 +286,9 @@ def test_sbomchecker_alpine_no_package_supplier_name_example():
     assert not got["componentSuppliers"]["allProvided"]
 
 
+### Other tests
+
+
 def test_sbomchecker_output_json():
     filepath = os.path.join(
         os.path.dirname(__file__), "data", "other_tests", "SPDXSBOMExample.spdx.yml"
@@ -201,6 +297,7 @@ def test_sbomchecker_output_json():
     got = sbom.output_json()
     assert got["sbomName"] == "xyz-0.1.0"
     assert not got["isNtiaConformant"]
+    assert not got["isConformant"]
     assert got["authorNameProvided"]
     assert got["timestampProvided"]
     assert got["dependencyRelationshipsProvided"]
@@ -263,7 +360,7 @@ def test_components_without_functions():
     assert components == ["glibc-no-supplier"]
     components = sbom.get_components_without_suppliers(return_tuples=True)
     assert components == [("glibc-no-supplier", "SPDXRef-Package4")]
-    # Not sure how to test this. If any package misses the SPDXID the whole file seems to be
-    # invalid.
+    # Not sure how to test this.
+    # If any package misses the SPDXID the whole file seems to be invalid.
     # components = sbom.get_components_without_identifiers()
     # assert components == ["glibc-no-identifier"]