pax_global_header00006660000000000000000000000064146337144000014514gustar00rootroot0000000000000052 comment=9db247b854b9634d0109153d515fd1a9efd5a1b1 tools-golang-0.5.5/000077500000000000000000000000001463371440000141305ustar00rootroot00000000000000tools-golang-0.5.5/.gitattributes000066400000000000000000000000161463371440000170200ustar00rootroot00000000000000* text eol=lf tools-golang-0.5.5/.github/000077500000000000000000000000001463371440000154705ustar00rootroot00000000000000tools-golang-0.5.5/.github/dependabot.yml000066400000000000000000000003151463371440000203170ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: "/" schedule: interval: "weekly" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" tools-golang-0.5.5/.github/workflows/000077500000000000000000000000001463371440000175255ustar00rootroot00000000000000tools-golang-0.5.5/.github/workflows/build.yml000066400000000000000000000015111463371440000213450ustar00rootroot00000000000000# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later name: build on: [push, pull_request] jobs: tests: # this is the only required check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1.18' - name: Run tests run: make test - name: Send coverage report to coveralls uses: shogo82148/actions-goveralls@v1 with: path-to-profile: profile.cov test-platforms: strategy: matrix: go-version: ['stable'] os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Run tests run: make test tools-golang-0.5.5/.gitignore000066400000000000000000000001601463371440000161150ustar00rootroot00000000000000# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later .vscode .idea .DS_Store scratch/* *.swp profile.cov tools-golang-0.5.5/CONTRIBUTING.md000066400000000000000000000054001463371440000163600ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 # Contributing All contributions must include a "Signed-off-by" line in the commit message. This indicates that the contribution is made pursuant to the [Developer Certificate of Origin (DCO)](https://developercertificate.org/), a copy of which is included below. ## Test coverage Since this library is intended to be relied upon by other tools to work with SPDX data, we are aiming to ensure that it is and remains well-tested. PRs with new code should include corresponding test files, and should continue to pass existing tests. Unit tests for `foo.go` should be placed in `foo_test.go`. Test data files and folders should be placed in the top-level `testdata/` folder. To run the test suite, from the top-level directory run: `go test ./...` ## License information New **code files** should include a [short-form SPDX ID](https://spdx.org/ids) at the top, indicating the project license for code, which is Apache-2.0 OR GPL-2.0-or-later. This should look like the following: ``` // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later ``` New **documentation files** should include a [short-form SPDX ID](https://spdx.org/ids) at the top, indicating the project license for documentation, which is CC-BY-4.0. This should look like the following: ``` SPDX-License-Identifier: CC-BY-4.0 ``` ## Developer Certificate of Origin (DCO) ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 1 Letterman Drive Suite D4700 San Francisco, CA, 94129 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` tools-golang-0.5.5/LICENSE.code000066400000000000000000000721251463371440000160550ustar00rootroot00000000000000The tools-golang source code is provided and may be used, at your option, under either: * Apache License, version 2.0 (Apache-2.0), OR * GNU General Public License, version 2.0 or later (GPL-2.0-or-later). Copies of both licenses are included below. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. tools-golang-0.5.5/LICENSE.docs000066400000000000000000000445701463371440000160760ustar00rootroot00000000000000The tools-golang documentation is provided under the Creative Commons Attribution 4.0 International license (CC-BY-4.0), a copy of which is provided below. Attribution 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. tools-golang-0.5.5/MAINTAINERS000066400000000000000000000004021463371440000156210ustar00rootroot00000000000000# spdx/tools-golang project maintainers # # GitHub ID, Name, Affiliation, Email address swinslow, Steve Winslow, NA, steve@swinslow.net RishabhBhatnagar, Rishabh Bhatnagar, media.net, bhatnagarrishabh4@gmail.com lumjjb, Brandon Lum, Google, lumjjb@gmail.com tools-golang-0.5.5/Makefile000066400000000000000000000007211463371440000155700ustar00rootroot00000000000000.PHONY: test test: unit fuzz .PHONY: bootstrap bootstrap: go install github.com/rinchsan/gosimports/cmd/gosimports@v0.3.8 .PHONY: format format: gofmt -w . go mod tidy gosimports -w -local github.com/spdx . .PHONY: unit unit: go test -v -covermode=count -coverprofile=profile.cov ./... .PHONY: fuzz fuzz: go test -v -run=Fuzz -fuzz=FuzzShouldIgnore ./utils -fuzztime=10s go test -v -run=Fuzz -fuzz=FuzzPackageCanGetVerificationCode ./utils -fuzztime=10s tools-golang-0.5.5/README.md000066400000000000000000000065041463371440000154140ustar00rootroot00000000000000[![Build Status](https://github.com/spdx/tools-golang/workflows/build/badge.svg)](https://github.com/spdx/tools-golang/actions) [![Coverage Status](https://coveralls.io/repos/github/spdx/tools-golang/badge.svg)](https://coveralls.io/github/spdx/tools-golang) [![GitHub release](https://img.shields.io/github/release/spdx/tools-golang.svg)](https://github.com/spdx/tools-golang/releases/latest) [![Go Reference](https://pkg.go.dev/badge/github.com/spdx/tools-golang.svg)](https://pkg.go.dev/github.com/spdx/tools-golang) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5710/badge)](https://bestpractices.coreinfrastructure.org/projects/5710) # SPDX tools-golang `tools-golang` is a collection of Go packages intended to make it easier for Go programs to work with [SPDX®](https://spdx.dev/) files. ## Recent news 2022-01-11: **v0.4.0**: added support for SPDX v2.3 and YAML, as well as other improvements and bugfixes. See [RELEASE-NOTES.md](./RELEASE-NOTES.md) for full details. ## What it does tools-golang currently works with files conformant to versions 2.1 and 2.2 of the SPDX specification, available at: https://spdx.dev/specifications tools-golang provides the following packages: * *spdx* - in-memory data model for the sections of an SPDX document * *tagvalue* - tag-value document reader and writer * *rdf* - RDF document reader * *json* - JSON document reader and writer * *yaml* - YAML document reader and writer * *builder* - builds "empty" SPDX document (with hashes) for directory contents * *idsearcher* - searches for [SPDX short-form IDs](https://spdx.org/ids/) and builds an SPDX document * *licensediff* - compares concluded licenses between files in two packages * *reporter* - generates basic license count report from an SPDX document * *spdxlib* - various utility functions for manipulating SPDX documents in memory * *utils* - various utility functions that support the other tools-golang packages Examples for how to use these packages can be found in the `examples/` directory. ## What it doesn't do `tools-golang` doesn't currently support files under any version of the SPDX spec prior to v2.1 ## Documentation SPDX tools-golang documentation is available on the pkg.go.dev website at https://pkg.go.dev/github.com/spdx/tools-golang. ## Contributors Thank you to all of the contributors to spdx/tools-golang. A full list can be found in the GitHub repo and in [the release notes](RELEASE-NOTES.md). In particular, thank you to the following for major contributions: JSON parsing and saving support was added by @specter25 as part of his Google Summer of Code 2021 project. RDF parsing support was added by @RishabhBhatnagar as part of his Google Summer of Code 2020 project. ## Licenses As indicated in `LICENSE-code`, tools-golang **source code files** are provided and may be used, at your option, under *either*: * Apache License, version 2.0 (**Apache-2.0**), **OR** * GNU General Public License, version 2.0 or later (**GPL-2.0-or-later**). As indicated in `LICENSE-docs`, tools-golang **documentation files** are provided and may be used under the Creative Commons Attribution 4.0 International license (**CC-BY-4.0**). This `README.md` file is documentation: `SPDX-License-Identifier: CC-BY-4.0` ## Security For security policy and reporting security issues, please refer to [SECURITY.md](SECURITY.md) tools-golang-0.5.5/RELEASE-NOTES.md000066400000000000000000000055261463371440000164300ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 # Release Notes for spdx/tools-golang ## 0.4.0 0.4.0 released on 2022-01-11 ### New Features and Enhancements * SPDX v2.3 support #164 * YAML support #134 * Add reference types enumerables to SPDX pkg definition #162 #163 * Expand hash algorithm support to include all valid SPDX 2.2 and 2.3 algorithms #173 ### Bug fixes * JSON encoding and decoding not properly handling SPDXRef- prefixes #170 ### Documentation and Cleanup * Overhaul structs, refactor JSON parser and saver #133 * YAML documentation and JSON documentation fixes #141 * Convert SPDX structs to versioned pkgs #146 * Ensure consistency between JSON struct tags across different SPDX versions #174 * Add Security.md for handling of security issues #154 * Update build workflow to go 1.18 #148 ### Contributors * @ianling * @CatalinStratu * @lumjjb * @pxp928 * @kzantow * @puerco * @jedevc ## 0.3.0 0.3.0 released on: 2022-04-03 -rc1 released on: 2022-03-27 ### New Features and Enhancements * Add support for saving SPDX JSON: #92, #94, #97, #98, #104, #106, #113 * Begin OpenSSF Best Practices process and add initial badge: #111 * also enabled branch protection for main branch ### Bug fixes * tvsaver: Fix incorrect tag for Snippet IDs: #95 * GitHub Actions: Fix incorrect branch for code coverage: #112 * builder: Fix file paths to be relative rather than absolute: #114 * builder: Add missing mandatory field LicenseInfoInFile: #119 ### Documentation and Cleanup * Fix link to release notes: #91 * Language fixes for JSON documentation: #108 * Add badges and links for releases and documentation: #109 * Update documentation for release: #121, #122 * Fixes for examples and sample run commands: #123, #125, #126, #127 ### Contributors * @CatalinStratu * @specter25 * @swinslow ## 0.2.0 Released on: 2021-07-04 ### New Features and Enhancements * Add support for parsing SPDX JSON: #72, #75, #83, #84, #87 * bug fixes in interim versions: #77, #78, #79, #80, #81, #82 * Improve handling of multiple hash checksum types: #41, #49, #60 * Enable filtering relationships by various relationship types: #71, #74 * Improve package license visibility: #65, #66 * Rename primary branch to 'main': #69 * Add release notes and push release: #85, #90 ### Bug fixes * Fix multiline (``) wrapping for various fields: #31, #53, #58, #89, #76 * Fix special SPDX IDs in right-hand side of Relationships: #59, #63, #68 * Throw error when parsing tag-value elements without SPDX IDs: #26, #64 * Fix missing colon in 'excludes' for Package Verification Code when saving tag-value documents: #86, #88 * Fix incorrect license statement: #70 ### Contributors * @autarch * @bisakhmondal * @ianling * @matthewkmayer * @RishabhBhatnagar * @specter25 * @swinslow ## 0.1.0 Released on: 2021-03-20 ### Contributors * @abhishekspeer * @goneall * @RishabhBhatnagar * @rtgdk * @swinslow tools-golang-0.5.5/SECURITY.md000066400000000000000000000004041463371440000157170ustar00rootroot00000000000000# Security For security-related issues, do NOT open a public issue. Instead, please send an email to the list of [maintainers](/MAINTAINERS) with the subject header with prefix "spdx/golang-tools SECURITY:", so that we can respond to you in a timely manner. tools-golang-0.5.5/builder/000077500000000000000000000000001463371440000155565ustar00rootroot00000000000000tools-golang-0.5.5/builder/build.go000066400000000000000000000057211463371440000172110ustar00rootroot00000000000000// Package builder is used to create tools-golang data structures for a given // directory path's contents, with hashes, etc. filled in and with empty // license data. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "fmt" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) // Config is a collection of configuration settings for builder. // A few mandatory fields are set here // so that they can be repeatedly reused in multiple calls to Build. type Config struct { // NamespacePrefix should be a URI representing a prefix for the // namespace with which the SPDX Document will be associated. // It will be used in the DocumentNamespace field in the CreationInfo // section, followed by the per-Document package name and a random UUID. NamespacePrefix string // CreatorType should be one of "Person", "Organization" or "Tool". // If not one of those strings, it will be interpreted as "Person". CreatorType string // Creator will be filled in for the given CreatorType. Creator string // PathsIgnored lists certain paths to be omitted from the built document. // Each string should be a path, relative to the package's dirRoot, // to a specific file or (for all files in a directory) ending in a slash. // Prefix the string with "**" to omit all instances of that file / // directory, regardless of where it is in the file tree. PathsIgnored []string // TestValues is used to pass fixed values for testing purposes // only, and should be set to nil for production use. It is only // exported so that it will be accessible within builder. TestValues map[string]string } // Build creates an SPDX Document, returning that document or // error if any is encountered. Arguments: // - packageName: name of package / directory // - dirRoot: path to directory to be analyzed // - config: Config object func Build(packageName string, dirRoot string, config *Config) (*spdx.Document, error) { // build Package section first -- will include Files and make the // package verification code available pkg, err := BuildPackageSection(packageName, dirRoot, config.PathsIgnored) if err != nil { return nil, err } ci, err := BuildCreationInfoSection(config.CreatorType, config.Creator, config.TestValues) if err != nil { return nil, err } rln, err := BuildRelationshipSection(packageName) if err != nil { return nil, err } var packageVerificationCode common.PackageVerificationCode if pkg.PackageVerificationCode != nil { packageVerificationCode = *pkg.PackageVerificationCode } doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), DocumentName: packageName, DocumentNamespace: fmt.Sprintf("%s%s-%s", config.NamespacePrefix, packageName, packageVerificationCode), CreationInfo: ci, Packages: []*spdx.Package{pkg}, Relationships: []*spdx.Relationship{rln}, } return doc, nil } tools-golang-0.5.5/builder/build_creation_info.go000066400000000000000000000025321463371440000221050ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "time" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) // BuildCreationInfoSection creates an SPDX Package, returning that // package or error if any is encountered. Arguments: // - packageName: name of package / directory // - code: verification code from Package // - namespacePrefix: prefix for DocumentNamespace (packageName and code will be added) // - creatorType: one of Person, Organization or Tool // - creator: creator string // - testValues: for testing only; call with nil when using in production func BuildCreationInfoSection(creatorType string, creator string, testValues map[string]string) (*spdx.CreationInfo, error) { // build creator slices creators := []common.Creator{ // add builder as a tool { Creator: "github.com/spdx/tools-golang/builder", CreatorType: "Tool", }, { Creator: creator, CreatorType: creatorType, }, } // use test Created time if passing test values location, _ := time.LoadLocation("UTC") locationTime := time.Now().In(location) created := locationTime.Format("2006-01-02T15:04:05Z") if testVal := testValues["Created"]; testVal != "" { created = testVal } ci := &spdx.CreationInfo{ Creators: creators, Created: created, } return ci, nil } tools-golang-0.5.5/builder/build_creation_info_test.go000066400000000000000000000065751463371440000231570ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "testing" ) // ===== CreationInfo section builder tests ===== func TestBuilderCanBuildCreationInfoSection(t *testing.T) { creatorType := "Organization" creator := "Jane Doe LLC" testValues := make(map[string]string) testValues["Created"] = "2018-10-20T16:48:00Z" ci, err := BuildCreationInfoSection(creatorType, creator, testValues) if err != nil { t.Fatalf("expected nil error, got %v", err) } if ci == nil { t.Fatalf("expected non-nil CreationInfo, got nil") } if len(ci.Creators) != 2 { t.Fatalf("expected %d, got %d", 2, len(ci.Creators)) } if ci.Creators[1].Creator != "Jane Doe LLC" { t.Errorf("expected %s, got %s", "Jane Doe LLC", ci.Creators[0].Creator) } if ci.Creators[0].Creator != "github.com/spdx/tools-golang/builder" { t.Errorf("expected %s, got %s", "github.com/spdx/tools-golang/builder", ci.Creators[1].Creator) } if ci.Created != "2018-10-20T16:48:00Z" { t.Errorf("expected %s, got %s", "2018-10-20T16:48:00Z", ci.Created) } } func TestBuilderCanBuildCreationInfoSectionWithCreatorPerson(t *testing.T) { creatorType := "Person" creator := "John Doe" testValues := make(map[string]string) testValues["Created"] = "2018-10-20T16:48:00Z" ci, err := BuildCreationInfoSection(creatorType, creator, testValues) if err != nil { t.Fatalf("expected nil error, got %v", err) } if ci == nil { t.Fatalf("expected non-nil CreationInfo, got nil") } if len(ci.Creators) != 2 { t.Fatalf("expected %d, got %d", 2, len(ci.Creators)) } if ci.Creators[1].Creator != "John Doe" { t.Errorf("expected %s, got %s", "John Doe", ci.Creators[0].Creator) } if ci.Creators[0].Creator != "github.com/spdx/tools-golang/builder" { t.Errorf("expected %s, got %s", "github.com/spdx/tools-golang/builder", ci.Creators[1].Creator) } } func TestBuilderCanBuildCreationInfoSectionWithCreatorTool(t *testing.T) { creatorType := "Tool" creator := "some-other-tool" testValues := make(map[string]string) testValues["Created"] = "2018-10-20T16:48:00Z" ci, err := BuildCreationInfoSection(creatorType, creator, testValues) if err != nil { t.Fatalf("expected nil error, got %v", err) } if ci == nil { t.Fatalf("expected non-nil CreationInfo, got nil") } if len(ci.Creators) != 2 { t.Fatalf("expected %d, got %d", 2, len(ci.Creators)) } if ci.Creators[0].Creator != "github.com/spdx/tools-golang/builder" { t.Errorf("expected %s, got %s", "github.com/spdx/tools-golang/builder", ci.Creators[0]) } if ci.Creators[1].Creator != "some-other-tool" { t.Errorf("expected %s, got %s", "some-other-tool", ci.Creators[1]) } } func TestBuilderCanBuildCreationInfoSectionWithInvalidPerson(t *testing.T) { creatorType := "Whatever" creator := "John Doe" testValues := make(map[string]string) testValues["Created"] = "2018-10-20T16:48:00Z" ci, err := BuildCreationInfoSection(creatorType, creator, testValues) if err != nil { t.Fatalf("expected nil error, got %v", err) } if ci == nil { t.Fatalf("expected non-nil CreationInfo, got nil") } if len(ci.Creators) != 2 { t.Fatalf("expected %d, got %d", 2, len(ci.Creators)) } if ci.Creators[1].Creator != "John Doe" { t.Errorf("expected %s, got %s", "John Doe", ci.Creators[1]) } if ci.Creators[0].Creator != "github.com/spdx/tools-golang/builder" { t.Errorf("expected %s, got %s", "github.com/spdx/tools-golang/builder", ci.Creators[0]) } } tools-golang-0.5.5/builder/build_file.go000066400000000000000000000025451463371440000202110ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "fmt" "path/filepath" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/utils" ) // BuildFileSection creates an SPDX File, returning that // file or error if any is encountered. Arguments: // - filePath: path to file, relative to prefix // - prefix: relative directory for filePath // - fileNumber: integer index (unique within package) to use in identifier func BuildFileSection(filePath string, prefix string, fileNumber int) (*spdx.File, error) { // build the full file path p := filepath.Join(prefix, filePath) // make sure we can get the file and its hashes ssha1, ssha256, smd5, err := utils.GetHashesForFilePath(p) if err != nil { return nil, err } // build the identifier i := fmt.Sprintf("File%d", fileNumber) // now build the File section f := &spdx.File{ FileName: filePath, FileSPDXIdentifier: common.ElementID(i), Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: ssha1, }, { Algorithm: common.SHA256, Value: ssha256, }, { Algorithm: common.MD5, Value: smd5, }, }, LicenseConcluded: "NOASSERTION", LicenseInfoInFiles: []string{"NOASSERTION"}, FileCopyrightText: "NOASSERTION", } return f, nil } tools-golang-0.5.5/builder/build_file_test.go000066400000000000000000000043211463371440000212420ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" ) // ===== File section builder tests ===== func TestBuilderCanBuildFileSection(t *testing.T) { filePath := "/file1.testdata.txt" prefix := "../testdata/project1/" fileNumber := 17 file1, err := BuildFileSection(filePath, prefix, fileNumber) if err != nil { t.Fatalf("expected nil error, got %v", err) } if file1 == nil { t.Fatalf("expected non-nil file, got nil") } if file1.FileName != "/file1.testdata.txt" { t.Errorf("expected %v, got %v", "/file1.testdata.txt", file1.FileName) } if file1.FileSPDXIdentifier != common.ElementID("File17") { t.Errorf("expected %v, got %v", "File17", file1.FileSPDXIdentifier) } for _, checksum := range file1.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "024f870eb6323f532515f7a09d5646a97083b819" { t.Errorf("expected %v, got %v", "024f870eb6323f532515f7a09d5646a97083b819", checksum.Value) } case common.SHA256: if checksum.Value != "b14e44284ca477b4c0db34b15ca4c454b2947cce7883e22321cf2984050e15bf" { t.Errorf("expected %v, got %v", "b14e44284ca477b4c0db34b15ca4c454b2947cce7883e22321cf2984050e15bf", checksum.Value) } case common.MD5: if checksum.Value != "37c8208479dfe42d2bb29debd6e32d4a" { t.Errorf("expected %v, got %v", "37c8208479dfe42d2bb29debd6e32d4a", checksum.Value) } } } if file1.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file1.LicenseConcluded) } if len(file1.LicenseInfoInFiles) != 1 { t.Errorf("expected %v, got %v", 1, len(file1.LicenseInfoInFiles)) } else { if file1.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file1.LicenseInfoInFiles[0]) } } if file1.FileCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file1.FileCopyrightText) } } func TestBuilderBuildFileSectionFailsForInvalidFilePath(t *testing.T) { filePath := "/file1.testdata.txt" prefix := "oops/wrong/path" fileNumber := 11 _, err := BuildFileSection(filePath, prefix, fileNumber) if err == nil { t.Fatalf("expected non-nil error, got nil") } } tools-golang-0.5.5/builder/build_package.go000066400000000000000000000037111463371440000206610ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "fmt" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/utils" ) // BuildPackageSection creates an SPDX Package, returning // that package or error if any is encountered. Arguments: // - packageName: name of package / directory // - dirRoot: path to directory to be analyzed // - pathsIgnore: slice of strings for filepaths to ignore func BuildPackageSection(packageName string, dirRoot string, pathsIgnore []string) (*spdx.Package, error) { // build the file section first, so we'll have it available // for calculating the package verification code shortPaths, err := utils.GetAllFilePaths(dirRoot, pathsIgnore) if err != nil { return nil, err } files := []*spdx.File{} fileNumber := 0 for _, shortPath := range shortPaths { // SPDX spec says file names should generally start with ./ and the shortPath already starts with / // see: https://spdx.github.io/spdx-spec/v2.3/file-information/#81-file-name-field relativePath := "." + shortPath newFile, err := BuildFileSection(relativePath, dirRoot, fileNumber) if err != nil { return nil, err } files = append(files, newFile) fileNumber++ } // get the verification code code, err := utils.GetVerificationCode(files, "") if err != nil { return nil, err } // now build the package section pkg := &spdx.Package{ PackageName: packageName, PackageSPDXIdentifier: common.ElementID(fmt.Sprintf("Package-%s", packageName)), PackageDownloadLocation: "NOASSERTION", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: &code, PackageLicenseConcluded: "NOASSERTION", PackageLicenseInfoFromFiles: []string{}, PackageLicenseDeclared: "NOASSERTION", PackageCopyrightText: "NOASSERTION", Files: files, } return pkg, nil } tools-golang-0.5.5/builder/build_package_test.go000066400000000000000000000114571463371440000217260ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" ) // ===== Package section builder tests ===== func TestBuilderCanBuildPackageSection(t *testing.T) { packageName := "project1" dirRoot := "../testdata/project1/" wantVerificationCode := common.PackageVerificationCode{Value: "fc9ac4a370af0a471c2e52af66d6b4cf4e2ba12b"} pkg, err := BuildPackageSection(packageName, dirRoot, nil) if err != nil { t.Fatalf("expected nil error, got %v", err) } if pkg == nil { t.Fatalf("expected non-nil Package, got nil") } if pkg.PackageName != "project1" { t.Errorf("expected %v, got %v", "project1", pkg.PackageName) } if pkg.PackageSPDXIdentifier != common.ElementID("Package-project1") { t.Errorf("expected %v, got %v", "Package-project1", pkg.PackageSPDXIdentifier) } if pkg.PackageDownloadLocation != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", pkg.PackageDownloadLocation) } if pkg.FilesAnalyzed != true { t.Errorf("expected %v, got %v", true, pkg.FilesAnalyzed) } if pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("expected %v, got %v", true, pkg.IsFilesAnalyzedTagPresent) } if pkg.PackageVerificationCode.Value != wantVerificationCode.Value { t.Errorf("expected %v, got %v", wantVerificationCode, pkg.PackageVerificationCode) } if pkg.PackageLicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", pkg.PackageLicenseConcluded) } if len(pkg.PackageLicenseInfoFromFiles) != 0 { t.Errorf("expected %v, got %v", 0, len(pkg.PackageLicenseInfoFromFiles)) } if pkg.PackageLicenseDeclared != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", pkg.PackageLicenseDeclared) } if pkg.PackageCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", pkg.PackageCopyrightText) } // and make sure we got the right number of files, and check the first one if pkg.Files == nil { t.Fatalf("expected non-nil pkg.Files, got nil") } if len(pkg.Files) != 5 { t.Fatalf("expected %d, got %d", 5, len(pkg.Files)) } fileEmpty := pkg.Files[0] if fileEmpty == nil { t.Fatalf("expected non-nil file, got nil") } if fileEmpty.FileName != "./emptyfile.testdata.txt" { t.Errorf("expected %v, got %v", "./emptyfile.testdata.txt", fileEmpty.FileName) } if fileEmpty.FileSPDXIdentifier != common.ElementID("File0") { t.Errorf("expected %v, got %v", "File0", fileEmpty.FileSPDXIdentifier) } for _, checksum := range fileEmpty.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "da39a3ee5e6b4b0d3255bfef95601890afd80709" { t.Errorf("expected %v, got %v", "da39a3ee5e6b4b0d3255bfef95601890afd80709", checksum.Value) } case common.SHA256: if checksum.Value != "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" { t.Errorf("expected %v, got %v", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", checksum.Value) } case common.MD5: if checksum.Value != "d41d8cd98f00b204e9800998ecf8427e" { t.Errorf("expected %v, got %v", "d41d8cd98f00b204e9800998ecf8427e", checksum.Value) } } } if fileEmpty.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", fileEmpty.LicenseConcluded) } if len(fileEmpty.LicenseInfoInFiles) != 1 { t.Errorf("expected %v, got %v", 1, len(fileEmpty.LicenseInfoInFiles)) } else { if fileEmpty.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", fileEmpty.LicenseInfoInFiles[0]) } } if fileEmpty.FileCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", fileEmpty.FileCopyrightText) } } func TestBuilderCanIgnoreFiles(t *testing.T) { packageName := "project3" dirRoot := "../testdata/project3/" pathsIgnored := []string{ "**/ignoredir/", "/excludedir/", "**/ignorefile.txt", "/alsoEXCLUDEthis.txt", } pkg, err := BuildPackageSection(packageName, dirRoot, pathsIgnored) if err != nil { t.Fatalf("expected nil error, got %v", err) } // make sure we got the right files if pkg.Files == nil { t.Fatalf("expected non-nil pkg.Files, got nil") } if len(pkg.Files) != 5 { t.Fatalf("expected %d, got %d", 5, len(pkg.Files)) } want := "./dontscan.txt" got := pkg.Files[0].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } want = "./keep/keep.txt" got = pkg.Files[1].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } want = "./keep.txt" got = pkg.Files[2].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } want = "./subdir/keep/dontscan.txt" got = pkg.Files[3].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } want = "./subdir/keep/keep.txt" got = pkg.Files[4].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } } tools-golang-0.5.5/builder/build_relationship.go000066400000000000000000000013231463371440000217640ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "fmt" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) // BuildRelationshipSection creates an SPDX Relationship // solely for the document "DESCRIBES" package relationship, returning that // relationship or error if any is encountered. Arguments: // - packageName: name of package / directory func BuildRelationshipSection(packageName string) (*spdx.Relationship, error) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", fmt.Sprintf("Package-%s", packageName)), Relationship: "DESCRIBES", } return rln, nil } tools-golang-0.5.5/builder/build_relationship_test.go000066400000000000000000000015071463371440000230270ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" ) // ===== Relationship section builder tests ===== func TestBuilderCanBuildRelationshipSection(t *testing.T) { packageName := "project17" rln, err := BuildRelationshipSection(packageName) if err != nil { t.Fatalf("expected nil error, got %v", err) } if rln == nil { t.Fatalf("expected non-nil relationship, got nil") } if rln.RefA != common.MakeDocElementID("", "DOCUMENT") { t.Errorf("expected %v, got %v", "DOCUMENT", rln.RefA) } if rln.RefB != common.MakeDocElementID("", "Package-project17") { t.Errorf("expected %v, got %v", "Package-project17", rln.RefB) } if rln.Relationship != "DESCRIBES" { t.Errorf("expected %v, got %v", "DESCRIBES", rln.Relationship) } } tools-golang-0.5.5/builder/build_test.go000066400000000000000000000341211463371440000202440ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package builder import ( "fmt" "testing" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) func TestBuildCreatesDocument(t *testing.T) { dirRoot := "../testdata/project1/" config := &Config{ NamespacePrefix: "https://github.com/swinslow/spdx-docs/spdx-go/testdata-", CreatorType: "Person", Creator: "John Doe", TestValues: make(map[string]string), } config.TestValues["Created"] = "2018-10-19T04:38:00Z" wantVerificationCode := common.PackageVerificationCode{Value: "fc9ac4a370af0a471c2e52af66d6b4cf4e2ba12b"} doc, err := Build("project1", dirRoot, config) if err != nil { t.Errorf("expected nil error, got %v", err) } if doc == nil { t.Fatalf("expected non-nil Document, got nil") } // check CI section if doc.CreationInfo == nil { t.Fatalf("expected non-nil CreationInfo section, got nil") } if doc.SPDXVersion != spdx.Version { t.Errorf("expected %s, got %s", spdx.Version, doc.SPDXVersion) } if doc.DataLicense != spdx.DataLicense { t.Errorf("expected %s, got %s", spdx.DataLicense, doc.DataLicense) } if doc.SPDXIdentifier != common.ElementID("DOCUMENT") { t.Errorf("expected %s, got %v", "DOCUMENT", doc.SPDXIdentifier) } if doc.DocumentName != "project1" { t.Errorf("expected %s, got %s", "project1", doc.DocumentName) } wantNamespace := fmt.Sprintf("https://github.com/swinslow/spdx-docs/spdx-go/testdata-project1-%s", wantVerificationCode) if doc.DocumentNamespace != wantNamespace { t.Errorf("expected %s, got %s", wantNamespace, doc.DocumentNamespace) } if len(doc.CreationInfo.Creators) != 2 { t.Fatalf("expected %d, got %d", 2, len(doc.CreationInfo.Creators)) } if doc.CreationInfo.Creators[1].Creator != "John Doe" { t.Errorf("expected %s, got %+v", "John Doe", doc.CreationInfo.Creators[1]) } if doc.CreationInfo.Creators[0].Creator != "github.com/spdx/tools-golang/builder" { t.Errorf("expected %s, got %+v", "github.com/spdx/tools-golang/builder", doc.CreationInfo.Creators[0]) } if doc.CreationInfo.Created != "2018-10-19T04:38:00Z" { t.Errorf("expected %s, got %s", "2018-10-19T04:38:00Z", doc.CreationInfo.Created) } // check Package section if doc.Packages == nil { t.Fatalf("expected non-nil doc.Packages, got nil") } if len(doc.Packages) != 1 { t.Fatalf("expected %d, got %d", 1, len(doc.Packages)) } pkg := doc.Packages[0] if pkg == nil { t.Fatalf("expected non-nil pkg, got nil") } if pkg.PackageName != "project1" { t.Errorf("expected %v, got %v", "project1", pkg.PackageName) } if pkg.PackageSPDXIdentifier != common.ElementID("Package-project1") { t.Errorf("expected %v, got %v", "Package-project1", pkg.PackageSPDXIdentifier) } if pkg.PackageDownloadLocation != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", pkg.PackageDownloadLocation) } if pkg.FilesAnalyzed != true { t.Errorf("expected %v, got %v", true, pkg.FilesAnalyzed) } if pkg.PackageVerificationCode.Value != wantVerificationCode.Value { t.Errorf("expected %v, got %v", wantVerificationCode, pkg.PackageVerificationCode) } if pkg.PackageLicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", pkg.PackageLicenseConcluded) } if len(pkg.PackageLicenseInfoFromFiles) != 0 { t.Errorf("expected %v, got %v", 0, len(pkg.PackageLicenseInfoFromFiles)) } if pkg.PackageLicenseDeclared != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", pkg.PackageLicenseDeclared) } if pkg.PackageCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", pkg.PackageCopyrightText) } // check Files section if pkg.Files == nil { t.Fatalf("expected non-nil pkg.Files, got nil") } if len(pkg.Files) != 5 { t.Fatalf("expected %d, got %d", 5, len(pkg.Files)) } // files should be in order of identifier, which is numeric, // created based on alphabetical order of files: // emptyfile, file1, file3, folder/file4, lastfile // check emptyfile.testdata.txt fileEmpty := pkg.Files[0] if fileEmpty == nil { t.Fatalf("expected non-nil file, got nil") } if fileEmpty.FileName != "./emptyfile.testdata.txt" { t.Errorf("expected %v, got %v", "./emptyfile.testdata.txt", fileEmpty.FileName) } if fileEmpty.FileSPDXIdentifier != common.ElementID("File0") { t.Errorf("expected %v, got %v", "File0", fileEmpty.FileSPDXIdentifier) } for _, checksum := range fileEmpty.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "da39a3ee5e6b4b0d3255bfef95601890afd80709" { t.Errorf("expected %v, got %v", "da39a3ee5e6b4b0d3255bfef95601890afd80709", checksum.Value) } case common.SHA256: if checksum.Value != "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" { t.Errorf("expected %v, got %v", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", checksum.Value) } case common.MD5: if checksum.Value != "d41d8cd98f00b204e9800998ecf8427e" { t.Errorf("expected %v, got %v", "d41d8cd98f00b204e9800998ecf8427e", checksum.Value) } } } if fileEmpty.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", fileEmpty.LicenseConcluded) } if len(fileEmpty.LicenseInfoInFiles) != 1 { t.Errorf("expected %v, got %v", 1, len(fileEmpty.LicenseInfoInFiles)) } else { if fileEmpty.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", fileEmpty.LicenseInfoInFiles[0]) } } if fileEmpty.FileCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", fileEmpty.FileCopyrightText) } // check file1.testdata.txt file1 := pkg.Files[1] if file1 == nil { t.Fatalf("expected non-nil file, got nil") } if file1.FileName != "./file1.testdata.txt" { t.Errorf("expected %v, got %v", "./file1.testdata.txt", file1.FileName) } if file1.FileSPDXIdentifier != common.ElementID("File1") { t.Errorf("expected %v, got %v", "File1", file1.FileSPDXIdentifier) } for _, checksum := range file1.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "024f870eb6323f532515f7a09d5646a97083b819" { t.Errorf("expected %v, got %v", "024f870eb6323f532515f7a09d5646a97083b819", checksum.Value) } case common.SHA256: if checksum.Value != "b14e44284ca477b4c0db34b15ca4c454b2947cce7883e22321cf2984050e15bf" { t.Errorf("expected %v, got %v", "b14e44284ca477b4c0db34b15ca4c454b2947cce7883e22321cf2984050e15bf", checksum.Value) } case common.MD5: if checksum.Value != "37c8208479dfe42d2bb29debd6e32d4a" { t.Errorf("expected %v, got %v", "37c8208479dfe42d2bb29debd6e32d4a", checksum.Value) } } } if file1.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file1.LicenseConcluded) } if len(file1.LicenseInfoInFiles) != 1 { t.Errorf("expected %v, got %v", 1, len(file1.LicenseInfoInFiles)) } else { if file1.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file1.LicenseInfoInFiles[0]) } } if file1.FileCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file1.FileCopyrightText) } // check file3.testdata.txt file3 := pkg.Files[2] if file3 == nil { t.Fatalf("expected non-nil file, got nil") } if file3.FileName != "./file3.testdata.txt" { t.Errorf("expected %v, got %v", "./file3.testdata.txt", file3.FileName) } if file3.FileSPDXIdentifier != common.ElementID("File2") { t.Errorf("expected %v, got %v", "File2", file3.FileSPDXIdentifier) } for _, checksum := range file3.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "a46114b70e163614f01c64adf44cdd438f158fce" { t.Errorf("expected %v, got %v", "a46114b70e163614f01c64adf44cdd438f158fce", checksum.Value) } case common.SHA256: if checksum.Value != "9fc181b9892720a15df1a1e561860318db40621bd4040ccdf18e110eb01d04b4" { t.Errorf("expected %v, got %v", "9fc181b9892720a15df1a1e561860318db40621bd4040ccdf18e110eb01d04b4", checksum.Value) } case common.MD5: if checksum.Value != "3e02d3ab9c58eec6911dbba37570934f" { t.Errorf("expected %v, got %v", "3e02d3ab9c58eec6911dbba37570934f", checksum.Value) } } } if file3.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file3.LicenseConcluded) } if len(file3.LicenseInfoInFiles) != 1 { t.Errorf("expected %v, got %v", 1, len(file3.LicenseInfoInFiles)) } else { if file3.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file3.LicenseInfoInFiles[0]) } } if file3.FileCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file3.FileCopyrightText) } // check folder1/file4.testdata.txt file4 := pkg.Files[3] if file4 == nil { t.Fatalf("expected non-nil file, got nil") } if file4.FileName != "./folder1/file4.testdata.txt" { t.Errorf("expected %v, got %v", "./folder1/file4.testdata.txt", file4.FileName) } if file4.FileSPDXIdentifier != common.ElementID("File3") { t.Errorf("expected %v, got %v", "File3", file4.FileSPDXIdentifier) } for _, checksum := range file4.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "e623d7d7d782a7c8323c4d436acee4afab34320f" { t.Errorf("expected %v, got %v", "e623d7d7d782a7c8323c4d436acee4afab34320f", checksum.Value) } case common.SHA256: if checksum.Value != "574fa42c5e0806c0f8906a44884166540206f021527729407cd5326838629c59" { t.Errorf("expected %v, got %v", "574fa42c5e0806c0f8906a44884166540206f021527729407cd5326838629c59", checksum.Value) } case common.MD5: if checksum.Value != "96e6a25d35df5b1c477710ef4d0c7210" { t.Errorf("expected %v, got %v", "96e6a25d35df5b1c477710ef4d0c7210", checksum.Value) } } } if file4.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file4.LicenseConcluded) } if len(file4.LicenseInfoInFiles) != 1 { t.Errorf("expected %v, got %v", 1, len(file4.LicenseInfoInFiles)) } else { if file4.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file4.LicenseInfoInFiles[0]) } } if file4.FileCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", file4.FileCopyrightText) } // check lastfile.testdata.txt lastfile := pkg.Files[4] if lastfile == nil { t.Fatalf("expected non-nil file, got nil") } if lastfile.FileName != "./lastfile.testdata.txt" { t.Errorf("expected %v, got %v", "/lastfile.testdata.txt", lastfile.FileName) } if lastfile.FileSPDXIdentifier != common.ElementID("File4") { t.Errorf("expected %v, got %v", "File4", lastfile.FileSPDXIdentifier) } for _, checksum := range lastfile.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "26d6221d682d9ba59116f9753a701f34271c8ce1" { t.Errorf("expected %v, got %v", "26d6221d682d9ba59116f9753a701f34271c8ce1", checksum.Value) } case common.SHA256: if checksum.Value != "0a4bdaf990e9b330ff72022dd78110ae98b60e08337cf2105b89856373416805" { t.Errorf("expected %v, got %v", "0a4bdaf990e9b330ff72022dd78110ae98b60e08337cf2105b89856373416805", checksum.Value) } case common.MD5: if checksum.Value != "f60baa793870d9085461ad6bbab50b7f" { t.Errorf("expected %v, got %v", "f60baa793870d9085461ad6bbab50b7f", checksum.Value) } } } if lastfile.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", lastfile.LicenseConcluded) } if len(lastfile.LicenseInfoInFiles) != 1 { t.Errorf("expected %v, got %v", 1, len(lastfile.LicenseInfoInFiles)) } else { if lastfile.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", lastfile.LicenseInfoInFiles[0]) } } if lastfile.FileCopyrightText != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", lastfile.FileCopyrightText) } // check Relationship section -- should be a relationship for doc DESCRIBES pkg if doc.Relationships == nil { t.Fatalf("expected non-nil Relationships section, got nil") } if len(doc.Relationships) == 0 { t.Fatalf("expected %v, got %v", 0, len(doc.Relationships)) } rln := doc.Relationships[0] if rln == nil { t.Fatalf("expected non-nil Relationship, got nil") } if rln.RefA != common.MakeDocElementID("", "DOCUMENT") { t.Errorf("expected %v, got %v", "DOCUMENT", rln.RefA) } if rln.RefB != common.MakeDocElementID("", "Package-project1") { t.Errorf("expected %v, got %v", "Package-project1", rln.RefB) } if rln.Relationship != "DESCRIBES" { t.Errorf("expected %v, got %v", "DESCRIBES", rln.Relationship) } // and check that other sections are present, but empty if doc.OtherLicenses != nil { t.Fatalf("expected nil OtherLicenses section, got non-nil") } if doc.Annotations != nil { t.Fatalf("expected nil Annotations section, got non-nil") } if doc.Reviews != nil { t.Fatalf("expected nil Reviews section, got non-nil") } } func TestBuildCanIgnoreFiles(t *testing.T) { dirRoot := "../testdata/project3/" config := &Config{ NamespacePrefix: "https://github.com/swinslow/spdx-docs/spdx-go/testdata-", CreatorType: "Person", Creator: "John Doe", PathsIgnored: []string{ "**/ignoredir/", "/excludedir/", "**/ignorefile.txt", "/alsoEXCLUDEthis.txt", }, TestValues: make(map[string]string), } config.TestValues["Created"] = "2018-10-19T04:38:00Z" doc, err := Build("project1", dirRoot, config) if err != nil { t.Errorf("expected nil error, got %v", err) } pkg := doc.Packages[0] if pkg == nil { t.Fatalf("expected non-nil pkg, got nil") } if len(pkg.Files) != 5 { t.Fatalf("expected len %d, got %d", 5, len(pkg.Files)) } want := "./dontscan.txt" got := pkg.Files[0].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } want = "./keep/keep.txt" got = pkg.Files[1].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } want = "./keep.txt" got = pkg.Files[2].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } want = "./subdir/keep/dontscan.txt" got = pkg.Files[3].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } want = "./subdir/keep/keep.txt" got = pkg.Files[4].FileName if want != got { t.Errorf("expected %v, got %v", want, got) } } tools-golang-0.5.5/convert/000077500000000000000000000000001463371440000156105ustar00rootroot00000000000000tools-golang-0.5.5/convert/chain.go000066400000000000000000000021631463371440000172230ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package convert import ( "fmt" "reflect" converter "github.com/anchore/go-struct-converter" "github.com/spdx/tools-golang/spdx/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" "github.com/spdx/tools-golang/spdx/v2/v2_2" "github.com/spdx/tools-golang/spdx/v2/v2_3" ) var DocumentChain = converter.NewChain( v2_1.Document{}, v2_2.Document{}, v2_3.Document{}, ) // Document converts from one document to another document // For example, converting a document to the latest version could be done like: // // sourceDoc := // e.g. a v2_2.Document from somewhere // var targetDoc spdx.Document // this can be any document version // err := convert.Document(sourceDoc, &targetDoc) // the target must be passed as a pointer func Document(from common.AnyDocument, to common.AnyDocument) error { if !IsPtr(to) { return fmt.Errorf("struct to convert to must be a pointer") } from = FromPtr(from) if reflect.TypeOf(from) == reflect.TypeOf(FromPtr(to)) { reflect.ValueOf(to).Elem().Set(reflect.ValueOf(from)) return nil } return DocumentChain.Convert(from, to) } tools-golang-0.5.5/convert/spdx_document_conversion_test.go000066400000000000000000001747721463371440000243410ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package convert import ( "reflect" "testing" "github.com/stretchr/testify/require" "github.com/spdx/tools-golang/json/marshal" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func Test_ConvertSPDXDocuments(t *testing.T) { tests := []struct { name string source interface{} expected interface{} }{ { name: "basic v2_2 to v2_3", source: v2_2.Document{ SPDXVersion: v2_2.Version, Packages: []*v2_2.Package{ { PackageName: "Pkg 1", Files: []*v2_2.File{ { FileName: "File 1", }, { FileName: "File 2", }, }, PackageVerificationCode: common.PackageVerificationCode{ Value: "verification code value", ExcludedFiles: []string{ "a", "b", }, }, }, }, }, expected: spdx.Document{ SPDXVersion: spdx.Version, Packages: []*spdx.Package{ { PackageName: "Pkg 1", Files: []*spdx.File{ { FileName: "File 1", }, { FileName: "File 2", }, }, PackageVerificationCode: &common.PackageVerificationCode{ Value: "verification code value", ExcludedFiles: []string{ "a", "b", }, }, }, }, }, }, { name: "full 2.1 -> 2.3 document", source: v2_1.Document{ SPDXVersion: "SPDX-2.2", DataLicense: "data license", SPDXIdentifier: "spdx id", DocumentName: "doc name", DocumentNamespace: "doc namespace", ExternalDocumentReferences: []v2_1.ExternalDocumentRef{ { DocumentRefID: "doc ref id 1", URI: "uri 1", Checksum: common.Checksum{ Algorithm: "algo 1", Value: "value 1", }, }, { DocumentRefID: "doc ref id 2", URI: "uri 2", Checksum: common.Checksum{ Algorithm: "algo 2", Value: "value 2", }, }, }, DocumentComment: "doc comment", CreationInfo: &v2_1.CreationInfo{ LicenseListVersion: "license list version", Creators: []common.Creator{ { Creator: "creator 1", CreatorType: "type 1", }, { Creator: "creator 2", CreatorType: "type 2", }, }, Created: "created date", CreatorComment: "creator comment", }, Packages: []*v2_1.Package{ { PackageName: "package name 1", PackageSPDXIdentifier: "id 1", PackageVersion: "version 1", PackageFileName: "file 1", PackageSupplier: nil, PackageOriginator: nil, PackageDownloadLocation: "", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: common.PackageVerificationCode{ Value: "value 1", ExcludedFiles: []string{"a", "b"}, }, PackageChecksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, PackageHomePage: "home page 1", PackageSourceInfo: "source info 1", PackageLicenseConcluded: "license concluded 1", PackageLicenseInfoFromFiles: []string{"a", "b"}, PackageLicenseDeclared: "license declared 1", PackageLicenseComments: "license comments 1", PackageCopyrightText: "copyright text 1", PackageSummary: "summary 1", PackageDescription: "description 1", PackageComment: "comment 1", PackageExternalReferences: []*v2_1.PackageExternalReference{ { Category: "cat 1", RefType: "type 1", Locator: "locator 1", ExternalRefComment: "comment 1", }, { Category: "cat 2", RefType: "type 2", Locator: "locator 2", ExternalRefComment: "comment 2", }, }, Files: []*v2_1.File{ { FileName: "file 1", FileSPDXIdentifier: "id 1", FileTypes: []string{"a", "b"}, Checksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, LicenseConcluded: "license concluded 1", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 1", FileCopyrightText: "copy text 1", ArtifactOfProjects: []*v2_1.ArtifactOfProject{ { Name: "name 1", HomePage: "home 1", URI: "uri 1", }, { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, }, FileComment: "comment 1", FileNotice: "notice 1", FileContributors: []string{"c1", "c2"}, FileDependencies: []string{"dep1", "dep2", "dep3"}, Snippets: map[common.ElementID]*v2_1.Snippet{ common.ElementID("e1"): { SnippetSPDXIdentifier: "id1", SnippetFromFileSPDXIdentifier: "file1", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 1, LineNumber: 2, FileSPDXIdentifier: "f1", }, EndPointer: common.SnippetRangePointer{ Offset: 3, LineNumber: 4, FileSPDXIdentifier: "f2", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", }, common.ElementID("e2"): { SnippetSPDXIdentifier: "id2", SnippetFromFileSPDXIdentifier: "file2", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 9, LineNumber: 10, FileSPDXIdentifier: "f13", }, EndPointer: common.SnippetRangePointer{ Offset: 11, LineNumber: 12, FileSPDXIdentifier: "f14", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", }, }, Annotations: []v2_1.Annotation{ { Annotator: common.Annotator{ Annotator: "ann 1", AnnotatorType: "typ 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "ann 2", AnnotatorType: "typ 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, }, }, Annotations: []v2_1.Annotation{ { Annotator: common.Annotator{ Annotator: "ann 1", AnnotatorType: "typ 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "ann 2", AnnotatorType: "typ 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, }, }, Files: []*v2_1.File{ { FileName: "file 1", FileSPDXIdentifier: "id 1", FileTypes: []string{"t1", "t2"}, Checksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, LicenseConcluded: "concluded 1", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 1", FileCopyrightText: "copy 1", ArtifactOfProjects: []*v2_1.ArtifactOfProject{ { Name: "name 1", HomePage: "home 1", URI: "uri 1", }, { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, }, FileComment: "comment 1", FileNotice: "notice 1", FileContributors: []string{"c1", "c2"}, FileDependencies: []string{"d1", "d2", "d3", "d4"}, Snippets: nil, // already have snippets elsewhere Annotations: nil, // already have annotations elsewhere }, { FileName: "file 2", FileSPDXIdentifier: "id 2", FileTypes: []string{"t3", "t4"}, Checksums: []common.Checksum{ { Algorithm: "alg 2", Value: "val 2", }, { Algorithm: "alg 3", Value: "val 3", }, }, LicenseConcluded: "concluded 2", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 2", FileCopyrightText: "copy 2", ArtifactOfProjects: []*v2_1.ArtifactOfProject{ { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, { Name: "name 4", HomePage: "home 4", URI: "uri 4", }, }, FileComment: "comment 2", FileNotice: "notice 2", FileContributors: []string{"c1", "c2"}, FileDependencies: []string{"d1", "d2", "d3", "d4"}, Snippets: nil, // already have snippets elsewhere Annotations: nil, // already have annotations elsewhere }, }, OtherLicenses: []*v2_1.OtherLicense{ { LicenseIdentifier: "id 1", ExtractedText: "text 1", LicenseName: "name 1", LicenseCrossReferences: []string{"x1", "x2", "x3"}, LicenseComment: "comment 1", }, { LicenseIdentifier: "id 2", ExtractedText: "text 2", LicenseName: "name 2", LicenseCrossReferences: []string{"x4", "x5", "x6"}, LicenseComment: "comment 2", }, }, Relationships: []*v2_1.Relationship{ { RefA: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, RefB: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, Relationship: "type 1", RelationshipComment: "comment 1", }, { RefA: common.DocElementID{ DocumentRefID: "doc 3", ElementRefID: "elem 3", SpecialID: "spec 3", }, RefB: common.DocElementID{ DocumentRefID: "doc 4", ElementRefID: "elem 4", SpecialID: "spec 4", }, Relationship: "type 2", RelationshipComment: "comment 2", }, }, Annotations: []*v2_1.Annotation{ { Annotator: common.Annotator{ Annotator: "annotator 1", AnnotatorType: "annotator type 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "annotator 2", AnnotatorType: "annotator type 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, Snippets: []v2_1.Snippet{ { SnippetSPDXIdentifier: "id1", SnippetFromFileSPDXIdentifier: "file1", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 1, LineNumber: 2, FileSPDXIdentifier: "f1", }, EndPointer: common.SnippetRangePointer{ Offset: 3, LineNumber: 4, FileSPDXIdentifier: "f2", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", }, { SnippetSPDXIdentifier: "id2", SnippetFromFileSPDXIdentifier: "file2", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 9, LineNumber: 10, FileSPDXIdentifier: "f13", }, EndPointer: common.SnippetRangePointer{ Offset: 11, LineNumber: 12, FileSPDXIdentifier: "f14", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", }, }, Reviews: []*v2_1.Review{ { Reviewer: "reviewer 1", ReviewerType: "type 1", ReviewDate: "date 1", ReviewComment: "comment 1", }, { Reviewer: "reviewer 2", ReviewerType: "type 2", ReviewDate: "date 2", ReviewComment: "comment 2", }, }, }, expected: spdx.Document{ SPDXVersion: "SPDX-2.3", // ConvertFrom updates this value DataLicense: "data license", SPDXIdentifier: "spdx id", DocumentName: "doc name", DocumentNamespace: "doc namespace", ExternalDocumentReferences: []spdx.ExternalDocumentRef{ { DocumentRefID: "doc ref id 1", URI: "uri 1", Checksum: common.Checksum{ Algorithm: "algo 1", Value: "value 1", }, }, { DocumentRefID: "doc ref id 2", URI: "uri 2", Checksum: common.Checksum{ Algorithm: "algo 2", Value: "value 2", }, }, }, DocumentComment: "doc comment", CreationInfo: &spdx.CreationInfo{ LicenseListVersion: "license list version", Creators: []common.Creator{ { Creator: "creator 1", CreatorType: "type 1", }, { Creator: "creator 2", CreatorType: "type 2", }, }, Created: "created date", CreatorComment: "creator comment", }, Packages: []*spdx.Package{ { IsUnpackaged: true, PackageName: "package name 1", PackageSPDXIdentifier: "id 1", PackageVersion: "version 1", PackageFileName: "file 1", PackageSupplier: nil, PackageOriginator: nil, PackageDownloadLocation: "", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: &common.PackageVerificationCode{ Value: "value 1", ExcludedFiles: []string{"a", "b"}, }, PackageChecksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, PackageHomePage: "home page 1", PackageSourceInfo: "source info 1", PackageLicenseConcluded: "license concluded 1", PackageLicenseInfoFromFiles: []string{"a", "b"}, PackageLicenseDeclared: "license declared 1", PackageLicenseComments: "license comments 1", PackageCopyrightText: "copyright text 1", PackageSummary: "summary 1", PackageDescription: "description 1", PackageComment: "comment 1", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: "cat 1", RefType: "type 1", Locator: "locator 1", ExternalRefComment: "comment 1", }, { Category: "cat 2", RefType: "type 2", Locator: "locator 2", ExternalRefComment: "comment 2", }, }, Files: []*spdx.File{ { FileName: "file 1", FileSPDXIdentifier: "id 1", FileTypes: []string{"a", "b"}, Checksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, LicenseConcluded: "license concluded 1", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 1", FileCopyrightText: "copy text 1", ArtifactOfProjects: []*spdx.ArtifactOfProject{ { Name: "name 1", HomePage: "home 1", URI: "uri 1", }, { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, }, FileComment: "comment 1", FileNotice: "notice 1", FileContributors: []string{"c1", "c2"}, FileDependencies: []string{"dep1", "dep2", "dep3"}, Snippets: map[common.ElementID]*spdx.Snippet{ common.ElementID("e1"): { SnippetSPDXIdentifier: "id1", SnippetFromFileSPDXIdentifier: "file1", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 1, LineNumber: 2, FileSPDXIdentifier: "f1", }, EndPointer: common.SnippetRangePointer{ Offset: 3, LineNumber: 4, FileSPDXIdentifier: "f2", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", }, common.ElementID("e2"): { SnippetSPDXIdentifier: "id2", SnippetFromFileSPDXIdentifier: "file2", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 9, LineNumber: 10, FileSPDXIdentifier: "f13", }, EndPointer: common.SnippetRangePointer{ Offset: 11, LineNumber: 12, FileSPDXIdentifier: "f14", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", }, }, Annotations: []spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "ann 1", AnnotatorType: "typ 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "ann 2", AnnotatorType: "typ 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, }, }, Annotations: []spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "ann 1", AnnotatorType: "typ 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "ann 2", AnnotatorType: "typ 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, }, }, Files: []*spdx.File{ { FileName: "file 1", FileSPDXIdentifier: "id 1", FileTypes: []string{"t1", "t2"}, Checksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, LicenseConcluded: "concluded 1", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 1", FileCopyrightText: "copy 1", ArtifactOfProjects: []*spdx.ArtifactOfProject{ { Name: "name 1", HomePage: "home 1", URI: "uri 1", }, { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, }, FileComment: "comment 1", FileNotice: "notice 1", FileContributors: []string{"c1", "c2"}, FileDependencies: []string{"d1", "d2", "d3", "d4"}, Snippets: nil, // already have snippets elsewhere Annotations: nil, // already have annotations elsewhere }, { FileName: "file 2", FileSPDXIdentifier: "id 2", FileTypes: []string{"t3", "t4"}, Checksums: []common.Checksum{ { Algorithm: "alg 2", Value: "val 2", }, { Algorithm: "alg 3", Value: "val 3", }, }, LicenseConcluded: "concluded 2", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 2", FileCopyrightText: "copy 2", ArtifactOfProjects: []*spdx.ArtifactOfProject{ { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, { Name: "name 4", HomePage: "home 4", URI: "uri 4", }, }, FileComment: "comment 2", FileNotice: "notice 2", FileContributors: []string{"c1", "c2"}, FileDependencies: []string{"d1", "d2", "d3", "d4"}, Snippets: nil, // already have snippets elsewhere Annotations: nil, // already have annotations elsewhere }, }, OtherLicenses: []*spdx.OtherLicense{ { LicenseIdentifier: "id 1", ExtractedText: "text 1", LicenseName: "name 1", LicenseCrossReferences: []string{"x1", "x2", "x3"}, LicenseComment: "comment 1", }, { LicenseIdentifier: "id 2", ExtractedText: "text 2", LicenseName: "name 2", LicenseCrossReferences: []string{"x4", "x5", "x6"}, LicenseComment: "comment 2", }, }, Relationships: []*spdx.Relationship{ { RefA: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, RefB: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, Relationship: "type 1", RelationshipComment: "comment 1", }, { RefA: common.DocElementID{ DocumentRefID: "doc 3", ElementRefID: "elem 3", SpecialID: "spec 3", }, RefB: common.DocElementID{ DocumentRefID: "doc 4", ElementRefID: "elem 4", SpecialID: "spec 4", }, Relationship: "type 2", RelationshipComment: "comment 2", }, }, Annotations: []*spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "annotator 1", AnnotatorType: "annotator type 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "annotator 2", AnnotatorType: "annotator type 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, Snippets: []spdx.Snippet{ { SnippetSPDXIdentifier: "id1", SnippetFromFileSPDXIdentifier: "file1", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 1, LineNumber: 2, FileSPDXIdentifier: "f1", }, EndPointer: common.SnippetRangePointer{ Offset: 3, LineNumber: 4, FileSPDXIdentifier: "f2", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", }, { SnippetSPDXIdentifier: "id2", SnippetFromFileSPDXIdentifier: "file2", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 9, LineNumber: 10, FileSPDXIdentifier: "f13", }, EndPointer: common.SnippetRangePointer{ Offset: 11, LineNumber: 12, FileSPDXIdentifier: "f14", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", }, }, Reviews: []*spdx.Review{ { Reviewer: "reviewer 1", ReviewerType: "type 1", ReviewDate: "date 1", ReviewComment: "comment 1", }, { Reviewer: "reviewer 2", ReviewerType: "type 2", ReviewDate: "date 2", ReviewComment: "comment 2", }, }, }, }, { name: "full 2.2 -> 2.3 document", source: v2_2.Document{ SPDXVersion: "SPDX-2.2", DataLicense: "data license", SPDXIdentifier: "spdx id", DocumentName: "doc name", DocumentNamespace: "doc namespace", ExternalDocumentReferences: []v2_2.ExternalDocumentRef{ { DocumentRefID: "doc ref id 1", URI: "uri 1", Checksum: common.Checksum{ Algorithm: "algo 1", Value: "value 1", }, }, { DocumentRefID: "doc ref id 2", URI: "uri 2", Checksum: common.Checksum{ Algorithm: "algo 2", Value: "value 2", }, }, }, DocumentComment: "doc comment", CreationInfo: &v2_2.CreationInfo{ LicenseListVersion: "license list version", Creators: []common.Creator{ { Creator: "creator 1", CreatorType: "type 1", }, { Creator: "creator 2", CreatorType: "type 2", }, }, Created: "created date", CreatorComment: "creator comment", }, Packages: []*v2_2.Package{ { IsUnpackaged: true, PackageName: "package name 1", PackageSPDXIdentifier: "id 1", PackageVersion: "version 1", PackageFileName: "file 1", PackageSupplier: nil, PackageOriginator: nil, PackageDownloadLocation: "", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: common.PackageVerificationCode{ Value: "value 1", ExcludedFiles: []string{"a", "b"}, }, PackageChecksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, PackageHomePage: "home page 1", PackageSourceInfo: "source info 1", PackageLicenseConcluded: "license concluded 1", PackageLicenseInfoFromFiles: []string{"a", "b"}, PackageLicenseDeclared: "license declared 1", PackageLicenseComments: "license comments 1", PackageCopyrightText: "copyright text 1", PackageSummary: "summary 1", PackageDescription: "description 1", PackageComment: "comment 1", PackageExternalReferences: []*v2_2.PackageExternalReference{ { Category: "cat 1", RefType: "type 1", Locator: "locator 1", ExternalRefComment: "comment 1", }, { Category: "cat 2", RefType: "type 2", Locator: "locator 2", ExternalRefComment: "comment 2", }, }, PackageAttributionTexts: []string{"a", "b", "c"}, Files: []*v2_2.File{ { FileName: "file 1", FileSPDXIdentifier: "id 1", FileTypes: []string{"a", "b"}, Checksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, LicenseConcluded: "license concluded 1", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 1", FileCopyrightText: "copy text 1", ArtifactOfProjects: []*v2_2.ArtifactOfProject{ { Name: "name 1", HomePage: "home 1", URI: "uri 1", }, { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, }, FileComment: "comment 1", FileNotice: "notice 1", FileContributors: []string{"c1", "c2"}, FileAttributionTexts: []string{"att1", "att2"}, FileDependencies: []string{"dep1", "dep2", "dep3"}, Snippets: map[common.ElementID]*v2_2.Snippet{ common.ElementID("e1"): { SnippetSPDXIdentifier: "id1", SnippetFromFileSPDXIdentifier: "file1", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 1, LineNumber: 2, FileSPDXIdentifier: "f1", }, EndPointer: common.SnippetRangePointer{ Offset: 3, LineNumber: 4, FileSPDXIdentifier: "f2", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", SnippetAttributionTexts: []string{"att1", "att2", "att3"}, }, common.ElementID("e2"): { SnippetSPDXIdentifier: "id2", SnippetFromFileSPDXIdentifier: "file2", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 9, LineNumber: 10, FileSPDXIdentifier: "f13", }, EndPointer: common.SnippetRangePointer{ Offset: 11, LineNumber: 12, FileSPDXIdentifier: "f14", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", SnippetAttributionTexts: []string{"att1", "att2", "att3"}, }, }, Annotations: []v2_2.Annotation{ { Annotator: common.Annotator{ Annotator: "ann 1", AnnotatorType: "typ 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "ann 2", AnnotatorType: "typ 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, }, }, Annotations: []v2_2.Annotation{ { Annotator: common.Annotator{ Annotator: "ann 1", AnnotatorType: "typ 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "ann 2", AnnotatorType: "typ 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, }, }, Files: []*v2_2.File{ { FileName: "file 1", FileSPDXIdentifier: "id 1", FileTypes: []string{"t1", "t2"}, Checksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, LicenseConcluded: "concluded 1", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 1", FileCopyrightText: "copy 1", ArtifactOfProjects: []*v2_2.ArtifactOfProject{ { Name: "name 1", HomePage: "home 1", URI: "uri 1", }, { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, }, FileComment: "comment 1", FileNotice: "notice 1", FileContributors: []string{"c1", "c2"}, FileAttributionTexts: []string{"att1", "att2"}, FileDependencies: []string{"d1", "d2", "d3", "d4"}, Snippets: nil, // already have snippets elsewhere Annotations: nil, // already have annotations elsewhere }, { FileName: "file 2", FileSPDXIdentifier: "id 2", FileTypes: []string{"t3", "t4"}, Checksums: []common.Checksum{ { Algorithm: "alg 2", Value: "val 2", }, { Algorithm: "alg 3", Value: "val 3", }, }, LicenseConcluded: "concluded 2", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 2", FileCopyrightText: "copy 2", ArtifactOfProjects: []*v2_2.ArtifactOfProject{ { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, { Name: "name 4", HomePage: "home 4", URI: "uri 4", }, }, FileComment: "comment 2", FileNotice: "notice 2", FileContributors: []string{"c1", "c2"}, FileAttributionTexts: []string{"att1", "att2"}, FileDependencies: []string{"d1", "d2", "d3", "d4"}, Snippets: nil, // already have snippets elsewhere Annotations: nil, // already have annotations elsewhere }, }, OtherLicenses: []*v2_2.OtherLicense{ { LicenseIdentifier: "id 1", ExtractedText: "text 1", LicenseName: "name 1", LicenseCrossReferences: []string{"x1", "x2", "x3"}, LicenseComment: "comment 1", }, { LicenseIdentifier: "id 2", ExtractedText: "text 2", LicenseName: "name 2", LicenseCrossReferences: []string{"x4", "x5", "x6"}, LicenseComment: "comment 2", }, }, Relationships: []*v2_2.Relationship{ { RefA: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, RefB: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, Relationship: "type 1", RelationshipComment: "comment 1", }, { RefA: common.DocElementID{ DocumentRefID: "doc 3", ElementRefID: "elem 3", SpecialID: "spec 3", }, RefB: common.DocElementID{ DocumentRefID: "doc 4", ElementRefID: "elem 4", SpecialID: "spec 4", }, Relationship: "type 2", RelationshipComment: "comment 2", }, }, Annotations: []*v2_2.Annotation{ { Annotator: common.Annotator{ Annotator: "annotator 1", AnnotatorType: "annotator type 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "annotator 2", AnnotatorType: "annotator type 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, Snippets: []v2_2.Snippet{ { SnippetSPDXIdentifier: "id1", SnippetFromFileSPDXIdentifier: "file1", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 1, LineNumber: 2, FileSPDXIdentifier: "f1", }, EndPointer: common.SnippetRangePointer{ Offset: 3, LineNumber: 4, FileSPDXIdentifier: "f2", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", SnippetAttributionTexts: []string{"att1", "att2", "att3"}, }, { SnippetSPDXIdentifier: "id2", SnippetFromFileSPDXIdentifier: "file2", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 9, LineNumber: 10, FileSPDXIdentifier: "f13", }, EndPointer: common.SnippetRangePointer{ Offset: 11, LineNumber: 12, FileSPDXIdentifier: "f14", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", SnippetAttributionTexts: []string{"att1", "att2", "att3"}, }, }, Reviews: []*v2_2.Review{ { Reviewer: "reviewer 1", ReviewerType: "type 1", ReviewDate: "date 1", ReviewComment: "comment 1", }, { Reviewer: "reviewer 2", ReviewerType: "type 2", ReviewDate: "date 2", ReviewComment: "comment 2", }, }, }, expected: spdx.Document{ SPDXVersion: "SPDX-2.3", // ConvertFrom updates this value DataLicense: "data license", SPDXIdentifier: "spdx id", DocumentName: "doc name", DocumentNamespace: "doc namespace", ExternalDocumentReferences: []spdx.ExternalDocumentRef{ { DocumentRefID: "doc ref id 1", URI: "uri 1", Checksum: common.Checksum{ Algorithm: "algo 1", Value: "value 1", }, }, { DocumentRefID: "doc ref id 2", URI: "uri 2", Checksum: common.Checksum{ Algorithm: "algo 2", Value: "value 2", }, }, }, DocumentComment: "doc comment", CreationInfo: &spdx.CreationInfo{ LicenseListVersion: "license list version", Creators: []common.Creator{ { Creator: "creator 1", CreatorType: "type 1", }, { Creator: "creator 2", CreatorType: "type 2", }, }, Created: "created date", CreatorComment: "creator comment", }, Packages: []*spdx.Package{ { IsUnpackaged: true, PackageName: "package name 1", PackageSPDXIdentifier: "id 1", PackageVersion: "version 1", PackageFileName: "file 1", PackageSupplier: nil, PackageOriginator: nil, PackageDownloadLocation: "", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: &common.PackageVerificationCode{ Value: "value 1", ExcludedFiles: []string{"a", "b"}, }, PackageChecksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, PackageHomePage: "home page 1", PackageSourceInfo: "source info 1", PackageLicenseConcluded: "license concluded 1", PackageLicenseInfoFromFiles: []string{"a", "b"}, PackageLicenseDeclared: "license declared 1", PackageLicenseComments: "license comments 1", PackageCopyrightText: "copyright text 1", PackageSummary: "summary 1", PackageDescription: "description 1", PackageComment: "comment 1", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: "cat 1", RefType: "type 1", Locator: "locator 1", ExternalRefComment: "comment 1", }, { Category: "cat 2", RefType: "type 2", Locator: "locator 2", ExternalRefComment: "comment 2", }, }, PackageAttributionTexts: []string{"a", "b", "c"}, Files: []*spdx.File{ { FileName: "file 1", FileSPDXIdentifier: "id 1", FileTypes: []string{"a", "b"}, Checksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, LicenseConcluded: "license concluded 1", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 1", FileCopyrightText: "copy text 1", ArtifactOfProjects: []*spdx.ArtifactOfProject{ { Name: "name 1", HomePage: "home 1", URI: "uri 1", }, { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, }, FileComment: "comment 1", FileNotice: "notice 1", FileContributors: []string{"c1", "c2"}, FileAttributionTexts: []string{"att1", "att2"}, FileDependencies: []string{"dep1", "dep2", "dep3"}, Snippets: map[common.ElementID]*spdx.Snippet{ common.ElementID("e1"): { SnippetSPDXIdentifier: "id1", SnippetFromFileSPDXIdentifier: "file1", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 1, LineNumber: 2, FileSPDXIdentifier: "f1", }, EndPointer: common.SnippetRangePointer{ Offset: 3, LineNumber: 4, FileSPDXIdentifier: "f2", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", SnippetAttributionTexts: []string{"att1", "att2", "att3"}, }, common.ElementID("e2"): { SnippetSPDXIdentifier: "id2", SnippetFromFileSPDXIdentifier: "file2", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 9, LineNumber: 10, FileSPDXIdentifier: "f13", }, EndPointer: common.SnippetRangePointer{ Offset: 11, LineNumber: 12, FileSPDXIdentifier: "f14", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", SnippetAttributionTexts: []string{"att1", "att2", "att3"}, }, }, Annotations: []spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "ann 1", AnnotatorType: "typ 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "ann 2", AnnotatorType: "typ 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, }, }, Annotations: []spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "ann 1", AnnotatorType: "typ 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "ann 2", AnnotatorType: "typ 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, }, }, Files: []*spdx.File{ { FileName: "file 1", FileSPDXIdentifier: "id 1", FileTypes: []string{"t1", "t2"}, Checksums: []common.Checksum{ { Algorithm: "alg 1", Value: "val 1", }, { Algorithm: "alg 2", Value: "val 2", }, }, LicenseConcluded: "concluded 1", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 1", FileCopyrightText: "copy 1", ArtifactOfProjects: []*spdx.ArtifactOfProject{ { Name: "name 1", HomePage: "home 1", URI: "uri 1", }, { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, }, FileComment: "comment 1", FileNotice: "notice 1", FileContributors: []string{"c1", "c2"}, FileAttributionTexts: []string{"att1", "att2"}, FileDependencies: []string{"d1", "d2", "d3", "d4"}, Snippets: nil, // already have snippets elsewhere Annotations: nil, // already have annotations elsewhere }, { FileName: "file 2", FileSPDXIdentifier: "id 2", FileTypes: []string{"t3", "t4"}, Checksums: []common.Checksum{ { Algorithm: "alg 2", Value: "val 2", }, { Algorithm: "alg 3", Value: "val 3", }, }, LicenseConcluded: "concluded 2", LicenseInfoInFiles: []string{"f1", "f2", "f3"}, LicenseComments: "comments 2", FileCopyrightText: "copy 2", ArtifactOfProjects: []*spdx.ArtifactOfProject{ { Name: "name 2", HomePage: "home 2", URI: "uri 2", }, { Name: "name 4", HomePage: "home 4", URI: "uri 4", }, }, FileComment: "comment 2", FileNotice: "notice 2", FileContributors: []string{"c1", "c2"}, FileAttributionTexts: []string{"att1", "att2"}, FileDependencies: []string{"d1", "d2", "d3", "d4"}, Snippets: nil, // already have snippets elsewhere Annotations: nil, // already have annotations elsewhere }, }, OtherLicenses: []*spdx.OtherLicense{ { LicenseIdentifier: "id 1", ExtractedText: "text 1", LicenseName: "name 1", LicenseCrossReferences: []string{"x1", "x2", "x3"}, LicenseComment: "comment 1", }, { LicenseIdentifier: "id 2", ExtractedText: "text 2", LicenseName: "name 2", LicenseCrossReferences: []string{"x4", "x5", "x6"}, LicenseComment: "comment 2", }, }, Relationships: []*spdx.Relationship{ { RefA: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, RefB: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, Relationship: "type 1", RelationshipComment: "comment 1", }, { RefA: common.DocElementID{ DocumentRefID: "doc 3", ElementRefID: "elem 3", SpecialID: "spec 3", }, RefB: common.DocElementID{ DocumentRefID: "doc 4", ElementRefID: "elem 4", SpecialID: "spec 4", }, Relationship: "type 2", RelationshipComment: "comment 2", }, }, Annotations: []*spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "annotator 1", AnnotatorType: "annotator type 1", }, AnnotationDate: "date 1", AnnotationType: "type 1", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 1", ElementRefID: "elem 1", SpecialID: "spec 1", }, AnnotationComment: "comment 1", }, { Annotator: common.Annotator{ Annotator: "annotator 2", AnnotatorType: "annotator type 2", }, AnnotationDate: "date 2", AnnotationType: "type 2", AnnotationSPDXIdentifier: common.DocElementID{ DocumentRefID: "doc 2", ElementRefID: "elem 2", SpecialID: "spec 2", }, AnnotationComment: "comment 2", }, }, Snippets: []spdx.Snippet{ { SnippetSPDXIdentifier: "id1", SnippetFromFileSPDXIdentifier: "file1", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 1, LineNumber: 2, FileSPDXIdentifier: "f1", }, EndPointer: common.SnippetRangePointer{ Offset: 3, LineNumber: 4, FileSPDXIdentifier: "f2", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", SnippetAttributionTexts: []string{"att1", "att2", "att3"}, }, { SnippetSPDXIdentifier: "id2", SnippetFromFileSPDXIdentifier: "file2", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 5, LineNumber: 6, FileSPDXIdentifier: "f3", }, EndPointer: common.SnippetRangePointer{ Offset: 7, LineNumber: 8, FileSPDXIdentifier: "f4", }, }, { StartPointer: common.SnippetRangePointer{ Offset: 9, LineNumber: 10, FileSPDXIdentifier: "f13", }, EndPointer: common.SnippetRangePointer{ Offset: 11, LineNumber: 12, FileSPDXIdentifier: "f14", }, }, }, SnippetLicenseConcluded: "license 1", LicenseInfoInSnippet: []string{"a", "b"}, SnippetLicenseComments: "license comment 1", SnippetCopyrightText: "copy 1", SnippetComment: "comment 1", SnippetName: "name 1", SnippetAttributionTexts: []string{"att1", "att2", "att3"}, }, }, Reviews: []*spdx.Review{ { Reviewer: "reviewer 1", ReviewerType: "type 1", ReviewDate: "date 1", ReviewComment: "comment 1", }, { Reviewer: "reviewer 2", ReviewerType: "type 2", ReviewDate: "date 2", ReviewComment: "comment 2", }, }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { outType := reflect.TypeOf(test.expected) outInstance := reflect.New(outType).Interface() // convert the start document to the target document using the conversion chain err := Document(test.source, outInstance) if err != nil { t.Fatalf("error converting: %v", err) } outInstance = reflect.ValueOf(outInstance).Elem().Interface() // use JSONEq here because it is much easier to see differences require.JSONEq(t, toJSON(test.expected), toJSON(outInstance)) }) } } func toJSON(data interface{}) string { bytes, err := marshal.JSON(data) if err != nil { panic(err) } return string(bytes) } tools-golang-0.5.5/convert/struct.go000066400000000000000000000021421463371440000174620ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package convert import ( "fmt" "reflect" "github.com/spdx/tools-golang/spdx/common" ) // FromPtr accepts a document or a document pointer and returns the direct struct reference func FromPtr(doc common.AnyDocument) common.AnyDocument { value := reflect.ValueOf(doc) for value.Type().Kind() == reflect.Ptr { value = value.Elem() } return value.Interface() } func IsPtr(obj common.AnyDocument) bool { t := reflect.TypeOf(obj) if t.Kind() == reflect.Interface { t = t.Elem() } return t.Kind() == reflect.Ptr } func Describe(o interface{}) string { value := reflect.ValueOf(o) typ := value.Type() prefix := "" for typ.Kind() == reflect.Ptr { prefix += "*" value = value.Elem() typ = value.Type() } str := limit(fmt.Sprintf("%+v", value.Interface()), 300) name := fmt.Sprintf("%s.%s%s", typ.PkgPath(), prefix, typ.Name()) return fmt.Sprintf("%s: %s", name, str) } func limit(text string, length int) string { if length <= 0 || len(text) <= length+3 { return text } r := []rune(text) r = r[:length] return string(r) + "..." } tools-golang-0.5.5/convert/struct_test.go000066400000000000000000000033671463371440000205330ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package convert import ( "testing" "github.com/stretchr/testify/assert" ) func Test_FromPtr(t *testing.T) { type t1 struct{} tests := []struct { name string input interface{} expected interface{} }{ { name: "struct", input: t1{}, expected: t1{}, }, { name: "ptr", input: &t1{}, expected: t1{}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { out := FromPtr(test.input) assert.Equal(t, test.expected, out) }) } } func Test_Describe(t *testing.T) { type t1 struct { text string } tests := []struct { name string input interface{} expected string }{ { name: "struct", input: t1{ text: "some-text", }, expected: "github.com/spdx/tools-golang/convert.t1: {text:some-text}", }, { name: "ptr", input: &t1{ text: "some-text", }, expected: "github.com/spdx/tools-golang/convert.*t1: {text:some-text}", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { out := Describe(test.input) assert.Equal(t, test.expected, out) }) } } func Test_limit(t *testing.T) { tests := []struct { expected string input string length int }{ { expected: "abc", input: "abc", length: 3, }, { expected: "abc...", input: "abcdefg", length: 3, }, { expected: "abcdef", input: "abcdef", length: 3, }, { expected: "abcd", input: "abcd", length: -1, }, { expected: "", input: "", length: 100, }, } for _, test := range tests { t.Run(test.expected, func(t *testing.T) { out := limit(test.input, test.length) assert.Equal(t, test.expected, out) }) } } tools-golang-0.5.5/docs/000077500000000000000000000000001463371440000150605ustar00rootroot00000000000000tools-golang-0.5.5/docs/builder-assumptions.md000066400000000000000000000003711463371440000214140ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 The Document builder in `package builder` makes the following assumptions: - Symbolic links will be ignored and will not be included in the Document's Files (see https://github.com/swinslow/spdx-go/issues/13). tools-golang-0.5.5/docs/idsearcher-assumptions.md000066400000000000000000000012761463371440000221040ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 The short-form ID searcher in `package idsearcher` makes the following assumptions: - The searcher uses bufio.Scanner to scan the contents of each file, line by line. If it encounters a line that is too large to fit within the buffer, it will stop scanning that file. As a result, it will currently only pick up short-form IDs that occur prior to such a line. - For PackageLicenseInfoFromFiles (in Package) and LicenseInfoInFile (in File), an exception should be treated as a separate "license". For example, in the expression `GPL-2.0-only WITH Classpath-exception-2.0`, each of `GPL-2.0-only` and `Classpath-exception-2.0` will be listed separately. tools-golang-0.5.5/docs/jsonloader.md000066400000000000000000000032161463371440000175440ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 ## Usage A json.Unmarshal function on the v2_2.Document struct is defined so that when the JSON is unmarshalled, the function is called and the JSON can be processed in a custom way. Then a new map[string]interface{} is defined which temporarily holds the unmarshalled JSON. The map is then parsed into the v2_2.Document using functions defined for each different section. JSON => map[string]interface{} => v2_2.Document ## Some Key Points - The packages have a property "hasFiles" defined in the schema which is an array of the SPDX Identifiers of the files of that package. The parser first parses all the files into the UnpackagedFiles map of the document and then when it parses the Packages, it removes the respective files from the UnpackagedFiles map and places them inside the Files map of the corresponding package. - The snippets have a property "snippetFromFile" which has the SPDX identifier of the file to which the snippet is related. Thus the snippets require the files to be parsed before them. Then the snippets are parsed one by one and inserted into the respective files using this property. ## Assumptions The json file loader in `package jsonloader` makes the following assumptions: ### Order of appearance of the properties * The parser does not make any assumptions based on the order in which the properties appear. ### Annotations * The JSON SPDX schema does not define the SPDX Identifier property for the annotation object. The parser assumes the SPDX Identifier of the parent property of the currently-being-parsed annotation array to be the SPDX Identifer for all the annotation objects of that array. tools-golang-0.5.5/docs/jsonsaver.md000066400000000000000000000032771463371440000174250ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 ## Working The SPDX document is converted to map[string]interface{} and then the entire map is converted to JSON using a single json.MarshalIndent function call. The saver uses temporary memory to store all the files (Packaged and Unpackaged) together in a single data structure in order to comply with the JSON schema defined by SPDX. v2_2.Document => map[string]interface{} => JSON ## Some Key Points - The packages have a property "hasFiles" defined in the schema which is an array of the SPDX Identifiers of the files of that package. The saver iterates through the files of a package and inserts all the SPDX Identifiers of the files in the "hasFiles" array. In addition it adds each file to a temporary storage map to store all the files of the entire document at a single place. - The files require the packages to be saved before them in order to ensure that the packaged files are added to the temporary storage before the files are saved. - The snippets are saved after the files and a property "snippetFromFile" identifies the file containing each snippet. ## Assumptions The json file loader in `package jsonsaver` makes the following assumptions: ### Order of appearance of the properties * The saver does not make any pre-assumptions based on the order in which the properties are saved. ### Annotations * The JSON SPDX schema does not define the SPDX Identifier property for the annotation object. The saver inserts the annotation inside the element whose SPDX Identifier matches the annotation's SPDX Identifier. ### Indentation * The jsonsaver uses the MarshalIndent function with "" as the prefix and "\t" as the indent character, passed as function parameters. tools-golang-0.5.5/docs/licensediff-assumptions.md000066400000000000000000000004711463371440000222420ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 The License diff tool in `package licensediff` makes the following assumptions: - In any single Package, a given filename will only appear once. This may or may not be required by the SPDX spec, but it's kind of implicit in being able to create a diff indexed by filename.tools-golang-0.5.5/docs/tvloader-assumptions.md000066400000000000000000000023161463371440000216070ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 The tag-value file loader in `package tvloader` makes the following assumptions: Document Creation Info ---------------------- * The Document Creation Info section will always come first, and be completed first. Although the spec may not make this explicit, it appears that this is the intended format. Unless it comes first, the parser will not be able to confirm what version of the SPDX spec is being used. And, "SPDXID:" tags are used for not just the Document Creation Info section but also for others (e.g. Packages, Files). Relationship ------------ * Relationship sections will begin with the "Relationship" tag. Annotation ---------- * Annotation sections will begin with the "Annotator" tag. Other License Info ------------------ * Other License sections will begin with the "LicenseID" tag. * Any Other License section, if present, will come later than the Document Creation Info section and after any Package, File and Snippet sections. Review ------ * Review sections will begin with the "Reviewer" tag. * Any Review section, if present, will come later than the Document Creation Info section and after any Package, File, Snippet, and Other License sections. tools-golang-0.5.5/docs/tvsaver-assumptions.md000066400000000000000000000010471463371440000214610ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 The tag-value file saver in `package tvsaver` makes the following assumptions: Document Creation Info ---------------------- * Mandatory fields will be treated the same way as optional fields; if they are set to the empty string, they will be omitted. Thus, an invalid Creation Info section (e.g. one that doesn't include a correct SPDXVersion field) will result in outputting an invalid Creation Info section. Relationship ------------ * Same comment as above re: optional fields, for RelationshipComment. tools-golang-0.5.5/examples/000077500000000000000000000000001463371440000157465ustar00rootroot00000000000000tools-golang-0.5.5/examples/1-load/000077500000000000000000000000001463371440000170235ustar00rootroot00000000000000tools-golang-0.5.5/examples/1-load/example_load.go000066400000000000000000000063321463371440000220100ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *tagvalue*, *spdx* // This example demonstrates loading an SPDX tag-value file from disk into // memory, and printing some of its contents to standard output. // Run project: go run example_load.go ../sample-docs/tv/hello.spdx package main import ( "fmt" "os" "github.com/spdx/tools-golang/spdxlib" "github.com/spdx/tools-golang/tagvalue" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 2 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load SPDX tag-value file , and\n") fmt.Printf(" print a portion of its contents.\n") return } // open the SPDX file filename := args[1] r, err := os.Open(filename) if err != nil { fmt.Printf("Error while opening %v for reading: %v", filename, err) return } defer r.Close() // try to load the SPDX file's contents as a tag-value file doc, err := tagvalue.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", filename, err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n\n", filename) // we can now take a look at its contents via the various data // structures representing the SPDX document's sections. // print the struct containing the SPDX file's Creation Info section data fmt.Printf("==============\n") fmt.Printf("Creation info:\n") fmt.Printf("==============\n") fmt.Printf("%#v\n\n", doc.CreationInfo) // check whether the SPDX file has at least one package that it describes pkgIDs, err := spdxlib.GetDescribedPackageIDs(doc) if err != nil { fmt.Printf("Unable to get describe packages from SPDX document: %v\n", err) return } if len(pkgIDs) == 0 { return } // it does, so we'll go through each one for _, pkg := range doc.Packages { var documentDescribesPackage bool for _, describedPackageID := range pkgIDs { if pkg.PackageSPDXIdentifier == describedPackageID { documentDescribesPackage = true break } } if !documentDescribesPackage { continue } pkgID := pkg.PackageSPDXIdentifier // check whether the package had its files analyzed if !pkg.FilesAnalyzed { fmt.Printf("Package %s (%s) had FilesAnalyzed: false\n", string(pkgID), pkg.PackageName) continue } // also check whether the package has any files present if pkg.Files == nil || len(pkg.Files) < 1 { fmt.Printf("Package %s (%s) has no Files\n", string(pkgID), pkg.PackageName) continue } // if we got here, there's at least one file // print the filename and license info for the first 50 fmt.Printf("============================\n") fmt.Printf("Package %s (%s)\n", string(pkgID), pkg.PackageName) fmt.Printf("File info (up to first 50):\n") i := 1 for _, f := range pkg.Files { // note that these will be in random order, since we're pulling // from a map. if we care about order, we should first pull the // IDs into a slice, sort it, and then print the ordered files. fmt.Printf("- File %d: %s\n", i, f.FileName) fmt.Printf(" License from file: %v\n", f.LicenseInfoInFiles) fmt.Printf(" License concluded: %v\n", f.LicenseConcluded) i++ if i > 50 { break } } } } tools-golang-0.5.5/examples/10-jsonloader/000077500000000000000000000000001463371440000203245ustar00rootroot00000000000000tools-golang-0.5.5/examples/10-jsonloader/example_json_loader.go000066400000000000000000000030001463371440000246560ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *json* // This example demonstrates loading an SPDX JSON document from disk into memory, // and then logging some attributes to the console. // Run project: go run example_json_loader.go ../sample-docs/json/SPDXJSONExample-v2.2.spdx.json package main import ( "fmt" "os" "strings" "github.com/spdx/tools-golang/json" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 2 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load SPDX JSON file , and\n") fmt.Printf(" print portions of its creation info data.\n") return } // open the SPDX file fileIn := args[1] r, err := os.Open(fileIn) if err != nil { fmt.Printf("Error while opening %v for reading: %v", fileIn, err) return } defer r.Close() // try to load the SPDX file's contents as a json file doc, err := json.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", args[1], err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n", args[1]) fmt.Println(strings.Repeat("=", 80)) fmt.Println("Some Attributes of the Document:") fmt.Printf("Document Name: %s\n", doc.DocumentName) fmt.Printf("DataLicense: %s\n", doc.DataLicense) fmt.Printf("Document Namespace: %s\n", doc.DocumentNamespace) fmt.Printf("SPDX Version: %s\n", doc.SPDXVersion) fmt.Println(strings.Repeat("=", 80)) } tools-golang-0.5.5/examples/11-yamltotv/000077500000000000000000000000001463371440000200445ustar00rootroot00000000000000tools-golang-0.5.5/examples/11-yamltotv/exampleyamltotv.go000066400000000000000000000032731463371440000236330ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *yaml* *tagvalue* // This example demonstrates loading an SPDX tag-value file from disk into memory, // and re-saving it to a different file on disk. // Run project: go run exampleyamltotv.go ../sample-docs/yaml/SPDXYAMLExample-2.2.spdx.yaml test.spdx package main import ( "fmt" "os" "github.com/spdx/tools-golang/tagvalue" "github.com/spdx/tools-golang/yaml" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 3 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load YAML file , and\n") fmt.Printf(" save it out to .\n") return } // open the SPDX file fileIn := args[1] r, err := os.Open(fileIn) if err != nil { fmt.Printf("Error while opening %v for reading: %v", fileIn, err) return } defer r.Close() // try to load the SPDX file's contents as a YAML file doc, err := yaml.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", fileIn, err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n", fileIn) // we can now save it back to disk, using yaml, but tagvalue work also. // create a new file for writing fileOut := args[2] w, err := os.Create(fileOut) if err != nil { fmt.Printf("Error while opening %v for writing: %v", fileOut, err) return } defer w.Close() // try to save the document to disk as an SPDX tag-value file err = tagvalue.Write(doc, w) if err != nil { fmt.Printf("Error while saving %v: %v", fileOut, err) return } // it worked fmt.Printf("Successfully saved %s\n", fileOut) } tools-golang-0.5.5/examples/12-tvtoyaml/000077500000000000000000000000001463371440000200455ustar00rootroot00000000000000tools-golang-0.5.5/examples/12-tvtoyaml/exampletvtoyaml.go000066400000000000000000000032351463371440000236320ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *tagvalue*, *yaml* // This example demonstrates loading an SPDX tag-value file from disk into memory, // and re-saving it to a different json file on disk. // Run project: go run exampletvtoyaml.go ../sample-docs/tv/hello.spdx example.yaml package main import ( "fmt" "os" "github.com/spdx/tools-golang/tagvalue" "github.com/spdx/tools-golang/yaml" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 3 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load SPDX tag-value file , and\n") fmt.Printf(" save it out to .\n") return } // open the SPDX file fileIn := args[1] r, err := os.Open(fileIn) if err != nil { fmt.Printf("Error while opening %v for reading: %v", fileIn, err) return } defer r.Close() // try to load the SPDX file's contents as a tag-value file doc, err := tagvalue.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", args[1], err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n", args[1]) // we can now save it back to disk, using yaml. // create a new file for writing fileOut := args[2] w, err := os.Create(fileOut) if err != nil { fmt.Printf("Error while opening %v for writing: %v", fileOut, err) return } defer w.Close() // try to save the document to disk as an YAML file err = yaml.Write(doc, w) if err != nil { fmt.Printf("Error while saving %v: %v", fileOut, err) return } // it worked fmt.Printf("Successfully saved %s\n", fileOut) } tools-golang-0.5.5/examples/13-yamlloader/000077500000000000000000000000001463371440000203205ustar00rootroot00000000000000tools-golang-0.5.5/examples/13-yamlloader/exampleYAMLLoader.go000066400000000000000000000027751463371440000241270ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *yaml* // This example demonstrates loading an SPDX YAML document from disk into memory, // and then logging some attributes to the console. // Run project: go run exampleYAMLLoader.go ../sample-docs/yaml/SPDXYAMLExample-2.2.spdx.yaml package main import ( "fmt" "os" "strings" "github.com/spdx/tools-golang/yaml" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 2 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load SPDX JSON file , and\n") fmt.Printf(" print portions of its creation info data.\n") return } // open the SPDX file fileIn := args[1] r, err := os.Open(fileIn) if err != nil { fmt.Printf("Error while opening %v for reading: %v", fileIn, err) return } defer r.Close() // try to load the SPDX file's contents as a json file doc, err := yaml.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", args[1], err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n", args[1]) fmt.Println(strings.Repeat("=", 80)) fmt.Println("Some Attributes of the Document:") fmt.Printf("Document Name: %s\n", doc.DocumentName) fmt.Printf("DataLicense: %s\n", doc.DataLicense) fmt.Printf("Document Namespace: %s\n", doc.DocumentNamespace) fmt.Printf("SPDX Version: %s\n", doc.SPDXVersion) fmt.Println(strings.Repeat("=", 80)) } tools-golang-0.5.5/examples/2-load-save/000077500000000000000000000000001463371440000177605ustar00rootroot00000000000000tools-golang-0.5.5/examples/2-load-save/example_load_save.go000066400000000000000000000032071463371440000237610ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *tagvalue*, *tagvalue* // This example demonstrates loading an SPDX tag-value file from disk into memory, // and re-saving it to a different file on disk. // Run project: go run example_load_save.go ../sample-docs/tv/hello.spdx test.spdx package main import ( "fmt" "os" "github.com/spdx/tools-golang/tagvalue" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 3 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load SPDX tag-value file , and\n") fmt.Printf(" save it out to .\n") return } // open the SPDX file fileIn := args[1] r, err := os.Open(fileIn) if err != nil { fmt.Printf("Error while opening %v for reading: %v", fileIn, err) return } defer r.Close() // try to load the SPDX file's contents as a tag-value file doc, err := tagvalue.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", fileIn, err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n", fileIn) // we can now save it back to disk, using tagvalue. // create a new file for writing fileOut := args[2] w, err := os.Create(fileOut) if err != nil { fmt.Printf("Error while opening %v for writing: %v", fileOut, err) return } defer w.Close() // try to save the document to disk as an SPDX tag-value file err = tagvalue.Write(doc, w) if err != nil { fmt.Printf("Error while saving %v: %v", fileOut, err) return } // it worked fmt.Printf("Successfully saved %s\n", fileOut) } tools-golang-0.5.5/examples/3-build/000077500000000000000000000000001463371440000172055ustar00rootroot00000000000000tools-golang-0.5.5/examples/3-build/example_build.go000066400000000000000000000076251463371440000223600ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *builder*, *tagvalue* // This example demonstrates building an 'empty' SPDX document in memory that // corresponds to a given directory's contents, including all files with their // hashes and the package's verification code, and saving the document to disk. // Run project: go run example_build.go project2 ../../testdata/project2 test.spdx package main import ( "fmt" "os" "github.com/spdx/tools-golang/builder" "github.com/spdx/tools-golang/tagvalue" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 4 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Build a SPDX document with one package called ;\n") fmt.Printf(" create files with hashes corresponding to the files in ;\n") fmt.Printf(" and save it out as a tag-value file to .\n") return } // get the command-line arguments packageName := args[1] packageRootDir := args[2] fileOut := args[3] // to use the SPDX builder package, the first step is to define a // builder.Config struct. this config data can be reused, in case you // are building SPDX documents for several directories in sequence. config := &builder.Config{ // NamespacePrefix is a prefix that will be used to populate the // mandatory DocumentNamespace field in the Creation Info section. // Because it needs to be unique, the value that will be filled in // for the document will have the package name and verification code // appended to this prefix. NamespacePrefix: "https://example.com/whatever/testdata-", // CreatorType will be used for the first part of the Creator field // in the Creation Info section. Per the SPDX spec, it can be // "Person", "Organization" or "Tool". CreatorType: "Person", // Creator will be used for the second part of the Creator field in // the Creation Info section. Creator: "Jane Doe", // note that builder will also add the following, in addition to the // Creator defined above: // Creator: Tool: github.com/spdx/tools-golang/builder // Finally, you can define one or more paths that should be ignored // when walking through the directory. This is intended to omit files // that are located within the package's directory, but which should // be omitted from the SPDX document. PathsIgnored: []string{ // ignore all files in the .git/ directory at the package root "/.git/", // ignore all files in all __pycache__/ directories, anywhere // within the package directory tree "**/__pycache__/", // ignore the file with this specific path relative to the // package root "/.ignorefile", // or ignore all files with this filename, anywhere within the // package directory tree "**/.DS_Store", }, } // now, when we actually ask builder to walk through a directory and // build an SPDX document, we need to give it three things: // - what to name the package; and // - where the directory is located on disk; and // - the config object we just defined. doc, err := builder.Build(packageName, packageRootDir, config) if err != nil { fmt.Printf("Error while building document: %v\n", err) return } // if we got here, the document has been created. // all license info is marked as NOASSERTION, but file hashes and // the package verification code have been filled in appropriately. fmt.Printf("Successfully created document for package %s\n", packageName) // we can now save it to disk, using tagvalue. // create a new file for writing w, err := os.Create(fileOut) if err != nil { fmt.Printf("Error while opening %v for writing: %v\n", fileOut, err) return } defer w.Close() err = tagvalue.Write(doc, w) if err != nil { fmt.Printf("Error while saving %v: %v", fileOut, err) return } fmt.Printf("Successfully saved %v\n", fileOut) } tools-golang-0.5.5/examples/4-search/000077500000000000000000000000001463371440000173545ustar00rootroot00000000000000tools-golang-0.5.5/examples/4-search/example_search.go000066400000000000000000000131701463371440000226650ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *idsearcher*, *tagvalue* // This example demonstrates building an SPDX document for a directory's // contents (implicitly using *builder*); searching through that directory for // [SPDX short-form IDs](https://spdx.org/ids/); filling those IDs into the // document's Package and File license fields; and saving the resulting document // to disk. // Run project: go run example_search.go project2 ../../testdata/project2/folder test.spdx package main import ( "fmt" "os" "github.com/spdx/tools-golang/idsearcher" "github.com/spdx/tools-golang/tagvalue" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 4 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Build a SPDX document with one package called ;\n") fmt.Printf(" create files with hashes corresponding to the files in ;\n") fmt.Printf(" search for SPDX short-form IDs, and use them to fill in license data\n") fmt.Printf(" where possible; and save it out as a tag-value file to .\n") return } // get the command-line arguments packageName := args[1] packageRootDir := args[2] fileOut := args[3] // to use the SPDX idsearcher package, the first step is to define a // idsearcher.Config struct. this config data can be reused, in case you // are building SPDX documents for several directories in sequence. config := &idsearcher.Config{ // NamespacePrefix is a prefix that will be used to populate the // mandatory DocumentNamespace field in the Creation Info section. // Because it needs to be unique, the value that will be filled in // for the document will have the package name and verification code // appended to this prefix. NamespacePrefix: "https://example.com/whatever/testdata-", // CreatorType and Creator, from builder.Config, are not needed for // idsearcher.Config. Because it is automated and doesn't assume // further review, the following two Creator fields are filled in: // Creator: Tool: github.com/spdx/tools-golang/builder // Creator: Tool: github.com/spdx/tools-golang/idsearcher // You can define one or more paths that should be ignored // when walking through the directory. This is intended to omit files // that are located within the package's directory, but which should // be omitted from the SPDX document. // This is directly passed through to builder, and uses the same // format as shown in examples/3-build/example_build.go. BuilderPathsIgnored: []string{ // ignore all files in the .git/ directory at the package root "/.git/", // ignore all files in all __pycache__/ directories, anywhere // within the package directory tree "**/__pycache__/", // ignore the file with this specific path relative to the // package root "/.ignorefile", // or ignore all files with this filename, anywhere within the // package directory tree "**/.DS_Store", }, // Finally, SearcherPathsIgnored lists certain paths that should not be // searched by idsearcher, even if those paths have Files present (and // had files filled in by builder). This is useful, for instance, if // your project has some directories or files with // "SPDX-License-Identifier:" tags, but for one reason or another you // want to exclude those files' tags from being picked up by the // searcher. // SearcherPathsIgnored uses the same format as BuilderPathsIgnored. SearcherPathsIgnored: []string{ // Example for the Linux kernel: ignore the documentation file // which explains how to use SPDX short-form IDs (and therefore // has a bunch of "SPDX-License-Identifier:" tags that we wouldn't // want to pick up). "/Documentation/process/license-rules.rst", // Similar example for the Linux kernel: ignore all files in the // /LICENSES/ directory. "/LICENSES/", }, } // now, when we actually ask idsearcher to walk through a directory and // build an SPDX document, we need to give it three things: // - what to name the package; and // - where the directory is located on disk; and // - the config object we just defined. // these are the same arguments needed for builder, and in fact they get // passed through to builder (with the relevant data from the config // object extracted behind the scenes). doc, err := idsearcher.BuildIDsDocument(packageName, packageRootDir, config) if err != nil { fmt.Printf("Error while building document: %v\n", err) return } // if we got here, the document has been created. // all file hashes and the package verification code have been filled in // appropriately by builder. // And, all files with "SPDX-License-Identifier:" tags have had their // licenses extracted into LicenseInfoInFiles and LicenseConcluded for // each file by idsearcher. The PackageLicenseInfoFromFiles field will // also be filled in with all license identifiers. fmt.Printf("Successfully created document and searched for IDs for package %s\n", packageName) // NOTE that BuildIDsDocument does NOT do any validation of the license // identifiers, to confirm that they are e.g. on the SPDX License List // or in other appropriate format (e.g., LicenseRef-...) // we can now save it to disk, using tagvalue. // create a new file for writing w, err := os.Create(fileOut) if err != nil { fmt.Printf("Error while opening %v for writing: %v\n", fileOut, err) return } defer w.Close() err = tagvalue.Write(doc, w) if err != nil { fmt.Printf("Error while saving %v: %v", fileOut, err) return } fmt.Printf("Successfully saved %v\n", fileOut) } tools-golang-0.5.5/examples/5-report/000077500000000000000000000000001463371440000174235ustar00rootroot00000000000000tools-golang-0.5.5/examples/5-report/example_report.go000066400000000000000000000053111463371440000230000ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *reporter*, *tagvalue* // This example demonstrates loading an SPDX tag-value file from disk into memory, // generating a basic report listing counts of the concluded licenses for its // files, and printing the report to standard output. // Run project: go run example_report.go ../sample-docs/tv/hello.spdx package main import ( "fmt" "os" "github.com/spdx/tools-golang/reporter" "github.com/spdx/tools-golang/spdxlib" "github.com/spdx/tools-golang/tagvalue" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 2 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load SPDX tag-value file , and\n") fmt.Printf(" generate and print a report of its concluded licenses.\n") return } // open the SPDX file filename := args[1] r, err := os.Open(filename) if err != nil { fmt.Printf("Error while opening %v for reading: %v", filename, err) return } defer r.Close() // try to load the SPDX file's contents as a tag-value file doc, err := tagvalue.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", filename, err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n\n", filename) // check whether the SPDX file has at least one package that it describes pkgIDs, err := spdxlib.GetDescribedPackageIDs(doc) if err != nil { fmt.Printf("Unable to get describe packages from SPDX document: %v\n", err) return } if len(pkgIDs) == 0 { return } // it does, so we'll go through each one for _, pkg := range doc.Packages { var documentDescribesPackage bool for _, describedPackageID := range pkgIDs { if pkg.PackageSPDXIdentifier == describedPackageID { documentDescribesPackage = true break } } if !documentDescribesPackage { continue } pkgID := pkg.PackageSPDXIdentifier // check whether the package had its files analyzed if !pkg.FilesAnalyzed { fmt.Printf("Package %s (%s) had FilesAnalyzed: false\n", string(pkgID), pkg.PackageName) return } // also check whether the package has any files present if pkg.Files == nil || len(pkg.Files) < 1 { fmt.Printf("Package %s (%s) has no Files\n", string(pkgID), pkg.PackageName) return } // if we got here, there's at least one file // generate and print a report of the Package's Files' LicenseConcluded // values, sorted by # of occurrences fmt.Printf("============================\n") fmt.Printf("Package %s (%s)\n", string(pkgID), pkg.PackageName) err = reporter.Generate(pkg, os.Stdout) if err != nil { fmt.Printf("Error while generating report: %v\n", err) } } } tools-golang-0.5.5/examples/6-licensediff/000077500000000000000000000000001463371440000203645ustar00rootroot00000000000000tools-golang-0.5.5/examples/6-licensediff/example_licensediff.go000066400000000000000000000122251463371440000247030ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *licensediff*, *tagvalue* // This example demonstrates loading two SPDX tag-value files from disk into // memory, and generating a diff of the concluded licenses for Files in // Packages with matching IDs in each document. // This is generally only useful when run with two SPDX documents that // describe licenses for subsequent versions of the same set of files, AND if // they have the same identifier in both documents. // Run project: go run example_licensediff.go ../sample-docs/tv/hello.spdx ../sample-docs/tv/hello-modified.spdx package main import ( "fmt" "os" "github.com/spdx/tools-golang/licensediff" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdxlib" "github.com/spdx/tools-golang/tagvalue" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 3 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load SPDX tag-value files and ,\n") fmt.Printf(" run a diff between their concluded licenses, and print basic results.\n") return } // open the first SPDX file filenameFirst := args[1] r, err := os.Open(filenameFirst) if err != nil { fmt.Printf("Error while opening %v for reading: %v", filenameFirst, err) return } defer r.Close() // try to load the first SPDX file's contents as a tag-value file docFirst, err := tagvalue.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", filenameFirst, err) return } // check whether the SPDX file has at least one package that it describes pkgIDsFirst, err := spdxlib.GetDescribedPackageIDs(docFirst) if err != nil { fmt.Printf("Unable to get describe packages from first SPDX document: %v\n", err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded first SPDX file %s\n", filenameFirst) // open the second SPDX file filenameSecond := args[2] r, err = os.Open(filenameSecond) if err != nil { fmt.Printf("Error while opening %v for reading: %v", filenameSecond, err) return } defer r.Close() // try to load the second SPDX file's contents as a tag-value file docSecond, err := tagvalue.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", filenameSecond, err) return } // check whether the SPDX file has at least one package that it describes pkgIDsSecond, err := spdxlib.GetDescribedPackageIDs(docSecond) if err != nil { fmt.Printf("Unable to get describe packages from second SPDX document: %v\n", err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded second SPDX file %s\n\n", filenameSecond) // compare the described packages from each Document, by SPDX ID // go through the first set first, report if they aren't in the second set for _, pkgID := range pkgIDsFirst { fmt.Printf("================================\n") var p1, p2 *spdx.Package var okFirst, okSecond bool for _, pkg := range docFirst.Packages { if pkg.PackageSPDXIdentifier == pkgID { okFirst = true p1 = pkg break } } if !okFirst { fmt.Printf("Package %s has described relationship in first document but ID not found\n", string(pkgID)) continue } fmt.Printf("Package %s (%s)\n", string(pkgID), p1.PackageName) for _, pkg := range docSecond.Packages { if pkg.PackageSPDXIdentifier == pkgID { okSecond = true p2 = pkg break } } if !okSecond { fmt.Printf(" Found in first document, not found in second\n") continue } // now, run a diff between the two pairs, err := licensediff.MakePairs(p1, p2) if err != nil { fmt.Printf(" Error generating licensediff pairs: %v\n", err) continue } // take the pairs and turn them into a more structured results set resultSet, err := licensediff.MakeResults(pairs) if err != nil { fmt.Printf(" Error generating licensediff results set: %v\n", err) continue } // print some information about the results fmt.Printf(" Files in first only: %d\n", len(resultSet.InFirstOnly)) fmt.Printf(" Files in second only: %d\n", len(resultSet.InSecondOnly)) fmt.Printf(" Files in both with different licenses: %d\n", len(resultSet.InBothChanged)) fmt.Printf(" Files in both with same licenses: %d\n", len(resultSet.InBothSame)) } // now report if there are any package IDs in the second set that aren't // in the first for _, pkgID := range pkgIDsSecond { var p2 *spdx.Package var okFirst, okSecond bool for _, pkg := range docSecond.Packages { if pkg.PackageSPDXIdentifier == pkgID { okSecond = true p2 = pkg break } } if !okSecond { fmt.Printf("================================\n") fmt.Printf("Package %s has described relationship in second document but ID not found\n", string(pkgID)) continue } for _, pkg := range docFirst.Packages { if pkg.PackageSPDXIdentifier == pkgID { okFirst = true break } } if !okFirst { fmt.Printf("================================\n") fmt.Printf("Package %s (%s)\n", string(pkgID), p2.PackageName) fmt.Printf(" Found in second document, not found in first\n") } } } tools-golang-0.5.5/examples/7-rdfloader/000077500000000000000000000000001463371440000200545ustar00rootroot00000000000000tools-golang-0.5.5/examples/7-rdfloader/exampleRDFLoader.go000066400000000000000000000025221463371440000235220ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Run project: go run exampleRDFLoader.go ../sample-docs/rdf/SPDXRdfExample-v2.2.spdx.rdf package main import ( "fmt" "os" "strings" "github.com/spdx/tools-golang/rdf" ) func getFilePathFromUser() (string, error) { if len(os.Args) == 1 { // user hasn't specified the rdf file path return "", fmt.Errorf("kindly provide path of the rdf file to be loaded as a spdx-document while running this file") } return os.Args[1], nil } func main() { // example to use the rdfLoader. filePath, ok := getFilePathFromUser() if ok != nil { fmt.Println(fmt.Errorf("%v", ok)) os.Exit(1) } file, err := os.Open(filePath) if err != nil { fmt.Println(fmt.Errorf("error opening File: %s", err)) os.Exit(1) } // loading the spdx-document doc, err := rdf.Read(file) if err != nil { fmt.Println(fmt.Errorf("error parsing given spdx document: %s", err)) os.Exit(1) } // Printing some of the document Information fmt.Println(strings.Repeat("=", 80)) fmt.Println("Some Attributes of the Document:") fmt.Printf("Document Name: %s\n", doc.DocumentName) fmt.Printf("DataLicense: %s\n", doc.DataLicense) fmt.Printf("Document Namespace: %s\n", doc.DocumentNamespace) fmt.Printf("SPDX Version: %s\n", doc.SPDXVersion) fmt.Println(strings.Repeat("=", 80)) } tools-golang-0.5.5/examples/8-jsontotv/000077500000000000000000000000001463371440000200015ustar00rootroot00000000000000tools-golang-0.5.5/examples/8-jsontotv/examplejsontotv.go000066400000000000000000000032751463371440000236010ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *json*, *tagvalue* // This example demonstrates loading an SPDX json from disk into memory, // and then re-saving it to a different file on disk in tag-value format . // Run project: go run examplejsontotv.go ../sample-docs/json/SPDXJSONExample-v2.2.spdx.json example.spdx package main import ( "fmt" "os" "github.com/spdx/tools-golang/json" "github.com/spdx/tools-golang/tagvalue" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 3 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load JSON file , and\n") fmt.Printf(" save it out to .\n") return } // open the SPDX file fileIn := args[1] r, err := os.Open(fileIn) if err != nil { fmt.Printf("Error while opening %v for reading: %v", fileIn, err) return } defer r.Close() // try to load the SPDX file's contents as a json file doc, err := json.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", args[1], err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n", args[1]) // we can now save it back to disk, using tagvalue. // create a new file for writing fileOut := args[2] w, err := os.Create(fileOut) if err != nil { fmt.Printf("Error while opening %v for writing: %v", fileOut, err) return } defer w.Close() // try to save the document to disk as an SPDX tag-value file err = tagvalue.Write(doc, w) if err != nil { fmt.Printf("Error while saving %v: %v", fileOut, err) return } // it worked fmt.Printf("Successfully saved %s\n", fileOut) } tools-golang-0.5.5/examples/9-tvtojson/000077500000000000000000000000001463371440000200025ustar00rootroot00000000000000tools-golang-0.5.5/examples/9-tvtojson/exampletvtojson.go000066400000000000000000000036761463371440000236070ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Example for: *tagvalue*, *json* // This example demonstrates loading an SPDX tag-value file from disk into memory, // and re-saving it to a different json file on disk. // Run project: go run exampletvtojson.go ../sample-docs/tv/hello.spdx example.json package main import ( "fmt" "os" "github.com/spdx/tools-golang/json" "github.com/spdx/tools-golang/tagvalue" ) func main() { // check that we've received the right number of arguments args := os.Args if len(args) != 3 { fmt.Printf("Usage: %v \n", args[0]) fmt.Printf(" Load SPDX tag-value file , and\n") fmt.Printf(" save it out to JSON .\n") return } // open the SPDX file fileIn := args[1] r, err := os.Open(fileIn) if err != nil { fmt.Printf("Error while opening %v for reading: %v", fileIn, err) return } defer r.Close() // try to load the SPDX file's contents as a tag-value file doc, err := tagvalue.Read(r) if err != nil { fmt.Printf("Error while parsing %v: %v", args[1], err) return } // if we got here, the file is now loaded into memory. fmt.Printf("Successfully loaded %s\n", args[1]) // we can now save it back to disk, using jsonsaver. // create a new file for writing fileOut := args[2] w, err := os.Create(fileOut) if err != nil { fmt.Printf("Error while opening %v for writing: %v", fileOut, err) return } defer w.Close() var opt []json.WriteOption // you can use WriteOption to change JSON format // uncomment the following code to test it /* opt = append(opt, json.Indent(" ")) // to create multiline json opt = append(opt, json.EscapeHTML(true)) // to escape HTML characters */ // try to save the document to disk as JSON file err = json.Write(doc, w, opt...) if err != nil { fmt.Printf("Error while saving %v: %v", fileOut, err) return } // it worked fmt.Printf("Successfully saved %s\n", fileOut) } tools-golang-0.5.5/examples/README.md000066400000000000000000000102421463371440000172240ustar00rootroot00000000000000SPDX-License-Identifier: CC-BY-4.0 # tools-golang Examples The `examples/` directory contains examples for how to use the various tools-golang sub-packages. Sample commands below should be run from within the example's subdirectory. ## 1-load/ *tagvalue*, *spdx* This example demonstrates loading an SPDX tag-value file from disk into memory, and printing some of its contents to standard output. #### Run project: *go run example_load.go ../sample-docs/tv/hello.spdx* ## 2-load-save/ *tagvalue*, *tagvalue* This example demonstrates loading an SPDX tag-value file from disk into memory, and re-saving it to a different file on disk. #### Run project: *go run example_load_save.go ../sample-docs/tv/hello.spdx test.spdx* ## 3-build/ *builder*, *tagvalue* This example demonstrates building an 'empty' SPDX document in memory that corresponds to a given directory's contents, including all files with their hashes and the package's verification code, and saving the document to disk. #### Run project: *go run example_build.go project2 ../../testdata/project2 test.spdx* ## 4-search/ *idsearcher*, *tagvalue* This example demonstrates building an SPDX document for a directory's contents (implicitly using *builder*); searching through that directory for [SPDX short-form IDs](https://spdx.org/ids/); filling those IDs into the document's Package and File license fields; and saving the resulting document to disk. #### Run project: *go run example_search.go project2 ../../testdata/project2/folder test.spdx* ## 5-report/ *reporter*, *tagvalue* This example demonstrates loading an SPDX tag-value file from disk into memory, generating a basic report listing counts of the concluded licenses for its files, and printing the report to standard output. #### Run project: *go run example_report.go ../sample-docs/tv/hello.spdx* ## 6-licensediff *licensediff*, *tagvalue* This example demonstrates loading two SPDX tag-value files from disk into memory, and generating a diff of the concluded licenses for Files in Packages with matching IDs in each document. This is generally only useful when run with two SPDX documents that describe licenses for subsequent versions of the same set of files, AND if they have the same identifier in both documents. #### Run project: *go run example_licensediff.go ../sample-docs/tv/hello.spdx ../sample-docs/tv/hello-modified.spdx* ## 7-rdfloader *rdfloader*, *spdx* This example demonstrates loading an SPDX rdf file from disk into memory and then printing the corresponding spdx struct for the document. #### Run project: *go run exampleRDFLoader.go ../sample-docs/rdf/SPDXRdfExample-v2.2.spdx.rdf* ## 8-jsontotv *json*, *tagvalue* This example demonstrates loading an SPDX json from disk into memory and then re-saving it to a different file on disk in tag-value format. #### Run project: *go run examplejsontotv.go ../sample-docs/json/SPDXJSONExample-v2.2.spdx.json example.spdx* ## 9-tvtojson *json*, *tagvalue* This example demonstrates loading an SPDX tag-value from disk into memory and then re-saving it to a different file on disk in json format. #### Run project: *go run exampletvtojson.go ../sample-docs/tv/hello.spdx example.json* ## 10-jsonloader *json* This example demonstrates loading an SPDX json from disk into memory and then logging some of the attributes to the console. #### Run project: *go run example_json_loader.go ../sample-docs/json/SPDXJSONExample-v2.2.spdx.json* ## 11-yamltotv *yaml* *tagvalue* This example demonstrates loading an SPDX yaml from disk into memory and then re-saving it to a different file on disk in tag-value format. #### Run project: *go run exampleyamltotv.go ../sample-docs/yaml/SPDXYAMLExample-2.2.spdx.yaml test.spdx* ## 12-tvtoyaml *yaml* *tagvalue* This example demonstrates loading an SPDX tag-value from disk into memory and then re-saving it to a different file on disk in yaml format. #### Run project: *go run exampletvtoyaml.go ../sample-docs/tv/hello.spdx example.yaml* ## 13-yamlloader *yaml* This example demonstrates loading an SPDX yaml from disk into memory and then logging some of the attributes to the console. #### Run project: *go run exampleYAMLLoader.go ../sample-docs/yaml/SPDXYAMLExample-2.2.spdx.yaml* tools-golang-0.5.5/examples/sample-docs/000077500000000000000000000000001463371440000201555ustar00rootroot00000000000000tools-golang-0.5.5/examples/sample-docs/json/000077500000000000000000000000001463371440000211265ustar00rootroot00000000000000tools-golang-0.5.5/examples/sample-docs/json/SPDXJSONExample-v2.2.spdx.json000066400000000000000000000504501463371440000262530ustar00rootroot00000000000000{ "SPDXID" : "SPDXRef-DOCUMENT", "spdxVersion" : "SPDX-2.2", "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.9" }, "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." } ], "documentNamespace" : "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", "documentDescribes" : [ "SPDXRef-File", "SPDXRef-Package" ], "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." ], "checksums" : [ { "algorithm" : "MD5", "checksumValue" : "624c1abb3664f4b35547e7c73864ad24" }, { "algorithm" : "SHA1", "checksumValue" : "85ed0817af83a24ad8da68c2b5094de69833983c" }, { "algorithm" : "SHA256", "checksumValue" : "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" } ], "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, "hasFiles" : [ "SPDXRef-CommonsLangSrc", "SPDXRef-JenaLib", "SPDXRef-DoapSource" ], "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" : "(LGPL-2.0-only OR LicenseRef-3)", "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" }, "sourceInfo" : "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.", "summary" : "GNU C library.", "supplier" : "Person: Jane Doe (jane.doe@example.com)", "versionInfo" : "2.11.1" }, { "SPDXID" : "SPDXRef-fromDoap-1", "copyrightText" : "NOASSERTION", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "homepage" : "http://commons.apache.org/proper/commons-lang/", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "NOASSERTION", "name" : "Apache Commons Lang" }, { "SPDXID" : "SPDXRef-fromDoap-0", "copyrightText" : "NOASSERTION", "downloadLocation" : "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz", "externalRefs" : [ { "referenceCategory" : "PACKAGE_MANAGER", "referenceLocator" : "pkg:maven/org.apache.jena/apache-jena@3.12.0", "referenceType" : "purl" } ], "homepage" : "http://www.openjena.org/", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "NOASSERTION", "name" : "Jena", "versionInfo" : "3.12.0" }, { "SPDXID" : "SPDXRef-Saxon", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "85ed0817af83a24ad8da68c2b5094de69833983c" } ], "copyrightText" : "Copyright Saxonica Ltd", "description" : "The Saxon package is a collection of tools for processing XML documents.", "downloadLocation" : "https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download", "filesAnalyzed" : false, "homepage" : "http://saxon.sourceforge.net/", "licenseComments" : "Other versions available for a commercial license", "licenseConcluded" : "MPL-1.0", "licenseDeclared" : "MPL-1.0", "name" : "Saxon", "packageFileName" : "saxonB-8.8.zip", "versionInfo" : "8.8" } ], "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-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", "relatedSpdxElement" : "SPDXRef-Package", "relationshipType" : "CONTAINS" }, { "spdxElementId" : "SPDXRef-DOCUMENT", "relatedSpdxElement" : "DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement", "relationshipType" : "COPY_OF" }, { "spdxElementId" : "SPDXRef-DOCUMENT", "relatedSpdxElement" : "SPDXRef-File", "relationshipType" : "DESCRIBES" }, { "spdxElementId" : "SPDXRef-DOCUMENT", "relatedSpdxElement" : "SPDXRef-Package", "relationshipType" : "DESCRIBES" }, { "spdxElementId" : "SPDXRef-Package", "relatedSpdxElement" : "SPDXRef-JenaLib", "relationshipType" : "CONTAINS" }, { "spdxElementId" : "SPDXRef-Package", "relatedSpdxElement" : "SPDXRef-Saxon", "relationshipType" : "DYNAMIC_LINK" }, { "spdxElementId" : "SPDXRef-CommonsLangSrc", "relatedSpdxElement" : "NOASSERTION", "relationshipType" : "GENERATED_FROM" }, { "spdxElementId" : "SPDXRef-JenaLib", "relatedSpdxElement" : "SPDXRef-Package", "relationshipType" : "CONTAINS" }, { "spdxElementId" : "SPDXRef-File", "relatedSpdxElement" : "SPDXRef-fromDoap-0", "relationshipType" : "GENERATED_FROM" } ] } tools-golang-0.5.5/examples/sample-docs/json/SPDXJSONExample-v2.3.spdx.json000066400000000000000000000534441463371440000262620ustar00rootroot00000000000000{ "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "SPDX-Tools-v2.0", "documentNamespace": "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", "externalDocumentRefs": [ { "externalDocumentId": "DocumentRef-spdx-tool-1.2", "spdxDocument": "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", "checksum": { "algorithm": "SHA1", "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2759" } } ], "comment": "This document was created using SPDX 2.0 using licenses from the web site.", "creationInfo": { "licenseListVersion": "3.9", "creators": [ "Tool: LicenseFind-1.0", "Organization: ExampleCodeInspect ()", "Person: Jane Doe ()" ], "created": "2010-01-29T18:30:22Z", "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." }, "packages": [ { "name": "glibc", "SPDXID": "SPDXRef-Package", "versionInfo": "2.11.1", "packageFileName": "glibc-2.11.1.tar.gz", "supplier": "Person: Jane Doe (jane.doe@example.com)", "originator": "Organization: ExampleCodeInspect (contact@example.com)", "downloadLocation": "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz", "filesAnalyzed": true, "packageVerificationCode": { "packageVerificationCodeValue": "d6a770ba38583ed4bb4525bd96e50461655d2758", "packageVerificationCodeExcludedFiles": [ "./package.spdx" ] }, "checksums": [ { "algorithm": "MD5", "checksumValue": "624c1abb3664f4b35547e7c73864ad24" }, { "algorithm": "SHA1", "checksumValue": "85ed0817af83a24ad8da68c2b5094de69833983c" }, { "algorithm": "SHA256", "checksumValue": "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" } ], "homepage": "http://ftp.gnu.org/gnu/glibc", "sourceInfo": "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.", "licenseConcluded": "(LGPL-2.0-only OR LicenseRef-3)", "licenseInfoFromFiles": [ "GPL-2.0-only", "LicenseRef-2", "LicenseRef-1" ], "licenseDeclared": "(LGPL-2.0-only AND LicenseRef-3)", "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.", "copyrightText": "Copyright 2008-2010 John Smith", "summary": "GNU C library.", "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.", "externalRefs": [ { "referenceCategory": "SECURITY", "referenceType": "cpe23Type", "referenceLocator": "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" }, { "referenceCategory": "OTHER", "referenceType": "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge", "referenceLocator": "acmecorp/acmenator/4.1.3-alpha", "comment": "This is the external ref for Acme" } ], "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." ], "annotations": [ { "annotator": "Person: Package Commenter", "annotationDate": "2011-01-29T18:30:22Z", "annotationType": "OTHER", "comment": "Package level annotation" } ] }, { "name": "Apache Commons Lang", "SPDXID": "SPDXRef-fromDoap-1", "downloadLocation": "NOASSERTION", "filesAnalyzed": false, "homepage": "http://commons.apache.org/proper/commons-lang/", "licenseConcluded": "NOASSERTION", "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION" }, { "name": "Jena", "SPDXID": "SPDXRef-fromDoap-0", "versionInfo": "3.12.0", "downloadLocation": "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz", "homepage": "http://www.openjena.org/", "licenseConcluded": "NOASSERTION", "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION", "externalRefs": [ { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:maven/org.apache.jena/apache-jena@3.12.0" } ] }, { "name": "Saxon", "SPDXID": "SPDXRef-Saxon", "versionInfo": "8.8", "packageFileName": "saxonB-8.8.zip", "downloadLocation": "https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download", "filesAnalyzed": false, "checksums": [ { "algorithm": "SHA1", "checksumValue": "85ed0817af83a24ad8da68c2b5094de69833983c" } ], "homepage": "http://saxon.sourceforge.net/", "licenseConcluded": "MPL-1.0", "licenseDeclared": "MPL-1.0", "licenseComments": "Other versions available for a commercial license", "copyrightText": "Copyright Saxonica Ltd", "description": "The Saxon package is a collection of tools for processing XML documents." }, { "name": "centos", "SPDXID": "SPDXRef-CentOS-7", "versionInfo": "centos7.9.2009", "packageFileName": "saxonB-8.8.zip", "downloadLocation": "NOASSERTION", "homepage": "https://www.centos.org/", "copyrightText": "NOASSERTION", "description": "The CentOS container used to run the application.", "primaryPackagePurpose": "CONTAINER", "releaseDate": "2021-10-15T02:38:00Z", "builtDate": "2021-09-15T02:38:00Z", "validUntilDate": "2022-10-15T02:38:00Z" } ], "files": [ { "fileName": "./src/org/spdx/parser/DOAPProject.java", "SPDXID": "SPDXRef-DoapSource", "fileTypes": [ "SOURCE" ], "checksums": [ { "algorithm": "SHA1", "checksumValue": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" } ], "licenseConcluded": "Apache-2.0", "licenseInfoInFiles": [ "Apache-2.0" ], "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": "./lib-source/commons-lang3-3.1-sources.jar", "SPDXID": "SPDXRef-CommonsLangSrc", "fileTypes": [ "ARCHIVE" ], "checksums": [ { "algorithm": "SHA1", "checksumValue": "c2b4e1c67a2d28fced849ee1bb76e7391b93f125" } ], "licenseConcluded": "Apache-2.0", "licenseInfoInFiles": [ "Apache-2.0" ], "copyrightText": "Copyright 2001-2011 The Apache Software Foundation", "comment": "This file is used by Jena", "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())", "fileContributors": [ "Apache Software Foundation" ] }, { "fileName": "./lib-source/jena-2.6.3-sources.jar", "SPDXID": "SPDXRef-JenaLib", "fileTypes": [ "ARCHIVE" ], "checksums": [ { "algorithm": "SHA1", "checksumValue": "3ab4e1c67a2d28fced849ee1bb76e7391b93f125" } ], "licenseConcluded": "LicenseRef-1", "licenseInfoInFiles": [ "LicenseRef-1" ], "licenseComments": "This license is used by Jena", "copyrightText": "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP", "comment": "This file belongs to Jena", "fileContributors": [ "Apache Software Foundation", "Hewlett Packard Inc." ] }, { "fileName": "./package/foo.c", "SPDXID": "SPDXRef-File", "fileTypes": [ "SOURCE" ], "checksums": [ { "algorithm": "SHA1", "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2758" }, { "algorithm": "MD5", "checksumValue": "624c1abb3664f4b35547e7c73864ad24" } ], "licenseConcluded": "(LGPL-2.0-only OR LicenseRef-2)", "licenseInfoInFiles": [ "GPL-2.0-only", "LicenseRef-2" ], "licenseComments": "The concluded license was taken from the package level that the file was included in.", "copyrightText": "Copyright 2008-2010 John Smith", "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.", "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.", "fileContributors": [ "The Regents of the University of California", "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation" ], "annotations": [ { "annotator": "Person: File Commenter", "annotationDate": "2011-01-29T18:30:22Z", "annotationType": "OTHER", "comment": "File level annotation" } ] } ], "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", "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/" ], "comment": "The beerware license has a couple of other standard variants." }, { "licenseId": "LicenseRef-3", "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" ], "comment": "This is tye CyperNeko License" } ], "relationships": [ { "spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-Package", "relationshipType": "CONTAINS", "comment": "A relationship comment" }, { "spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement", "relationshipType": "COPY_OF" }, { "spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-File", "relationshipType": "DESCRIBES" }, { "spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-Package", "relationshipType": "DESCRIBES" }, { "spdxElementId": "SPDXRef-Package", "relatedSpdxElement": "SPDXRef-JenaLib", "relationshipType": "CONTAINS" }, { "spdxElementId": "SPDXRef-Package", "relatedSpdxElement": "SPDXRef-Saxon", "relationshipType": "DYNAMIC_LINK" }, { "spdxElementId": "SPDXRef-CommonsLangSrc", "relatedSpdxElement": "NOASSERTION", "relationshipType": "GENERATED_FROM" }, { "spdxElementId": "SPDXRef-JenaLib", "relatedSpdxElement": "SPDXRef-Package", "relationshipType": "CONTAINS" }, { "spdxElementId": "SPDXRef-File", "relatedSpdxElement": "SPDXRef-fromDoap-0", "relationshipType": "GENERATED_FROM" } ], "annotations": [ { "annotator": "Person: Jane Doe ()", "annotationDate": "2010-01-29T18:30:22Z", "annotationType": "OTHER", "comment": "Document level annotation" }, { "annotator": "Person: Joe Reviewer", "annotationDate": "2010-02-10T00:00:00Z", "annotationType": "REVIEW", "comment": "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses" }, { "annotator": "Person: Suzanne Reviewer", "annotationDate": "2011-03-13T00:00:00Z", "annotationType": "REVIEW", "comment": "Another example reviewer." } ], "snippets": [ { "SPDXID": "SPDXRef-Snippet", "snippetFromFile": "SPDXRef-DoapSource", "ranges": [ { "startPointer": { "offset": 310, "reference": "SPDXRef-DoapSource" }, "endPointer": { "offset": 420, "reference": "SPDXRef-DoapSource" } }, { "startPointer": { "lineNumber": 5, "reference": "SPDXRef-DoapSource" }, "endPointer": { "lineNumber": 23, "reference": "SPDXRef-DoapSource" } } ], "licenseConcluded": "GPL-2.0-only", "licenseInfoInSnippets": [ "GPL-2.0-only" ], "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.", "copyrightText": "Copyright 2008-2010 John Smith", "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.", "name": "from linux kernel" } ] }tools-golang-0.5.5/examples/sample-docs/rdf/000077500000000000000000000000001463371440000207305ustar00rootroot00000000000000tools-golang-0.5.5/examples/sample-docs/rdf/SPDXRdfExample-v2.2.spdx.rdf000066400000000000000000007536541463371440000256410ustar00rootroot00000000000000 from linux kernel Copyright 2008-2010 John Smith 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. Copyright 2010, 2011 Source Auditor Inc. Open Logic Inc. ./src/org/spdx/parser/DOAPProject.java Black Duck Software In.c 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 Source Auditor Inc. SPDX Technical Team Members This license was released January 2004 Apache License 2.0 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. true <<beginOptional>> Apache License Version 2.0, January 2004 http://www.apache.org/licenses/<<endOptional>><<beginOptional>> TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION<<endOptional>> <<var;name="bullet";original="1.";match=".{0,20}">> Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. <<var;name="bullet";original="2.";match=".{0,20}">> Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. <<var;name="bullet";original="3.";match=".{0,20}">> Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. <<var;name="bullet";original="4.";match=".{0,20}">> Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: <<var;name="bullet";original="(a)";match=".{0,20}">> You must give any other recipients of the Work or Derivative Works a copy of this License; and <<var;name="bullet";original="(b)";match=".{0,20}">> You must cause any modified files to carry prominent notices stating that You changed the files; and <<var;name="bullet";original="(c)";match=".{0,20}">> You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and <<var;name="bullet";original="(d)";match=".{0,20}">> If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. <<var;name="bullet";original="5.";match=".{0,20}">> Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. <<var;name="bullet";original="6.";match=".{0,20}">> Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. <<var;name="bullet";original="7.";match=".{0,20}">> Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. <<var;name="bullet";original="8.";match=".{0,20}">> Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. <<var;name="bullet";original="9.";match=".{0,20}">> Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.<<beginOptional>> END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright <<var;name="copyright";original="[yyyy] [name of copyright owner]";match=".+">> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.<<endOptional>> https://opensource.org/licenses/Apache-2.0 http://www.apache.org/licenses/LICENSE-2.0 Copyright <<var;name="copyright";original="[yyyy] [name of copyright owner]";match=".+">> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Apache-2.0 true Protecode Inc. http://www.openjena.org/ Jena /* * (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. */ LicenseRef-1 This license is used by Jena Hewlett Packard Inc. (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 Apache Software Foundation 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()) Apache Software Foundation c2b4e1c67a2d28fced849ee1bb76e7391b93f125 ./lib-source/commons-lang3-3.1-sources.jar Copyright 2001-2011 The Apache Software Foundation This file is used by Jena http://commons.apache.org/proper/commons-lang/ Apache Commons Lang This file belongs to Jena LGPL-2.0-only https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html <<beginOptional>> GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991<<endOptional>> <<var;name="copyright";original="Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA";match=".{0,1000}">> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION <<var;name="bullet";original="0.";match=".{0,20}">> This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. <<var;name="bullet";original="1.";match=".{0,20}">> You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. <<var;name="bullet";original="2.";match=".{0,20}">> You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: <<var;name="bullet";original="a)";match=".{0,20}">> The modified work must itself be a software library. <<var;name="bullet";original="b)";match=".{0,20}">> You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. <<var;name="bullet";original="c)";match=".{0,20}">> You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. <<var;name="bullet";original="d)";match=".{0,20}">> If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. <<var;name="bullet";original="3.";match=".{0,20}">> You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. <<var;name="bullet";original="4.";match=".{0,20}">> You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. <<var;name="bullet";original="5.";match=".{0,20}">> A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. <<var;name="bullet";original="6.";match=".{0,20}">> As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: <<var;name="bullet";original="a)";match=".{0,20}">> Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) <<var;name="bullet";original="b)";match=".{0,20}">> Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. <<var;name="bullet";original="c)";match=".{0,20}">> If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. <<var;name="bullet";original="d)";match=".{0,20}">> Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. <<var;name="bullet";original="7.";match=".{0,20}">> You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: <<var;name="bullet";original="a)";match=".{0,20}">> Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. <<var;name="bullet";original="b)";match=".{0,20}">> Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. <<var;name="bullet";original="8.";match=".{0,20}">> You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. <<var;name="bullet";original="9.";match=".{0,20}">> You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. <<var;name="bullet";original="10.";match=".{0,20}">> Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. <<var;name="bullet";original="11.";match=".{0,20}">> If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. <<var;name="bullet";original="12.";match=".{0,20}">> If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. <<var;name="bullet";original="13.";match=".{0,20}">> The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. <<var;name="bullet";original="14.";match=".{0,20}">> If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY <<var;name="bullet";original="15.";match=".{0,20}">> BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. <<var;name="bullet";original="16.";match=".{0,20}">> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.<<beginOptional>> END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it!<<endOptional>> Copyright (C) <<var;name="copyright";original="year name of author";match=".+">> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! This license was released: June 1991. This license has been superseded by LGPL-2.1. This license identifier refers to the choice to use the code under LGPL-2.0-only, as distinguished from use of code under LGPL-2.0-or-later (i.e., LGPL-2.0 or some later version). The license notice (as seen in the Standard License Header field below) states which of these applies to the code in the file. The example in the exhibit to the license shows the license notice for the "or later" approach. true GNU Library General Public License v2 only This is tye CyperNeko License http://justasample.url.com http://people.apache.org/~andyc/neko/LICENSE CyberNeko License 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. LicenseRef-3 http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd 624c1abb3664f4b35547e7c73864ad24 2.11.1 Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. true https://opensource.org/licenses/GPL-2.0 true https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html This license was released: June 1991. This license identifier refers to the choice to use the code under GPL-2.0-only, as distinguished from use of code under GPL-2.0-or-later (i.e., GPL-2.0 or some later version). The license notice (as seen in the Standard License Header field below) states which of these applies to the code in the file. The example in the exhibit to the license shows the license notice for the "or later" approach. GPL-2.0-only <<beginOptional>> GNU GENERAL PUBLIC LICENSE Version 2, June 1991<<endOptional>> Copyright (C) 1989, 1991 Free Software Foundation, Inc. <<var;name="incComma";original="";match=",|">> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. <<var;name="termsTitle";original="";match="GNU GENERAL PUBLIC LICENSE|">> TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION <<var;name="bullet";original="0.";match=".{0,20}">> This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. <<var;name="bullet";original="1.";match=".{0,20}">> You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. <<var;name="bullet";original="2.";match=".{0,20}">> You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: <<var;name="bullet";original="a)";match=".{0,20}">> You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. <<var;name="bullet";original="b)";match=".{0,20}">> You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. <<var;name="bullet";original="c)";match=".{0,20}">> If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. <<var;name="bullet";original="3.";match=".{0,20}">> You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: <<var;name="bullet";original="a)";match=".{0,20}">> Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, <<var;name="bullet";original="b)";match=".{0,20}">> Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, <<var;name="bullet";original="c)";match=".{0,20}">> Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. <<var;name="bullet";original="4.";match=".{0,20}">> You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. <<var;name="bullet";original="5.";match=".{0,20}">> You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. <<var;name="bullet";original="6.";match=".{0,20}">> Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. <<var;name="bullet";original="7.";match=".{0,20}">> If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. <<var;name="bullet";original="8.";match=".{0,20}">> If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. <<var;name="bullet";original="9.";match=".{0,20}">> The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. <<var;name="bullet";original="10.";match=".{0,20}">> If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY <<var;name="bullet";original="11.";match=".{0,20}">> BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. <<var;name="bullet";original="12.";match=".{0,20}">> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.<<beginOptional>> END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <<beginOptional>><<<endOptional>>one line to give the program's name and <<var;name="ideaArticle";original="an";match="a brief|an">> idea of what it does.<<beginOptional>>><<endOptional>> Copyright (C)<<beginOptional>><<<endOptional>> <<var;name="templateYear";original="yyyy";match="yyyy|year">><<beginOptional>>> <<endOptional>><<beginOptional>> <<<endOptional>>name of author<<beginOptional>>><<endOptional>> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <<beginOptional>><<<endOptional>>signature of Ty Coon<<beginOptional>> ><<endOptional>>, 1 April 1989 Ty Coon, President of Vice<<endOptional>><<beginOptional>> This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.<<endOptional>> GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C)< yyyy> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. GNU General Public License v2.0 only Copyright (C) <<var;name="copyright";original="yyyy name of author";match=".+">> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA. 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. This is the external ref for Acme acmecorp/acmenator/4.1.3-alpha d6a770ba38583ed4bb4525bd96e50461655d2758 excludes: ./package.spdx http://ftp.gnu.org/gnu/glibc glibc Person: Jane Doe (jane.doe@example.com) Organization: ExampleCodeInspect (contact@example.com) 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. LicenseRef-2 2011-01-29T18:30:22Z Package level annotation Person: Package Commenter cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* 85ed0817af83a24ad8da68c2b5094de69833983c uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. glibc-2.11.1.tar.gz GNU C library. true Saxon The Saxon package is a collection of tools for processing XML documents. http://saxon.sourceforge.net/ 8.8 Other versions available for a commercial license https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download saxonB-8.8.zip false https://opensource.org/licenses/MPL-1.0 http://www.mozilla.org/MPL/MPL-1.0.html This license has been superseded by v1.1 MOZILLA PUBLIC LICENSE Version 1.0 1. Definitions. 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications. 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data. 1.5. "Executable" means Covered Code in any form other than Source Code. 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 1.8. "License" means this document. 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. B. Any new file that contains any part of the Original Code or previous Modifications. 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. 1.12. "You" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. 2. Source Code License. 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell ("Utilize") the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations. 2.2. Contributor Grant. Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and (b) under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations. 3. Distribution Obligations. 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. 3.3. Description of Modifications. You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. 3.4. Intellectual Property Matters (a) Third Party Claims. If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. (b) Contributor APIs. If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file. 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. 4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Application of this License. This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code. 6. Versions of the License. 6.1. New Versions. Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. 6.3. Derivative Works. If you create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "NPL" or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) 7. DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 8. TERMINATION. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 9. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 10. U.S. GOVERNMENT END USERS. The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. 11. MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in, the United States of America: (a) unless otherwise agreed in writing, all disputes relating to this License (excepting any dispute relating to intellectual property rights) shall be subject to final and binding arbitration, with the losing party paying all costs of arbitration; (b) any arbitration relating to this Agreement shall be held in Santa Clara County, California, under the auspices of JAMS/EndDispute; and (c) any litigation relating to this Agreement shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. 12. RESPONSIBILITY FOR CLAIMS. Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis. EXHIBIT A. "The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is _____ . The Initial Developer of the Original Code is _____ . Portions created by _____ are Copyright (C) _____ . All Rights Reserved. Contributor(s): _____ ." Mozilla Public License 1.0 <<beginOptional>>"<<endOptional>>The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is <<var;name="code";original="_____";match=".+">> . The Initial Developer of the Original Code is <<var;name="InitialDeveloper";original="_____";match=".+">> . Portions created by <<var;name="createdby";original="_____";match=".+">> are Copyright (C) <<var;name="copyright";original="_____";match=".+">> . All Rights Reserved. Contributor(s): <<var;name="contributor";original="_____";match=".+">> .<<beginOptional>>"<<endOptional>> <<beginOptional>> MOZILLA PUBLIC LICENSE Version 1.0<<endOptional>> <<var;name="bullet";original="1.";match=".{0,20}">> Definitions. <<var;name="bullet";original="1.1.";match=".{0,20}">> "Contributor" means each entity that creates or contributes to the creation of Modifications. <<var;name="bullet";original="1.2.";match=".{0,20}">> "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. <<var;name="bullet";original="1.3.";match=".{0,20}">> "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. <<var;name="bullet";original="1.4.";match=".{0,20}">> "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data. <<var;name="bullet";original="1.5.";match=".{0,20}">> "Executable" means Covered Code in any form other than Source Code. <<var;name="bullet";original="1.6.";match=".{0,20}">> "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. <<var;name="bullet";original="1.7.";match=".{0,20}">> "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. <<var;name="bullet";original="1.8.";match=".{0,20}">> "License" means this document. <<var;name="bullet";original="1.9.";match=".{0,20}">> "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: <<var;name="bullet";original="A.";match=".{0,20}">> Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. <<var;name="bullet";original="B.";match=".{0,20}">> Any new file that contains any part of the Original Code or previous Modifications. <<var;name="bullet";original="1.10.";match=".{0,20}">> "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. <<var;name="bullet";original="1.11.";match=".{0,20}">> "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. <<var;name="bullet";original="1.12.";match=".{0,20}">> "You" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. <<var;name="bullet";original="2.";match=".{0,20}">> Source Code License. <<var;name="bullet";original="2.1.";match=".{0,20}">> The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: <<var;name="bullet";original="(a)";match=".{0,20}">> to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and <<var;name="bullet";original="(b)";match=".{0,20}">> under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell ("Utilize") the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations. <<var;name="bullet";original="2.2.";match=".{0,20}">> Contributor Grant. Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: <<var;name="bullet";original="(a)";match=".{0,20}">> to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and <<var;name="bullet";original="(b)";match=".{0,20}">> under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations. <<var;name="bullet";original="3.";match=".{0,20}">> Distribution Obligations. <<var;name="bullet";original="3.1.";match=".{0,20}">> Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. <<var;name="bullet";original="3.2.";match=".{0,20}">> Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. <<var;name="bullet";original="3.3.";match=".{0,20}">> Description of Modifications. You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. <<var;name="bullet";original="3.4.";match=".{0,20}">> Intellectual Property Matters <<var;name="bullet";original="(a)";match=".{0,20}">> Third Party Claims. If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. <<var;name="bullet";original="(b)";match=".{0,20}">> Contributor APIs. If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file. <<var;name="bullet";original="3.5.";match=".{0,20}">> Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. <<var;name="bullet";original="3.6.";match=".{0,20}">> Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. <<var;name="bullet";original="3.7.";match=".{0,20}">> Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. <<var;name="bullet";original="4.";match=".{0,20}">> Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. <<var;name="bullet";original="5.";match=".{0,20}">> Application of this License. This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code. <<var;name="bullet";original="6.";match=".{0,20}">> Versions of the License. <<var;name="bullet";original="6.1.";match=".{0,20}">> New Versions. Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. <<var;name="bullet";original="6.2.";match=".{0,20}">> Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. <<var;name="bullet";original="6.3.";match=".{0,20}">> Derivative Works. If you create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "NPL" or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) <<var;name="bullet";original="7.";match=".{0,20}">> DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. <<var;name="bullet";original="8.";match=".{0,20}">> TERMINATION. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. <<var;name="bullet";original="9.";match=".{0,20}">> LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. <<var;name="bullet";original="10.";match=".{0,20}">> U.S. GOVERNMENT END USERS. The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. <<var;name="bullet";original="11.";match=".{0,20}">> MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in, the United States of America: (a) unless otherwise agreed in writing, all disputes relating to this License (excepting any dispute relating to intellectual property rights) shall be subject to final and binding arbitration, with the losing party paying all costs of arbitration; (b) any arbitration relating to this Agreement shall be held in Santa Clara County, California, under the auspices of JAMS/EndDispute; and (c) any litigation relating to this Agreement shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. <<var;name="bullet";original="12.";match=".{0,20}">> RESPONSIBILITY FOR CLAIMS. Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis.<<beginOptional>> EXHIBIT A. <<beginOptional>>"<<endOptional>>The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is <<var;name="code";original="_____";match=".+">> . The Initial Developer of the Original Code is <<var;name="InitialDeveloper";original="_____";match=".+">> . Portions created by <<var;name="createdby";original="_____";match=".+">> are Copyright (C) <<var;name="copyright";original="_____";match=".+">> . All Rights Reserved. Contributor(s): <<var;name="contributor";original="_____";match=".+">> .<<beginOptional>>"<<endOptional>><<endOptional>> MPL-1.0 "The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is _____ . The Initial Developer of the Original Code is _____ . Portions created by _____ are Copyright (C) _____ . All Rights Reserved. Contributor(s): _____ ." true Copyright 2008-2010 John Smith 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. ./lib-source/jena-2.6.3-sources.jar 420 310 23 5 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. <<beginOptional>> GNU GENERAL PUBLIC LICENSE Version 2, June 1991<<endOptional>> Copyright (C) 1989, 1991 Free Software Foundation, Inc. <<var;name="incComma";original="";match=",|">> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. <<var;name="termsTitle";original="";match="GNU GENERAL PUBLIC LICENSE|">> TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION <<var;name="bullet";original="0.";match=".{0,20}">> This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. <<var;name="bullet";original="1.";match=".{0,20}">> You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. <<var;name="bullet";original="2.";match=".{0,20}">> You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: <<var;name="bullet";original="a)";match=".{0,20}">> You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. <<var;name="bullet";original="b)";match=".{0,20}">> You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. <<var;name="bullet";original="c)";match=".{0,20}">> If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. <<var;name="bullet";original="3.";match=".{0,20}">> You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: <<var;name="bullet";original="a)";match=".{0,20}">> Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, <<var;name="bullet";original="b)";match=".{0,20}">> Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, <<var;name="bullet";original="c)";match=".{0,20}">> Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. <<var;name="bullet";original="4.";match=".{0,20}">> You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. <<var;name="bullet";original="5.";match=".{0,20}">> You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. <<var;name="bullet";original="6.";match=".{0,20}">> Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. <<var;name="bullet";original="7.";match=".{0,20}">> If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. <<var;name="bullet";original="8.";match=".{0,20}">> If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. <<var;name="bullet";original="9.";match=".{0,20}">> The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. <<var;name="bullet";original="10.";match=".{0,20}">> If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY <<var;name="bullet";original="11.";match=".{0,20}">> BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. <<var;name="bullet";original="12.";match=".{0,20}">> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.<<beginOptional>> END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <<beginOptional>><<<endOptional>>one line to give the program's name and <<var;name="ideaArticle";original="an";match="a brief|an">> idea of what it does.<<beginOptional>>><<endOptional>> Copyright (C)<<beginOptional>> <<<endOptional>> <<var;name="templateYear";original="yyyy";match="yyyy|year">><<beginOptional>>> <<endOptional>><<beginOptional>> <<<endOptional>>name of author<<beginOptional>>><<endOptional>> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <<beginOptional>><<<endOptional>>signature of Ty Coon<<beginOptional>>><<endOptional>>, 1 April 1989 Ty Coon, President of Vice<<endOptional>><<beginOptional>> This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.<<endOptional>> Copyright (C) <<var;name="copyright";original="yyyy name of author";match=".+">> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA. https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html GPL-2.0 Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. GNU General Public License v2.0 only true true GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) < yyyy> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. https://opensource.org/licenses/GPL-2.0 true Another example reviewer. 2011-03-13T00:00:00Z Person: Suzanne Reviewer SPDX-2.2 ./package/foo.c The concluded license was taken from the package level that the file was included in. 2011-01-29T18:30:22Z File level annotation Person: File Commenter Copyright 2008-2010 John Smith http://www.openjena.org/ Jena The Regents of the University of California 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. Modified by Paul Mundt lethal@linux-sh.org IBM Corporation d6a770ba38583ed4bb4525bd96e50461655d2758 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. <<beginOptional>> <<beginOptional>> Creative Commons<<beginOptional>> Legal Code<<endOptional>><<endOptional>> CC0 1.0 Universal<<endOptional>><<beginOptional>> CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER.<<endOptional>> Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. <<var;name="bullet";original="1.";match=".{0,20}">> Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: <<var;name="bullet";original="i.";match=".{0,20}">> the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; <<var;name="bullet";original="ii.";match=".{0,20}">> moral rights retained by the original author(s) and/or performer(s); <<var;name="bullet";original="iii.";match=".{0,20}">> publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; <<var;name="bullet";original="iv.";match=".{0,20}">> rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; <<var;name="bullet";original="v.";match=".{0,20}">> rights protecting the extraction, dissemination, use and reuse of data in a Work; <<var;name="bullet";original="vi.";match=".{0,20}">> database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and <<var;name="bullet";original="vii.";match=".{0,20}">> other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. <<var;name="bullet";original="2.";match=".{0,20}">> Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. <<var;name="bullet";original="3.";match=".{0,20}">> Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. <<var;name="bullet";original="4.";match=".{0,20}">> Limitations and Disclaimers. <<var;name="bullet";original="a.";match=".{0,20}">> No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. <<var;name="bullet";original="b.";match=".{0,20}">> Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. <<var;name="bullet";original="c.";match=".{0,20}">> Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. <<var;name="bullet";original="d.";match=".{0,20}">> Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.<<beginOptional>> <<var;name="upstreamLink";original="";match="For more information, please see <http://creativecommons.org/publicdomain/zero/1.0/>">><<endOptional>> Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. https://creativecommons.org/publicdomain/zero/1.0/legalcode Creative Commons Zero v1.0 Universal CC0-1.0 true "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 </ LicenseName: Beer-Ware License (Version 42) LicenseCrossReference: http://people.freebsd.org/~phk/ LicenseComment: The beerware license has a couple of other standard variants. LicenseRef-Beerware-4.2 /* * (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. */ LicenseRef-4 Another example reviewer. 2011-03-13T00:00:00Z Person: Suzanne Reviewer This document was created using SPDX 2.0 using licenses from the web site. DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 2010-01-29T18:30:22Z Document level annotation Person: Jane Doe () SPDX-Tools-v2.0 1.19 2010-01-29T18:30:22Z Organization: ExampleCodeInspect () Tool: LicenseFind-1.0 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. Person: Jane Doe () This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses 2010-02-10T00:00:00Z Person: Joe Reviewer tools-golang-0.5.5/examples/sample-docs/tv/000077500000000000000000000000001463371440000206065ustar00rootroot00000000000000tools-golang-0.5.5/examples/sample-docs/tv/SPDXTagExample-v2.2.spdx000066400000000000000000000432701463371440000247270ustar00rootroot00000000000000SPDXVersion: SPDX-2.2 DataLicense: CC0-1.0 DocumentNamespace: http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 DocumentName: SPDX-Tools-v2.0 SPDXID: SPDXRef-DOCUMENT DocumentComment: This document was created using SPDX 2.0 using licenses from the web site. ## External Document References ExternalDocumentRef: DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759 ## Creation Information Creator: Tool: LicenseFind-1.0 Creator: Organization: ExampleCodeInspect () Creator: Person: Jane Doe () Created: 2010-01-29T18:30:22Z CreatorComment: 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. LicenseListVersion: 3.9 ## Annotations Annotator: Person: Jane Doe () AnnotationDate: 2010-01-29T18:30:22Z AnnotationComment: Document level annotation AnnotationType: OTHER SPDXREF: SPDXRef-DOCUMENT Annotator: Person: Joe Reviewer AnnotationDate: 2010-02-10T00:00:00Z AnnotationComment: This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT Annotator: Person: Suzanne Reviewer AnnotationDate: 2011-03-13T00:00:00Z AnnotationComment: Another example reviewer. AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT ## Relationships Relationship: SPDXRef-DOCUMENT CONTAINS SPDXRef-Package Relationship: SPDXRef-DOCUMENT COPY_OF DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package FileName: ./package/foo.c SPDXID: SPDXRef-File FileComment: 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. FileType: SOURCE FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2758 FileChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 LicenseConcluded: (LGPL-2.0-only OR LicenseRef-2) LicenseInfoInFile: GPL-2.0-only LicenseInfoInFile: LicenseRef-2 LicenseComments: The concluded license was taken from the package level that the file was included in. FileCopyrightText: Copyright 2008-2010 John Smith FileNotice: 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. FileContributor: The Regents of the University of California FileContributor: Modified by Paul Mundt lethal@linux-sh.org FileContributor: IBM Corporation ## Annotations Annotator: Person: File Commenter AnnotationDate: 2011-01-29T18:30:22Z AnnotationComment: File level annotation AnnotationType: OTHER SPDXREF: SPDXRef-File ## Relationships Relationship: SPDXRef-File GENERATED_FROM SPDXRef-fromDoap-0 ## Package Information PackageName: glibc SPDXID: SPDXRef-Package PackageVersion: 2.11.1 PackageFileName: glibc-2.11.1.tar.gz PackageSupplier: Person: Jane Doe (jane.doe@example.com) PackageOriginator: Organization: ExampleCodeInspect (contact@example.com) PackageDownloadLocation: http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz PackageVerificationCode: d6a770ba38583ed4bb4525bd96e50461655d2758(./package.spdx) PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageHomePage: http://ftp.gnu.org/gnu/glibc PackageSourceInfo: uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. PackageLicenseConcluded: (LGPL-2.0-only OR LicenseRef-3) ## License information from files PackageLicenseInfoFromFiles: GPL-2.0-only PackageLicenseInfoFromFiles: LicenseRef-2 PackageLicenseInfoFromFiles: LicenseRef-1 PackageLicenseDeclared: (LGPL-2.0-only AND LicenseRef-3) PackageLicenseComments: 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. PackageCopyrightText: Copyright 2008-2010 John Smith PackageSummary: GNU C library. PackageDescription: 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. PackageAttributionText: 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. ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* ExternalRef: OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3-alpha ExternalRefComment: This is the external ref for Acme ## Annotations Annotator: Person: Package Commenter AnnotationDate: 2011-01-29T18:30:22Z AnnotationComment: Package level annotation AnnotationType: OTHER SPDXREF: SPDXRef-Package ## Relationships Relationship: SPDXRef-Package CONTAINS SPDXRef-JenaLib Relationship: SPDXRef-Package DYNAMIC_LINK SPDXRef-Saxon ## File Information FileName: ./lib-source/commons-lang3-3.1-sources.jar SPDXID: SPDXRef-CommonsLangSrc FileComment: This file is used by Jena FileType: ARCHIVE FileChecksum: SHA1: c2b4e1c67a2d28fced849ee1bb76e7391b93f125 LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright 2001-2011 The Apache Software Foundation FileNotice: 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()) FileContributor: Apache Software Foundation ## Relationships Relationship: SPDXRef-CommonsLangSrc GENERATED_FROM NOASSERTION FileName: ./lib-source/jena-2.6.3-sources.jar SPDXID: SPDXRef-JenaLib FileComment: This file belongs to Jena FileType: ARCHIVE FileChecksum: SHA1: 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 LicenseConcluded: LicenseRef-1 LicenseInfoInFile: LicenseRef-1 LicenseComments: This license is used by Jena FileCopyrightText: (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP FileContributor: Apache Software Foundation FileContributor: Hewlett Packard Inc. ## Relationships Relationship: SPDXRef-JenaLib CONTAINS SPDXRef-Package FileName: ./src/org/spdx/parser/DOAPProject.java SPDXID: SPDXRef-DoapSource FileType: SOURCE FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright 2010, 2011 Source Auditor Inc. FileContributor: Protecode Inc. FileContributor: SPDX Technical Team Members FileContributor: Open Logic Inc. FileContributor: Source Auditor Inc. FileContributor: Black Duck Software In.c ## Package Information PackageName: Apache Commons Lang SPDXID: SPDXRef-fromDoap-1 PackageDownloadLocation: NOASSERTION PackageHomePage: http://commons.apache.org/proper/commons-lang/ PackageLicenseConcluded: NOASSERTION PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION FilesAnalyzed: false ## Package Information PackageName: Jena SPDXID: SPDXRef-fromDoap-0 PackageVersion: 3.12.0 PackageDownloadLocation: https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz PackageHomePage: http://www.openjena.org/ PackageLicenseConcluded: NOASSERTION PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ExternalRef: PACKAGE-MANAGER purl pkg:maven/org.apache.jena/apache-jena@3.12.0 FilesAnalyzed: false ## Package Information PackageName: Saxon SPDXID: SPDXRef-Saxon PackageVersion: 8.8 PackageFileName: saxonB-8.8.zip PackageDownloadLocation: https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageHomePage: http://saxon.sourceforge.net/ PackageLicenseConcluded: MPL-1.0 PackageLicenseDeclared: MPL-1.0 PackageLicenseComments: Other versions available for a commercial license PackageCopyrightText: Copyright Saxonica Ltd PackageDescription: The Saxon package is a collection of tools for processing XML documents. FilesAnalyzed: false ## Snippet Information SnippetSPDXID: SPDXRef-Snippet SnippetFromFileSPDXID: SPDXRef-DoapSource SnippetByteRange: 310:420 SnippetLineRange: 5:23 SnippetLicenseConcluded: GPL-2.0-only LicenseInfoInSnippet: GPL-2.0-only SnippetLicenseComments: 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. SnippetCopyrightText: Copyright 2008-2010 John Smith SnippetComment: 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. SnippetName: from linux kernel ## License Information LicenseID: LicenseRef-1 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. */ LicenseID: LicenseRef-2 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. LicenseID: LicenseRef-4 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. */ LicenseID: LicenseRef-Beerware-4.2 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 LicenseName: Beer-Ware License (Version 42) LicenseCrossReference: http://people.freebsd.org/~phk/ LicenseComment: The beerware license has a couple of other standard variants. LicenseID: LicenseRef-3 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. LicenseName: CyberNeko License LicenseCrossReference: http://people.apache.org/~andyc/neko/LICENSE, http://justasample.url.com LicenseComment: This is tye CyperNeko License tools-golang-0.5.5/examples/sample-docs/tv/SPDXTagExample-v2.3.spdx000066400000000000000000000413401463371440000247240ustar00rootroot00000000000000SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: SPDX-Tools-v2.0 DocumentNamespace: http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 ExternalDocumentRef: DocumentRef-DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1:d6a770ba38583ed4bb4525bd96e50461655d2759 DocumentComment: This document was created using SPDX 2.0 using licenses from the web site. LicenseListVersion: 3.9 Creator: Tool: LicenseFind-1.0 Creator: Organization: ExampleCodeInspect () Creator: Person: Jane Doe () Created: 2010-01-29T18:30:22Z CreatorComment: 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. ##### Unpackaged files FileName: ./lib-source/commons-lang3-3.1-sources.jar SPDXID: SPDXRef-CommonsLangSrc FileType: ARCHIVE FileChecksum: SHA1: c2b4e1c67a2d28fced849ee1bb76e7391b93f125 LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright 2001-2011 The Apache Software Foundation FileComment: This file is used by Jena FileNotice: 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()) FileContributor: Apache Software Foundation FileName: ./src/org/spdx/parser/DOAPProject.java SPDXID: SPDXRef-DoapSource FileType: SOURCE FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright 2010, 2011 Source Auditor Inc. FileContributor: Protecode Inc. FileContributor: SPDX Technical Team Members FileContributor: Open Logic Inc. FileContributor: Source Auditor Inc. FileContributor: Black Duck Software In.c FileName: ./package/foo.c SPDXID: SPDXRef-File FileType: SOURCE FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2758 FileChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 LicenseConcluded: (LGPL-2.0-only OR LicenseRef-2) LicenseInfoInFile: GPL-2.0-only LicenseInfoInFile: LicenseRef-2 LicenseComments: The concluded license was taken from the package level that the file was included in. FileCopyrightText: Copyright 2008-2010 John Smith FileComment: 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. FileNotice: 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. FileContributor: The Regents of the University of California FileContributor: Modified by Paul Mundt lethal@linux-sh.org FileContributor: IBM Corporation FileName: ./lib-source/jena-2.6.3-sources.jar SPDXID: SPDXRef-JenaLib FileType: ARCHIVE FileChecksum: SHA1: 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 LicenseConcluded: LicenseRef-1 LicenseInfoInFile: LicenseRef-1 LicenseComments: This license is used by Jena FileCopyrightText: (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP FileComment: This file belongs to Jena FileContributor: Apache Software Foundation FileContributor: Hewlett Packard Inc. ##### Package: centos PackageName: centos SPDXID: SPDXRef-CentOS-7 PackageVersion: centos7.9.2009 PackageFileName: saxonB-8.8.zip PackageDownloadLocation: NOASSERTION PrimaryPackagePurpose: CONTAINER ReleaseDate: 2021-10-15T02:38:00Z BuiltDate: 2021-09-15T02:38:00Z ValidUntilDate: 2022-10-15T02:38:00Z FilesAnalyzed: true PackageHomePage: https://www.centos.org/ PackageCopyrightText: NOASSERTION PackageDescription: The CentOS container used to run the application. ##### Package: glibc PackageName: glibc SPDXID: SPDXRef-Package PackageVersion: 2.11.1 PackageFileName: glibc-2.11.1.tar.gz PackageSupplier: Person: Jane Doe (jane.doe@example.com) PackageOriginator: Organization: ExampleCodeInspect (contact@example.com) PackageDownloadLocation: http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz PackageVerificationCode: d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx) PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageHomePage: http://ftp.gnu.org/gnu/glibc PackageSourceInfo: uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. PackageLicenseConcluded: (LGPL-2.0-only OR LicenseRef-3) PackageLicenseInfoFromFiles: GPL-2.0-only PackageLicenseInfoFromFiles: LicenseRef-2 PackageLicenseInfoFromFiles: LicenseRef-1 PackageLicenseDeclared: (LGPL-2.0-only AND LicenseRef-3) PackageLicenseComments: 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. PackageCopyrightText: Copyright 2008-2010 John Smith PackageSummary: GNU C library. PackageDescription: 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. ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* ExternalRef: OTHER http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge acmecorp/acmenator/4.1.3-alpha ExternalRefComment: This is the external ref for Acme PackageAttributionText: 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. ##### Package: Saxon PackageName: Saxon SPDXID: SPDXRef-Saxon PackageVersion: 8.8 PackageFileName: saxonB-8.8.zip PackageDownloadLocation: https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download FilesAnalyzed: false PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageHomePage: http://saxon.sourceforge.net/ PackageLicenseConcluded: MPL-1.0 PackageLicenseDeclared: MPL-1.0 PackageLicenseComments: Other versions available for a commercial license PackageCopyrightText: Copyright Saxonica Ltd PackageDescription: The Saxon package is a collection of tools for processing XML documents. ##### Package: Jena PackageName: Jena SPDXID: SPDXRef-fromDoap-0 PackageVersion: 3.12.0 PackageDownloadLocation: https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz FilesAnalyzed: true PackageHomePage: http://www.openjena.org/ PackageLicenseConcluded: NOASSERTION PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ExternalRef: PACKAGE-MANAGER purl pkg:maven/org.apache.jena/apache-jena@3.12.0 ##### Package: Apache Commons Lang PackageName: Apache Commons Lang SPDXID: SPDXRef-fromDoap-1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageHomePage: http://commons.apache.org/proper/commons-lang/ PackageLicenseConcluded: NOASSERTION PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ##### Other Licenses LicenseID: LicenseRef-1 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. */ LicenseID: LicenseRef-2 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. LicenseID: LicenseRef-4 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. */ LicenseID: LicenseRef-Beerware-4.2 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 LicenseName: Beer-Ware License (Version 42) LicenseCrossReference: http://people.freebsd.org/~phk/ LicenseComment: The beerware license has a couple of other standard variants. LicenseID: LicenseRef-3 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. LicenseName: CyberNeko License LicenseCrossReference: http://people.apache.org/~andyc/neko/LICENSE LicenseCrossReference: http://justasample.url.com LicenseComment: This is tye CyperNeko License ##### Relationships Relationship: SPDXRef-DOCUMENT CONTAINS SPDXRef-Package RelationshipComment: A relationship comment Relationship: SPDXRef-DOCUMENT COPY_OF DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package Relationship: SPDXRef-Package CONTAINS SPDXRef-JenaLib Relationship: SPDXRef-Package DYNAMIC_LINK SPDXRef-Saxon Relationship: SPDXRef-CommonsLangSrc GENERATED_FROM NOASSERTION Relationship: SPDXRef-JenaLib CONTAINS SPDXRef-Package Relationship: SPDXRef-File GENERATED_FROM SPDXRef-fromDoap-0 ##### Annotations Annotator: Person: Jane Doe () AnnotationDate: 2010-01-29T18:30:22Z AnnotationType: OTHER AnnotationComment: Document level annotation Annotator: Person: Joe Reviewer AnnotationDate: 2010-02-10T00:00:00Z AnnotationType: REVIEW AnnotationComment: This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses Annotator: Person: Suzanne Reviewer AnnotationDate: 2011-03-13T00:00:00Z AnnotationType: REVIEW AnnotationComment: Another example reviewer. tools-golang-0.5.5/examples/sample-docs/tv/hello-modified.spdx000066400000000000000000000042321463371440000243700ustar00rootroot00000000000000SPDXVersion: SPDX-2.2 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: hello-modified DocumentNamespace: https://swinslow.net/sampledocs/hello-modified Creator: Person: Steve Winslow (steve@swinslow.net) Creator: Tool: github.com/spdx/tools-golang/builder Creator: Tool: github.com/spdx/tools-golang/idsearcher Created: 2022-03-30T21:23:00Z ##### Package: hello PackageName: hello SPDXID: SPDXRef-Package-hello PackageDownloadLocation: git+https://github.com/swinslow/spdx-examples.git#example1/content FilesAnalyzed: true PackageVerificationCode: 9d20237bb72087e87069f96afb41c6ca2fa2a342 PackageLicenseConcluded: GPL-3.0-or-later AND MIT PackageLicenseInfoFromFiles: GPL-3.0-or-later PackageLicenseDeclared: GPL-3.0-or-later PackageCopyrightText: NOASSERTION Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-hello FileName: /build/hello SPDXID: SPDXRef-hello-binary FileType: BINARY FileChecksum: SHA1: 20291a81ef065ff891b537b64d4fdccaf6f5ac02 FileChecksum: SHA256: 83a33ff09648bb5fc5272baca88cf2b59fd81ac4cc6817b86998136af368708e FileChecksum: MD5: 08a12c966d776864cc1eb41fd03c3c3d LicenseConcluded: GPL-3.0-or-later AND MIT LicenseInfoInFile: NOASSERTION FileCopyrightText: NOASSERTION FileName: /src/Makefile SPDXID: SPDXRef-Makefile FileType: SOURCE FileChecksum: SHA1: 69a2e85696fff1865c3f0686d6c3824b59915c80 FileChecksum: SHA256: 5da19033ba058e322e21c90e6d6d859c90b1b544e7840859c12cae5da005e79c FileChecksum: MD5: 559424589a4f3f75fd542810473d8bc1 LicenseConcluded: GPL-3.0-or-later LicenseInfoInFile: GPL-3.0-or-later FileCopyrightText: NOASSERTION FileName: /src/hello.c SPDXID: SPDXRef-hello-src FileType: SOURCE FileChecksum: SHA1: 20862a6d08391d07d09344029533ec644fac6b21 FileChecksum: SHA256: b4e5ca56d1f9110ca94ed0bf4e6d9ac11c2186eb7cd95159c6fdb50e8db5a823 FileChecksum: MD5: 935054fe899ca782e11003bbae5e166c LicenseConcluded: GPL-3.0-or-later AND MIT LicenseInfoInFile: GPL-3.0-or-later FileCopyrightText: Copyright Contributors to the spdx-examples project. Relationship: SPDXRef-hello-binary GENERATED_FROM SPDXRef-hello-src Relationship: SPDXRef-hello-binary GENERATED_FROM SPDXRef-Makefile Relationship: SPDXRef-Makefile BUILD_TOOL_OF SPDXRef-Package-hello tools-golang-0.5.5/examples/sample-docs/tv/hello.spdx000066400000000000000000000041771463371440000226220ustar00rootroot00000000000000SPDXVersion: SPDX-2.2 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: hello DocumentNamespace: https://swinslow.net/spdx-examples/example1/hello-v3 Creator: Person: Steve Winslow (steve@swinslow.net) Creator: Tool: github.com/spdx/tools-golang/builder Creator: Tool: github.com/spdx/tools-golang/idsearcher Created: 2021-08-26T01:46:00Z ##### Package: hello PackageName: hello SPDXID: SPDXRef-Package-hello PackageDownloadLocation: git+https://github.com/swinslow/spdx-examples.git#example1/content FilesAnalyzed: true PackageVerificationCode: 9d20237bb72087e87069f96afb41c6ca2fa2a342 PackageLicenseConcluded: GPL-3.0-or-later PackageLicenseInfoFromFiles: GPL-3.0-or-later PackageLicenseDeclared: GPL-3.0-or-later PackageCopyrightText: NOASSERTION Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-hello FileName: /build/hello SPDXID: SPDXRef-hello-binary FileType: BINARY FileChecksum: SHA1: 20291a81ef065ff891b537b64d4fdccaf6f5ac02 FileChecksum: SHA256: 83a33ff09648bb5fc5272baca88cf2b59fd81ac4cc6817b86998136af368708e FileChecksum: MD5: 08a12c966d776864cc1eb41fd03c3c3d LicenseConcluded: GPL-3.0-or-later LicenseInfoInFile: NOASSERTION FileCopyrightText: NOASSERTION FileName: /src/Makefile SPDXID: SPDXRef-Makefile FileType: SOURCE FileChecksum: SHA1: 69a2e85696fff1865c3f0686d6c3824b59915c80 FileChecksum: SHA256: 5da19033ba058e322e21c90e6d6d859c90b1b544e7840859c12cae5da005e79c FileChecksum: MD5: 559424589a4f3f75fd542810473d8bc1 LicenseConcluded: GPL-3.0-or-later LicenseInfoInFile: GPL-3.0-or-later FileCopyrightText: NOASSERTION FileName: /src/hello.c SPDXID: SPDXRef-hello-src FileType: SOURCE FileChecksum: SHA1: 20862a6d08391d07d09344029533ec644fac6b21 FileChecksum: SHA256: b4e5ca56d1f9110ca94ed0bf4e6d9ac11c2186eb7cd95159c6fdb50e8db5a823 FileChecksum: MD5: 935054fe899ca782e11003bbae5e166c LicenseConcluded: GPL-3.0-or-later LicenseInfoInFile: GPL-3.0-or-later FileCopyrightText: Copyright Contributors to the spdx-examples project. Relationship: SPDXRef-hello-binary GENERATED_FROM SPDXRef-hello-src Relationship: SPDXRef-hello-binary GENERATED_FROM SPDXRef-Makefile Relationship: SPDXRef-Makefile BUILD_TOOL_OF SPDXRef-Package-hello tools-golang-0.5.5/examples/sample-docs/xls/000077500000000000000000000000001463371440000207635ustar00rootroot00000000000000tools-golang-0.5.5/examples/sample-docs/xls/SPDXSpreadsheetExample-v2.2.xlsx000066400000000000000000000351451463371440000266620ustar00rootroot00000000000000PKmR[Content_Types].xml͖n0E%JhKiE`DzE*מ d IE)W3w=ydU5%p+WcI"3)k"ƒx48P&F@i h `^[?rk"փGOPk]朒E+#LaAuHP@5lO@ p.^ kAXR@*@ΣG ;)i4(b5P.ޏ['³N&B*B<2X+CpAQGtKNB3iYvq6++-D8p\'qm"D8&q` PKW4T[\PKmR _rels/.relsj0 _8`Q2m4[ILbږ.[K ($}v?IQ.uӂhx>=@pH"~} n*"H׺؁8Z^'#7m{O3Guܓ'y|aD l_EYȾvql3MLeh*\3Y0oJ׏ :^}PKzIPKmRdocProps/app.xmlM 0Droz4 '{MHV盓zTE1eɆѳ:No7jH!bbYVT)$o0M9ؗGb7pe*~R>YEBnWPK6n!PKmRdocProps/core.xmlmaK0w=SCb(.E2nc[yMT߶y8{q;:?],eF CO)Ah%F]SI˥qxpƢ z5sikڇ`9=N§1|1n!^]VW!䌕0aJ0JnJU$!K3a;&yFō2x_O|]"mK""QÇ|'UM,ae6gE\Eu6Hw =WPK%APKmRxl/sharedStrings.xml\[sH~' ^=NOO 5p_Ko!Fm!iu1mOڿUJྰ'DL7ƪ//U׿=\_uV&̳|~-nN7UzE1ċ-72I!9L"6+$,i,[Tn\V\[,MȔz\+9Ad!/=9l-[x^(lȩS N^8Pb>Pe'Cth`HB2 ;岂.~h҉H͒Fh#`6nz6C njidQ0b苬xI,ԶZ#3goݽd@~nۍ$ f)  N?iHh>AS(d*ss!dba֢Fpu̒=JuF^#K@ha켦!h6)Atoܙ8j!#Fm#0ij_[1ȗ p 21bjB]RvLjV@2oOƦÁB6tw~9 dYeĹŕn2\qj]i>|=r7hee|޼l-ZfݺbWUs٢vUi3֩_EIұej۵nfN -h_H>=Uvkm,.XikvwQG"V~ m-,畧ӜFpS$D9#⋘XKt)fez†nTBL$ݭfAdn X#)Z$D5ANs@/Z <pbB֠ aWD.EE^"x >u}Dc:}϶ VHmZ PoN-oY6$[1$!&AxS5W 0V$ >:4MOJ8`w%N'!H6L|ΈDb$,`a4Ȋn !AZGBVH۽DpoW@ym^4#J1&$*B#@ JfOpzr섺F-L&yEZX6'kVlps![M£Vwou#E<:b񈆮k^7E2!im& .l9l [Iw/öp˸'HQi;PIV@Tv<ҼV:jGZ~7'\CrRV;8r(@e!DQKGRjhwRSvZ;+"p~ILz~j>>hk9C^k!k?| "~/ zvO N xGjà_xٴ5KɞyRgɲy|C56X Е=>8 T,z>Ŷ-Zm_Jm^@hA}j+~1b :<Ǧ)<sPp;O0RȉCd#0aN"#N;/2٦ ( ~gTy遅f%M`WCց_t6̳TԈXselarVA,4u(D%DQ&17T L@OШ~v _LP}"T7MnȀQ@ffnV1^U3t+< oȝ>PYog~42 )O!^a 5Np# ka~Q:0]uA50z>HxV9~?6T\?4ve} C!Xe̘tn<tOCi7Q79cm zM~Wvj0z9ᚅ'$: !,o6P0ljgn0l<ۭwC>؁=2L`aa{v JY;H 4RߦWKHn7W,Yp\旅ˆ'OQ4ek1ᵮ <4R{lp@M 'ϻ3 }9wAٿSG;Yy\0sop>Wp.?Nl|? OT:*Y -柗v*")V(3M3M3M3M3M&\3(-C)_TFޠgIƫ^)X> 6x m#!U/4"?ف(tKh`U'hqX@ knWm8:%mwέH8q7&8SI葉-zxJ« {g͹< Rtǒ\鈃5@HG#Ri PA"R+!̮{-iwz00#zlb E0S5~\d__lFLa ?%uG:Yx (J@4Z~҂dKZjf4e6;Ppv3DfTL|2ۄӃDKV>%}X'1īY":]vO.4"Ɵ@|_)8%1!;>OkV!o{4)£ST'ƻÁBmyc<m~34ng HTPdoS0CȢ ({%۾@-{(+FUq:kmN;]5%O$<1}K[W3K< f ])fZ}`c+W"hVcm˓k<$jS- Bc=yBt;YZ )hR5Wȓf^\E@Z;Z;C -4zsz{i*K3Tiν4Cf43/P U}m>WNAbq86l-G#uOz5hQ@(Ewjxvrjڷީ:N[NN{wj'NS;SϽS;Sީ}kɩ3Ծ5NmS ?&3??PKCtf0PKmRxl/workbook.xmlMo0 (o)!!=.h*ɀԑ;TYy׭X^TIN`i܋(Lu&n4aqH|-s՜1 П(|iVDf J֏1SB"uy.Sxt&JdC2 ECNsQZ,Y'K"R'O{N:ǂuIP(ങ1ה85l@5e\7 FGq6EI4E7v^93&eM4j54@6zF2 ȤMQV_GLEu͒Ys-oPKL PKmRxl/_rels/workbook.xml.relsn WA/$iUi/Ӥ^Pℨ `}F:N!}`wxFF (ƴ<j`>Hh Ge^ ֳrKg$C[ٜe gc+I)(Y Oiknh4/Kw> N`͛qggm<[lkʫEˈW*~AϢH>%)cV4fSјer̚ƬcjS%lh_1|?PK' PKmRxl/worksheets/sheet1.xmlVn0}i}i6M5'l'i~lF%srg "L *8N}~Z|sm&1 Ímܲ$_JEqE3jQZЏeD]7_aL{Jz{xSd'Ж!fJ:/l%F蘱D>aRI[WRGNcj&#e8[*}f/56|u{ hƕ͆o^ɷuev÷;wI|ߚwZNֹpyf2419ԍkh'Q7U;ڿEOY!ˇ; (ǔ=Jj"t0D@1" 8D"b1DX"iErpDኈ!$Q_G $䢫{5v }}J9?F9dQR|!%)l~/FOY0J½1!Y F+n=Ԝx6$k5-E_.weVf-;#O l0G a)WH( \hT݅$>K(K Uw%H(JQ]5||h ve y'cPCDgZ0+ P.e+4Bld> Cj(P7(j-DQ1DB-B({. jQ#Dѡ2(j(y9#jUrMn|oDQf1퐈;(,bY6e \-2cޭ'rq(@}1Z[E!S(?L_ѥ. ZԂg]Zeݳʺ^U9C卋m&Z鷜 O홳l+QgYc_YzPK8asvPKmRxl/worksheets/sheet3.xmln0#,&$I,Fvۏ ]T݀m.THƻ ڝx*o!\ȚR4 JSM["WVJ.ZVTHBm HPKX`-LF hCA~l m$FWʪZ+ x#t AK^}`3#8#%541ʕF>$GO|o>x_22xtځ(@9f'"m\=<^rMŰe`s-8p-KZ,k9.XdAhj1<ח&^KYOuN,o{WQrTe3 _@2Oas SET SM7;] }1҅zGQJN&vl,1l, /Xe` ePΦ .}dZXNaE~{˰b`TmB5tJUx//3L9%:ۻY&T5qw\ܑV+UKȕa\]kz'O?셙L1R$N-\ nWjh)@kG;~)@U_ qP j<bWI;Ka|O{^'HGtuT$ng*,QU,Y & :KT$f:b"?Y>~~pr"NVN_^hKDq!e@Lá6\8rB A[tȹѕour8r8>8,uϽȟ:Q:-:N& DQ@Mt\zp QTIQy+(-DQ'Žukg&=@L =uۜu[ȝxr[(+zgYmd ,Q!D4Ce<!:vu ע~/!|wR@G%N 8mC54m]@e|;Vm]mTO\f sɲLx^=Ȃeͦ8P͟7_PKGl PKmRxl/worksheets/sheet6.xmln0sD@4Ej3ٞkr0a贛8~mp$״K ݞuWχO>~HN䄎B+b=dBYx·Im PfI^SDH޶ ʒAX=~E )a`g '>4=}!uY .7Lӆr!hsQбT.h4@ wC2lFg2p 2p'3&O3 2k9T %SŔFC ?Y`'&O5cj>i\SsL~A2G/™ߥoDh<$Wܙ:ZXݺ(^^ymAyf 3zkE GGckhP 6A 5(\Ok:giR4KT kRA&(>A]keD]˨@Y/qIaVTN&](7UA L䡩 )Ġ,h?U]O߃PK6#;PKmRxl/worksheets/sheet7.xmlQo '? ll]5Ma4mݳتm" IcZ^"޿ 8S.je0nʺ;d߿K/R ԄNdxU-Či=m!Ր8rZfR E<-v"tnK S}E .؋|*3h+zݚ_8(85+|jYLڱF_zūԥ2NB' ?>lt b@ ? H@l@r6ôn]"O9n{ R~Ĩ| "sNutYyy]z̳p=1lG\ASBФoý2Q+\`G?k"Xkxo Z+ !Xij Ԃp`يq1^AΫzTIhZүnRX,or3pTfndl2)3So2 &s?Yn< SPDXRef-DOCUMENT SPDX-2.2 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. 2010-01-29T18:30:22Z Tool: LicenseFind-1.0 Organization: ExampleCodeInspect () Person: Jane Doe () 3.9 SPDX-Tools-v2.0 CC0-1.0 This document was created using SPDX 2.0 using licenses from the web site. DocumentRef-spdx-tool-1.2 SHA1 d6a770ba38583ed4bb4525bd96e50461655d2759 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 LicenseRef-1 /* * (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. */ LicenseRef-2 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. LicenseRef-4 /* * (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. */ LicenseRef-Beerware-4.2 The beerware license has a couple of other standard variants. "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 Beer-Ware License (Version 42) http://people.freebsd.org/~phk/ LicenseRef-3 This is tye CyperNeko License 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. CyberNeko License http://people.apache.org/~andyc/neko/LICENSE http://justasample.url.com 2010-01-29T18:30:22Z OTHER Person: Jane Doe () Document level annotation 2010-02-10T00:00:00Z REVIEW Person: Joe Reviewer This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses 2011-03-13T00:00:00Z REVIEW Person: Suzanne Reviewer Another example reviewer. http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 SPDXRef-File SPDXRef-Package SPDXRef-Package 2011-01-29T18:30:22Z OTHER Person: Package Commenter Package level annotation 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. MD5 624c1abb3664f4b35547e7c73864ad24 SHA1 85ed0817af83a24ad8da68c2b5094de69833983c SHA256 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd Copyright 2008-2010 John Smith 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. http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz SECURITY cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* cpe23Type This is the external ref for Acme OTHER acmecorp/acmenator/4.1.3-alpha http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge true SPDXRef-CommonsLangSrc SPDXRef-JenaLib SPDXRef-DoapSource http://ftp.gnu.org/gnu/glibc 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. (LGPL-2.0-only OR LicenseRef-3) (LGPL-2.0-only AND LicenseRef-3) GPL-2.0-only LicenseRef-2 LicenseRef-1 glibc Organization: ExampleCodeInspect (contact@example.com) glibc-2.11.1.tar.gz ./package.spdx d6a770ba38583ed4bb4525bd96e50461655d2758 uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. GNU C library. Person: Jane Doe (jane.doe@example.com) 2.11.1 SPDXRef-fromDoap-1 NOASSERTION NOASSERTION false http://commons.apache.org/proper/commons-lang/ NOASSERTION NOASSERTION Apache Commons Lang SPDXRef-fromDoap-0 NOASSERTION https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz PACKAGE_MANAGER pkg:maven/org.apache.jena/apache-jena@3.12.0 purl false http://www.openjena.org/ NOASSERTION NOASSERTION Jena 3.12.0 SPDXRef-Saxon SHA1 85ed0817af83a24ad8da68c2b5094de69833983c Copyright Saxonica Ltd The Saxon package is a collection of tools for processing XML documents. https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download false http://saxon.sourceforge.net/ Other versions available for a commercial license MPL-1.0 MPL-1.0 Saxon saxonB-8.8.zip 8.8 SPDXRef-DoapSource SHA1 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 Copyright 2010, 2011 Source Auditor Inc. Protecode Inc. SPDX Technical Team Members Open Logic Inc. Source Auditor Inc. Black Duck Software In.c ./src/org/spdx/parser/DOAPProject.java SOURCE Apache-2.0 Apache-2.0 SPDXRef-CommonsLangSrc SHA1 c2b4e1c67a2d28fced849ee1bb76e7391b93f125 This file is used by Jena Copyright 2001-2011 The Apache Software Foundation Apache Software Foundation ./lib-source/commons-lang3-3.1-sources.jar ARCHIVE Apache-2.0 Apache-2.0 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()) SPDXRef-JenaLib SHA1 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 This file belongs to Jena (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP Apache Software Foundation Hewlett Packard Inc. ./lib-source/jena-2.6.3-sources.jar ARCHIVE This license is used by Jena LicenseRef-1 LicenseRef-1 SPDXRef-File 2011-01-29T18:30:22Z OTHER Person: File Commenter File level annotation SHA1 d6a770ba38583ed4bb4525bd96e50461655d2758 MD5 624c1abb3664f4b35547e7c73864ad24 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. Copyright 2008-2010 John Smith The Regents of the University of California Modified by Paul Mundt lethal@linux-sh.org IBM Corporation ./package/foo.c SOURCE The concluded license was taken from the package level that the file was included in. (LGPL-2.0-only OR LicenseRef-2) GPL-2.0-only LicenseRef-2 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. SPDXRef-Snippet 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. Copyright 2008-2010 John Smith 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. GPL-2.0-only GPL-2.0-only from linux kernel 420 SPDXRef-DoapSource 310 SPDXRef-DoapSource 23 SPDXRef-DoapSource 5 SPDXRef-DoapSource SPDXRef-DoapSource SPDXRef-DOCUMENT SPDXRef-Package CONTAINS SPDXRef-DOCUMENT DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement COPY_OF SPDXRef-DOCUMENT SPDXRef-File DESCRIBES SPDXRef-DOCUMENT SPDXRef-Package DESCRIBES SPDXRef-Package SPDXRef-JenaLib CONTAINS SPDXRef-Package SPDXRef-Saxon DYNAMIC_LINK SPDXRef-CommonsLangSrc NOASSERTION GENERATED_FROM SPDXRef-JenaLib SPDXRef-Package CONTAINS SPDXRef-File SPDXRef-fromDoap-0 GENERATED_FROM tools-golang-0.5.5/examples/sample-docs/yaml/000077500000000000000000000000001463371440000211175ustar00rootroot00000000000000tools-golang-0.5.5/examples/sample-docs/yaml/SPDXYAMLExample-2.2.spdx.yaml000066400000000000000000000466401463371440000260460ustar00rootroot00000000000000--- SPDXID: "SPDXRef-DOCUMENT" spdxVersion: "SPDX-2.2" 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.9" 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\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." 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." documentNamespace: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301" documentDescribes: - "SPDXRef-File" - "SPDXRef-Package" 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." checksums: - algorithm: "MD5" checksumValue: "624c1abb3664f4b35547e7c73864ad24" - algorithm: "SHA1" checksumValue: "85ed0817af83a24ad8da68c2b5094de69833983c" - algorithm: "SHA256" checksumValue: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" 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 hasFiles: - "SPDXRef-CommonsLangSrc" - "SPDXRef-JenaLib" - "SPDXRef-DoapSource" 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: "(LGPL-2.0-only OR LicenseRef-3)" 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" sourceInfo: "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git." summary: "GNU C library." supplier: "Person: Jane Doe (jane.doe@example.com)" versionInfo: "2.11.1" - SPDXID: "SPDXRef-fromDoap-1" copyrightText: "NOASSERTION" downloadLocation: "NOASSERTION" filesAnalyzed: false homepage: "http://commons.apache.org/proper/commons-lang/" licenseConcluded: "NOASSERTION" licenseDeclared: "NOASSERTION" name: "Apache Commons Lang" - SPDXID: "SPDXRef-fromDoap-0" copyrightText: "NOASSERTION" downloadLocation: "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz" externalRefs: - referenceCategory: "PACKAGE_MANAGER" referenceLocator: "pkg:maven/org.apache.jena/apache-jena@3.12.0" referenceType: "purl" homepage: "http://www.openjena.org/" licenseConcluded: "NOASSERTION" licenseDeclared: "NOASSERTION" name: "Jena" versionInfo: "3.12.0" - SPDXID: "SPDXRef-Saxon" checksums: - algorithm: "SHA1" checksumValue: "85ed0817af83a24ad8da68c2b5094de69833983c" copyrightText: "Copyright Saxonica Ltd" description: "The Saxon package is a collection of tools for processing XML documents." downloadLocation: "https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download" filesAnalyzed: false homepage: "http://saxon.sourceforge.net/" licenseComments: "Other versions available for a commercial license" licenseConcluded: "MPL-1.0" licenseDeclared: "MPL-1.0" name: "Saxon" packageFileName: "saxonB-8.8.zip" versionInfo: "8.8" 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-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\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." 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" relatedSpdxElement: "SPDXRef-Package" relationshipType: "CONTAINS" - spdxElementId: "SPDXRef-DOCUMENT" relatedSpdxElement: "DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement" relationshipType: "COPY_OF" - spdxElementId: "SPDXRef-DOCUMENT" relatedSpdxElement: "SPDXRef-File" relationshipType: "DESCRIBES" - spdxElementId: "SPDXRef-DOCUMENT" relatedSpdxElement: "SPDXRef-Package" relationshipType: "DESCRIBES" - spdxElementId: "SPDXRef-Package" relatedSpdxElement: "SPDXRef-JenaLib" relationshipType: "CONTAINS" - spdxElementId: "SPDXRef-Package" relatedSpdxElement: "SPDXRef-Saxon" relationshipType: "DYNAMIC_LINK" - spdxElementId: "SPDXRef-CommonsLangSrc" relatedSpdxElement: "NOASSERTION" relationshipType: "GENERATED_FROM" - spdxElementId: "SPDXRef-JenaLib" relatedSpdxElement: "SPDXRef-Package" relationshipType: "CONTAINS" - spdxElementId: "SPDXRef-File" relatedSpdxElement: "SPDXRef-fromDoap-0" relationshipType: "GENERATED_FROM" tools-golang-0.5.5/examples/sample-docs/yaml/SPDXYAMLExample-2.3.spdx.yaml000066400000000000000000000460101463371440000260360ustar00rootroot00000000000000SPDXID: 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.9" 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-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\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." 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. */ licenseId: LicenseRef-1 - 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-2 - 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. */ 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\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." 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. checksums: - algorithm: MD5 checksumValue: 624c1abb3664f4b35547e7c73864ad24 - algorithm: SHA1 checksumValue: 85ed0817af83a24ad8da68c2b5094de69833983c - algorithm: SHA256 checksumValue: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd 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: (LGPL-2.0-only OR LicenseRef-3) 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 sourceInfo: uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. summary: GNU C library. supplier: 'Person: Jane Doe (jane.doe@example.com)' versionInfo: 2.11.1 - SPDXID: SPDXRef-fromDoap-1 copyrightText: NOASSERTION downloadLocation: NOASSERTION filesAnalyzed: false homepage: http://commons.apache.org/proper/commons-lang/ licenseConcluded: NOASSERTION licenseDeclared: NOASSERTION name: Apache Commons Lang - SPDXID: SPDXRef-fromDoap-0 copyrightText: NOASSERTION downloadLocation: https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz externalRefs: - referenceCategory: PACKAGE-MANAGER referenceLocator: pkg:maven/org.apache.jena/apache-jena@3.12.0 referenceType: purl homepage: http://www.openjena.org/ licenseConcluded: NOASSERTION licenseDeclared: NOASSERTION name: Jena versionInfo: 3.12.0 - SPDXID: SPDXRef-Saxon checksums: - algorithm: SHA1 checksumValue: 85ed0817af83a24ad8da68c2b5094de69833983c copyrightText: Copyright Saxonica Ltd description: The Saxon package is a collection of tools for processing XML documents. filesAnalyzed: false downloadLocation: https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download homepage: http://saxon.sourceforge.net/ licenseComments: Other versions available for a commercial license licenseConcluded: MPL-1.0 licenseDeclared: MPL-1.0 name: Saxon packageFileName: saxonB-8.8.zip versionInfo: "8.8" - SPDXID: SPDXRef-CentOS-7 builtDate: "2021-09-15T02:38:00Z" copyrightText: NOASSERTION description: The CentOS container used to run the application. downloadLocation: NOASSERTION homepage: https://www.centos.org/ name: centos packageFileName: saxonB-8.8.zip primaryPackagePurpose: CONTAINER releaseDate: "2021-10-15T02:38:00Z" validUntilDate: "2022-10-15T02:38:00Z" versionInfo: centos7.9.2009 relationships: - comment: A relationship comment relatedSpdxElement: SPDXRef-Package relationshipType: CONTAINS spdxElementId: SPDXRef-DOCUMENT - relatedSpdxElement: DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement relationshipType: COPY_OF spdxElementId: SPDXRef-DOCUMENT - relatedSpdxElement: SPDXRef-File relationshipType: DESCRIBES spdxElementId: SPDXRef-DOCUMENT - relatedSpdxElement: SPDXRef-Package relationshipType: DESCRIBES spdxElementId: SPDXRef-DOCUMENT - relatedSpdxElement: SPDXRef-JenaLib relationshipType: CONTAINS spdxElementId: SPDXRef-Package - relatedSpdxElement: SPDXRef-Saxon relationshipType: DYNAMIC_LINK spdxElementId: SPDXRef-Package - relatedSpdxElement: NOASSERTION relationshipType: GENERATED_FROM spdxElementId: SPDXRef-CommonsLangSrc - relatedSpdxElement: SPDXRef-Package relationshipType: CONTAINS spdxElementId: SPDXRef-JenaLib - relatedSpdxElement: SPDXRef-fromDoap-0 relationshipType: GENERATED_FROM spdxElementId: SPDXRef-File 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 tools-golang-0.5.5/go.mod000066400000000000000000000004351463371440000152400ustar00rootroot00000000000000module github.com/spdx/tools-golang go 1.13 require ( github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 github.com/google/go-cmp v0.6.0 github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb github.com/stretchr/testify v1.9.0 sigs.k8s.io/yaml v1.4.0 ) tools-golang-0.5.5/go.sum000066400000000000000000000050101463371440000152570ustar00rootroot00000000000000github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb h1:bLo8hvc8XFm9J47r690TUKBzcjSWdJDxmjXJZ+/f92U= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= tools-golang-0.5.5/idsearcher/000077500000000000000000000000001463371440000162415ustar00rootroot00000000000000tools-golang-0.5.5/idsearcher/idsearcher.go000066400000000000000000000161461463371440000207110ustar00rootroot00000000000000// Package idsearcher is used to search for short-form IDs in files // within a directory, and to build an SPDX Document containing those // license findings. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package idsearcher import ( "bufio" "fmt" "os" "path/filepath" "regexp" "sort" "strings" "github.com/spdx/tools-golang/builder" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/utils" ) // Config is a collection of configuration settings for docbuilder. // A few mandatory fields are set here // so that they can be repeatedly reused in multiple calls to Build. type Config struct { // NamespacePrefix should be a URI representing a prefix for the // namespace with which the SPDX Document will be associated. // It will be used in the DocumentNamespace field in the CreationInfo // section, followed by the per-Document package name and a random UUID. NamespacePrefix string // BuilderPathsIgnored lists certain paths to be omitted from the built // document. Each string should be a path, relative to the package's // dirRoot, to a specific file or (for all files in a directory) ending // in a slash. Prefix the string with "**" to omit all instances of that // file / directory, regardless of where it is in the file tree. BuilderPathsIgnored []string // SearcherPathsIgnored lists certain paths that should not be searched // by idsearcher, even if those paths have Files present. It uses the // same format as BuilderPathsIgnored. SearcherPathsIgnored []string } // BuildIDsDocument creates an SPDX Document and searches for // short-form IDs in each file, filling in license fields as appropriate. It // returns that document or error if any is encountered. Arguments: // - packageName: name of package / directory // - dirRoot: path to directory to be analyzed // - namespacePrefix: URI representing a prefix for the // namespace with which the SPDX Document will be associated func BuildIDsDocument(packageName string, dirRoot string, idconfig *Config) (*spdx.Document, error) { // first, build the Document using builder bconfig := &builder.Config{ NamespacePrefix: idconfig.NamespacePrefix, CreatorType: "Tool", Creator: "github.com/spdx/tools-golang/idsearcher", PathsIgnored: idconfig.BuilderPathsIgnored, } doc, err := builder.Build(packageName, dirRoot, bconfig) if err != nil { return nil, err } if doc == nil { return nil, fmt.Errorf("builder returned nil Document") } if doc.Packages == nil { return nil, fmt.Errorf("builder returned nil Packages map") } if len(doc.Packages) != 1 { return nil, fmt.Errorf("builder returned %d Packages", len(doc.Packages)) } // now, walk through each file and find its licenses (if any) pkg := doc.Packages[0] if pkg == nil { return nil, fmt.Errorf("builder returned nil Package") } if pkg.Files == nil { return nil, fmt.Errorf("builder returned nil Files in Package") } licsForPackage := map[string]int{} for _, f := range pkg.Files { // start by initializing / clearing values f.LicenseInfoInFiles = []string{"NOASSERTION"} f.LicenseConcluded = "NOASSERTION" // check whether the searcher should ignore this file if utils.ShouldIgnore(f.FileName, idconfig.SearcherPathsIgnored) { continue } fPath := filepath.Join(dirRoot, f.FileName) // FIXME this is not preferable -- ignoring error ids, _ := searchFileIDs(fPath) // FIXME for now, proceed onwards with whatever IDs we obtained. // FIXME instead of ignoring the error, should probably either log it, // FIXME and/or enable the caller to configure what should happen. // separate out for this file's licenses licsForFile := map[string]int{} licsParens := []string{} for _, lid := range ids { // get individual elements and add for file and package licElements := getIndividualLicenses(lid) for _, elt := range licElements { licsForFile[elt] = 1 licsForPackage[elt] = 1 } // parenthesize if needed and add to slice for joining licsParens = append(licsParens, makeElement(lid)) } // OK -- now we can fill in the file's details, or NOASSERTION if none if len(licsForFile) > 0 { f.LicenseInfoInFiles = []string{} for lic := range licsForFile { f.LicenseInfoInFiles = append(f.LicenseInfoInFiles, lic) } sort.Strings(f.LicenseInfoInFiles) // avoid adding parens and joining for single-ID items if len(licsParens) == 1 { f.LicenseConcluded = ids[0] } else { f.LicenseConcluded = strings.Join(licsParens, " AND ") } } } // and finally, we can fill in the package's details if len(licsForPackage) == 0 { pkg.PackageLicenseInfoFromFiles = []string{"NOASSERTION"} } else { pkg.PackageLicenseInfoFromFiles = []string{} for lic := range licsForPackage { pkg.PackageLicenseInfoFromFiles = append(pkg.PackageLicenseInfoFromFiles, lic) } sort.Strings(pkg.PackageLicenseInfoFromFiles) } return doc, nil } // ===== Utility functions (not version-specific) ===== func searchFileIDs(filePath string) ([]string, error) { idsMap := map[string]int{} ids := []string{} f, err := os.Open(filePath) if err != nil { return nil, err } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { if strings.Contains(scanner.Text(), "SPDX-License-Identifier:") { strs := strings.SplitN(scanner.Text(), "SPDX-License-Identifier:", 2) // if prefixed by more than n characters, it's probably not a // short-form ID; it's probably code to detect short-form IDs. // Like this function itself, for example =) prefix := stripTrash(strs[0]) if len(prefix) > 5 { continue } // stop before trailing */ if it is present lidToExtract := strs[1] lidToExtract = strings.Split(lidToExtract, "*/")[0] lid := strings.TrimSpace(lidToExtract) lid = stripTrash(lid) idsMap[lid] = 1 } } // FIXME for now, ignore scanner errors because we want to return whatever // FIXME IDs were in fact found. should probably be changed to either // FIXME log the error, and/or be configurable for what should happen. // if err = scanner.Err(); err != nil { // return nil, err // } // now, convert map to string for lid := range idsMap { ids = append(ids, lid) } // and sort it sort.Strings(ids) return ids, nil } func stripTrash(lid string) string { re := regexp.MustCompile(`[^\w\s\d.\-\+()]+`) return re.ReplaceAllString(lid, "") } func makeElement(lic string) string { if strings.Contains(lic, " AND ") || strings.Contains(lic, " OR ") { return fmt.Sprintf("(%s)", lic) } return lic } func getIndividualLicenses(lic string) []string { // replace parens and '+' with spaces lic = strings.Replace(lic, "(", " ", -1) lic = strings.Replace(lic, ")", " ", -1) lic = strings.Replace(lic, "+", " ", -1) // now, split by spaces, trim, and add to slice licElements := strings.Split(lic, " ") lics := []string{} for _, elt := range licElements { elt := strings.TrimSpace(elt) // don't add if empty or if case-insensitive operator if elt == "" || strings.EqualFold(elt, "AND") || strings.EqualFold(elt, "OR") || strings.EqualFold(elt, "WITH") { continue } lics = append(lics, elt) } // sort before returning sort.Strings(lics) return lics } tools-golang-0.5.5/idsearcher/idsearcher_test.go000066400000000000000000000440601463371440000217440ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package idsearcher import ( "testing" ) func TestSearcherCanFillInIDs(t *testing.T) { packageName := "project2" dirRoot := "../testdata/project2/" config := &Config{ NamespacePrefix: "https://github.com/swinslow/spdx-docs/spdx-go/testdata-", } doc, err := BuildIDsDocument(packageName, dirRoot, config) if err != nil { t.Fatalf("expected nil error, got %v", err) } if doc == nil { t.Fatalf("expected non-nil Document, got nil") } // not checking all contents of doc, see builder tests for those // get the package and its files, checking size of each if doc.Packages == nil { t.Fatalf("expected non-nil Packages, got nil") } if len(doc.Packages) != 1 { t.Fatalf("expected Packages len to be 1, got %d", len(doc.Packages)) } pkg := doc.Packages[0] if pkg == nil { t.Fatalf("expected non-nil pkg, got nil") } if pkg.Files == nil { t.Fatalf("expected non-nil Files, got nil") } if len(pkg.Files) != 6 { t.Fatalf("expected Files len to be 6, got %d", len(pkg.Files)) } fileInFolder := pkg.Files[0] if fileInFolder.LicenseInfoInFiles == nil { t.Fatalf("expected non-nil LicenseInfoInFiles, got nil") } if len(fileInFolder.LicenseInfoInFiles) != 1 { t.Fatalf("expected LicenseInfoInFiles len to be 1, got %d", len(fileInFolder.LicenseInfoInFiles)) } if fileInFolder.LicenseInfoInFiles[0] != "MIT" { t.Errorf("expected %v, got %v", "MIT", fileInFolder.LicenseInfoInFiles[0]) } if fileInFolder.LicenseConcluded != "MIT" { t.Errorf("expected %v, got %v", "MIT", fileInFolder.LicenseConcluded) } fileTrailingComment := pkg.Files[1] if fileTrailingComment.LicenseInfoInFiles == nil { t.Fatalf("expected non-nil LicenseInfoInFiles, got nil") } if len(fileTrailingComment.LicenseInfoInFiles) != 1 { t.Fatalf("expected LicenseInfoInFiles len to be 1, got %d", len(fileTrailingComment.LicenseInfoInFiles)) } if fileTrailingComment.LicenseInfoInFiles[0] != "GPL-2.0-or-later" { t.Errorf("expected %v, got %v", "GPL-2.0-or-later", fileTrailingComment.LicenseInfoInFiles[0]) } if fileTrailingComment.LicenseConcluded != "GPL-2.0-or-later" { t.Errorf("expected %v, got %v", "GPL-2.0-or-later", fileTrailingComment.LicenseConcluded) } fileHasDuplicateID := pkg.Files[2] if fileHasDuplicateID.LicenseInfoInFiles == nil { t.Fatalf("expected non-nil LicenseInfoInFiles, got nil") } if len(fileHasDuplicateID.LicenseInfoInFiles) != 1 { t.Fatalf("expected LicenseInfoInFiles len to be 1, got %d", len(fileHasDuplicateID.LicenseInfoInFiles)) } if fileHasDuplicateID.LicenseInfoInFiles[0] != "MIT" { t.Errorf("expected %v, got %v", "MIT", fileHasDuplicateID.LicenseInfoInFiles[0]) } if fileHasDuplicateID.LicenseConcluded != "MIT" { t.Errorf("expected %v, got %v", "MIT", fileHasDuplicateID.LicenseConcluded) } fileHasID := pkg.Files[3] if fileHasID.LicenseInfoInFiles == nil { t.Fatalf("expected non-nil LicenseInfoInFiles, got nil") } if len(fileHasID.LicenseInfoInFiles) != 2 { t.Fatalf("expected LicenseInfoInFiles len to be 2, got %d", len(fileHasID.LicenseInfoInFiles)) } if fileHasID.LicenseInfoInFiles[0] != "Apache-2.0" { t.Errorf("expected %v, got %v", "Apache-2.0", fileHasID.LicenseInfoInFiles[0]) } if fileHasID.LicenseInfoInFiles[1] != "GPL-2.0-or-later" { t.Errorf("expected %v, got %v", "GPL-2.0-or-later", fileHasID.LicenseInfoInFiles[1]) } if fileHasID.LicenseConcluded != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("expected %v, got %v", "Apache-2.0 OR GPL-2.0-or-later", fileHasID.LicenseConcluded) } fileMultipleIDs := pkg.Files[4] if fileMultipleIDs.LicenseInfoInFiles == nil { t.Fatalf("expected non-nil LicenseInfoInFiles, got nil") } if len(fileMultipleIDs.LicenseInfoInFiles) != 5 { t.Fatalf("expected LicenseInfoInFiles len to be 5, got %d", len(fileMultipleIDs.LicenseInfoInFiles)) } if fileMultipleIDs.LicenseInfoInFiles[0] != "BSD-2-Clause" { t.Errorf("expected %v, got %v", "BSD-2-Clause", fileMultipleIDs.LicenseInfoInFiles[0]) } if fileMultipleIDs.LicenseInfoInFiles[1] != "BSD-3-Clause" { t.Errorf("expected %v, got %v", "BSD-3-Clause", fileMultipleIDs.LicenseInfoInFiles[1]) } // here, DO NOT keep the + if fileMultipleIDs.LicenseInfoInFiles[2] != "EPL-1.0" { t.Errorf("expected %v, got %v", "EPL-1.0", fileMultipleIDs.LicenseInfoInFiles[2]) } if fileMultipleIDs.LicenseInfoInFiles[3] != "ISC" { t.Errorf("expected %v, got %v", "ISC", fileMultipleIDs.LicenseInfoInFiles[3]) } if fileMultipleIDs.LicenseInfoInFiles[4] != "MIT" { t.Errorf("expected %v, got %v", "MIT", fileMultipleIDs.LicenseInfoInFiles[4]) } if fileMultipleIDs.LicenseConcluded != "((MIT AND BSD-3-Clause) OR ISC) AND BSD-2-Clause AND EPL-1.0+" { t.Errorf("expected %v, got %v", "((MIT AND BSD-3-Clause) OR ISC) AND BSD-2-Clause AND EPL-1.0+", fileMultipleIDs.LicenseConcluded) } fileNoID := pkg.Files[5] if fileNoID.LicenseInfoInFiles == nil { t.Fatalf("expected non-nil LicenseInfoInFiles, got nil") } if len(fileNoID.LicenseInfoInFiles) != 1 { t.Fatalf("expected LicenseInfoInFiles len to be 1, got %d", len(fileNoID.LicenseInfoInFiles)) } if fileNoID.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", fileNoID.LicenseInfoInFiles[0]) } if fileNoID.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %v, got %v", "NOASSERTION", fileNoID.LicenseConcluded) } // and finally, the package should have all of these licenses if pkg.PackageLicenseInfoFromFiles == nil { t.Fatalf("expected non-nil PackageLicenseInfoFromFiles, got nil") } if len(pkg.PackageLicenseInfoFromFiles) != 7 { t.Fatalf("expected PackageLicenseInfoFromFiles len to be 7, got %d", len(pkg.PackageLicenseInfoFromFiles)) } if pkg.PackageLicenseInfoFromFiles[0] != "Apache-2.0" { t.Errorf("expected %v, got %v", "Apache-2.0", pkg.PackageLicenseInfoFromFiles[0]) } if pkg.PackageLicenseInfoFromFiles[1] != "BSD-2-Clause" { t.Errorf("expected %v, got %v", "BSD-2-Clause", pkg.PackageLicenseInfoFromFiles[1]) } if pkg.PackageLicenseInfoFromFiles[2] != "BSD-3-Clause" { t.Errorf("expected %v, got %v", "BSD-3-Clause", pkg.PackageLicenseInfoFromFiles[2]) } // here, DO NOT keep the + if pkg.PackageLicenseInfoFromFiles[3] != "EPL-1.0" { t.Errorf("expected %v, got %v", "EPL-1.0", pkg.PackageLicenseInfoFromFiles[3]) } if pkg.PackageLicenseInfoFromFiles[4] != "GPL-2.0-or-later" { t.Errorf("expected %v, got %v", "GPL-2.0-or-later", pkg.PackageLicenseInfoFromFiles[4]) } if pkg.PackageLicenseInfoFromFiles[5] != "ISC" { t.Errorf("expected %v, got %v", "ISC", pkg.PackageLicenseInfoFromFiles[5]) } if pkg.PackageLicenseInfoFromFiles[6] != "MIT" { t.Errorf("expected %v, got %v", "MIT", pkg.PackageLicenseInfoFromFiles[6]) } } func TestSearcherCanFillInIDsAndIgnorePaths(t *testing.T) { packageName := "project3" dirRoot := "../testdata/project3/" config := &Config{ NamespacePrefix: "https://github.com/swinslow/spdx-docs/spdx-go/testdata-", BuilderPathsIgnored: []string{ "**/ignoredir/", "/excludedir/", "**/ignorefile.txt", "/alsoEXCLUDEthis.txt", }, SearcherPathsIgnored: []string{ "**/dontscan.txt", }, } doc, err := BuildIDsDocument(packageName, dirRoot, config) if err != nil { t.Fatalf("expected nil error, got %v", err) } if doc == nil { t.Fatalf("expected non-nil Document, got nil") } // not checking all contents of doc, see builder tests for those // get the package and its files, checking licenses for each, and // confirming NOASSERTION for those that are skipped pkg := doc.Packages[0] if pkg == nil { t.Fatalf("expected non-nil pkg, got nil") } if len(pkg.Files) != 5 { t.Fatalf("expected len %d, got %d", 5, len(pkg.Files)) } f := pkg.Files[0] if f.FileName != "./dontscan.txt" { t.Errorf("expected %v, got %v", "./dontscan.txt", f.FileName) } if len(f.LicenseInfoInFiles) != 1 { t.Errorf("expected len to be %d, got %d", 1, len(f.LicenseInfoInFiles)) } if f.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %s, got %s", "NOASSERTION", f.LicenseInfoInFiles[0]) } if f.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %s, got %s", "NOASSERTION", f.LicenseConcluded) } f = pkg.Files[1] if f.FileName != "./keep/keep.txt" { t.Errorf("expected %v, got %v", "./keep/keep.txt", f.FileName) } if len(f.LicenseInfoInFiles) != 1 { t.Errorf("expected len to be %d, got %d", 1, len(f.LicenseInfoInFiles)) } if f.LicenseInfoInFiles[0] != "MIT" { t.Errorf("expected %s, got %s", "MIT", f.LicenseInfoInFiles[0]) } if f.LicenseConcluded != "MIT" { t.Errorf("expected %s, got %s", "MIT", f.LicenseConcluded) } f = pkg.Files[2] if f.FileName != "./keep.txt" { t.Errorf("expected %v, got %v", "./keep.txt", f.FileName) } if len(f.LicenseInfoInFiles) != 1 { t.Errorf("expected len to be %d, got %d", 1, len(f.LicenseInfoInFiles)) } if f.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %s, got %s", "NOASSERTION", f.LicenseInfoInFiles[0]) } if f.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %s, got %s", "NOASSERTION", f.LicenseConcluded) } f = pkg.Files[3] if f.FileName != "./subdir/keep/dontscan.txt" { t.Errorf("expected %v, got %v", "./subdir/keep/dontscan.txt", f.FileName) } if len(f.LicenseInfoInFiles) != 1 { t.Errorf("expected len to be %d, got %d", 1, len(f.LicenseInfoInFiles)) } if f.LicenseInfoInFiles[0] != "NOASSERTION" { t.Errorf("expected %s, got %s", "NOASSERTION", f.LicenseInfoInFiles[0]) } if f.LicenseConcluded != "NOASSERTION" { t.Errorf("expected %s, got %s", "NOASSERTION", f.LicenseConcluded) } f = pkg.Files[4] if f.FileName != "./subdir/keep/keep.txt" { t.Errorf("expected %v, got %v", "./subdir/keep/keep.txt", f.FileName) } if len(f.LicenseInfoInFiles) != 1 { t.Errorf("expected len to be %d, got %d", 1, len(f.LicenseInfoInFiles)) } if f.LicenseInfoInFiles[0] != "MIT" { t.Errorf("expected %s, got %s", "MIT", f.LicenseInfoInFiles[0]) } if f.LicenseConcluded != "MIT" { t.Errorf("expected %s, got %s", "MIT", f.LicenseConcluded) } } func TestSearcherFailsWithInvalidPath(t *testing.T) { packageName := "project2" dirRoot := "./oops/invalid" config := &Config{ NamespacePrefix: "whatever", } _, err := BuildIDsDocument(packageName, dirRoot, config) if err == nil { t.Fatalf("expected non-nil error, got nil") } } // ===== Searcher utility tests ===== func TestCanFindShortFormIDWhenPresent(t *testing.T) { filePath := "../testdata/project2/has-id.txt" ids, err := searchFileIDs(filePath) if err != nil { t.Fatalf("expected nil error, got %v", err) } if len(ids) != 1 { t.Fatalf("expected len 1, got %d", len(ids)) } if ids[0] != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("expected %v, got %v", "Apache-2.0 OR GPL-2.0-or-later", ids[0]) } } func TestCanFindMultipleShortFormIDsWhenPresent(t *testing.T) { filePath := "../testdata/project2/has-multiple-ids.txt" ids, err := searchFileIDs(filePath) if err != nil { t.Fatalf("expected nil error, got %v", err) } if len(ids) != 3 { t.Fatalf("expected len 3, got %d", len(ids)) } if ids[0] != "(MIT AND BSD-3-Clause) OR ISC" { t.Errorf("expected %v, got %v", "(MIT AND BSD-3-Clause) OR ISC", ids[0]) } if ids[1] != "BSD-2-Clause" { t.Errorf("expected %v, got %v", "BSD-2-Clause", ids[1]) } if ids[2] != "EPL-1.0+" { t.Errorf("expected %v, got %v", "EPL-1.0+", ids[2]) } } func TestCanCollapseDuplicateShortFormIDsWhenPresent(t *testing.T) { filePath := "../testdata/project2/has-duplicate-ids.txt" ids, err := searchFileIDs(filePath) if err != nil { t.Fatalf("expected nil error, got %v", err) } if len(ids) != 1 { t.Fatalf("expected len 1, got %d", len(ids)) } if ids[0] != "MIT" { t.Errorf("expected %v, got %v", "MIT", ids[0]) } } func TestCanStripTrailingStarSlash(t *testing.T) { filePath := "../testdata/project2/folder/has-trailing-comment-marker.c" ids, err := searchFileIDs(filePath) if err != nil { t.Fatalf("expected nil error, got %v", err) } if len(ids) != 1 { t.Fatalf("expected len 1, got %d", len(ids)) } if ids[0] != "GPL-2.0-or-later" { t.Errorf("expected %v, got %v", "GPL-2.0-or-later", ids[0]) } } func TestCanIgnoreShortFormIDWhenTooManyPrefixChars(t *testing.T) { filePath := "../testdata/project4/has-id-to-ignore.txt" ids, err := searchFileIDs(filePath) if err != nil { t.Fatalf("expected nil error, got %v", err) } if len(ids) != 0 { t.Fatalf("expected len 0, got %d", len(ids)) } } func TestCanPickJustTheRightID(t *testing.T) { filePath := "../testdata/project4/has-mix-of-ids.txt" ids, err := searchFileIDs(filePath) if err != nil { t.Fatalf("expected nil error, got %v", err) } if len(ids) != 1 { t.Fatalf("expected len 1, got %d", len(ids)) } if ids[0] != "MIT" { t.Errorf("expected %v, got %v", "MIT", ids[0]) } } func TestCannotFindShortFormIDWhenAbsent(t *testing.T) { filePath := "../testdata/project2/no-id.txt" ids, err := searchFileIDs(filePath) if err != nil { t.Fatalf("expected nil error, got %v", err) } if len(ids) != 0 { t.Fatalf("expected len 0, got %d", len(ids)) } } func TestCanExcludeTrashCharactersFromID(t *testing.T) { lid := "Apac\",he-2.0" want := "Apache-2.0" got := stripTrash(lid) if want != got { t.Errorf("expected %v, got %v", want, got) } lid = "Apache-2.0" want = "Apache-2.0" got = stripTrash(lid) if want != got { t.Errorf("expected %v, got %v", want, got) } } func TestSearchFileIDsFailsWithInvalidFilePath(t *testing.T) { filePath := "./oops/nm/invalid" _, err := searchFileIDs(filePath) if err == nil { t.Fatalf("expected non-nil error, got nil") } } func TestWillParenthesizeIfNeeded(t *testing.T) { licID := "MIT OR BSD-3-Clause" retval := makeElement(licID) if retval != "(MIT OR BSD-3-Clause)" { t.Errorf("expected %v, got %v", "(MIT OR BSD-3-Clause)", retval) } licID = "ISC AND HPND" retval = makeElement(licID) if retval != "(ISC AND HPND)" { t.Errorf("expected %v, got %v", "(ISC AND HPND)", retval) } } func TestWillNotParenthesizeIfNotNeeded(t *testing.T) { lic := "MIT" retval := makeElement(lic) if retval != "MIT" { t.Errorf("expected %v, got %v", "MIT", retval) } lic = "GPL-2.0-only WITH Classpath-exception-2.0" retval = makeElement(lic) if retval != "GPL-2.0-only WITH Classpath-exception-2.0" { t.Errorf("expected %v, got %v", "GPL-2.0-only WITH Classpath-exception-2.0", retval) } } func TestCanGetIndividualLicenses(t *testing.T) { // single license lic := "MIT" lics := getIndividualLicenses(lic) if lics == nil { t.Fatalf("expected non-nil lics, got nil") } if len(lics) != 1 { t.Fatalf("expected lics to have len 1, got %d", len(lics)) } if lics[0] != "MIT" { t.Errorf("expected %v, got %v", "MIT", lics[0]) } // two-license combo lic = "ISC AND BSD-3-Clause" lics = getIndividualLicenses(lic) if lics == nil { t.Fatalf("expected non-nil lics, got nil") } if len(lics) != 2 { t.Fatalf("expected lics to have len 2, got %d", len(lics)) } // should be sorted alphabetically if lics[0] != "BSD-3-Clause" { t.Errorf("expected %v, got %v", "BSD-3-Clause", lics[0]) } if lics[1] != "ISC" { t.Errorf("expected %v, got %v", "ISC", lics[1]) } // license WITH exception lic = "GPL-2.0-only WITH Classpath-exception-2.0" lics = getIndividualLicenses(lic) if lics == nil { t.Fatalf("expected non-nil lics, got nil") } if len(lics) != 2 { t.Fatalf("expected lics to have len 2, got %d", len(lics)) } // exception should be listed separately if lics[0] != "Classpath-exception-2.0" { t.Errorf("expected %v, got %v", "Classpath-exception-2.0", lics[0]) } if lics[1] != "GPL-2.0-only" { t.Errorf("expected %v, got %v", "GPL-2.0-only", lics[1]) } // two-license combo with parens lic = "(JSON OR BSD-2-Clause)" lics = getIndividualLicenses(lic) if lics == nil { t.Fatalf("expected non-nil lics, got nil") } if len(lics) != 2 { t.Fatalf("expected lics to have len 2, got %d", len(lics)) } // parens should get dropped if lics[0] != "BSD-2-Clause" { t.Errorf("expected %v, got %v", "BSD-2-Clause", lics[0]) } if lics[1] != "JSON" { t.Errorf("expected %v, got %v", "JSON", lics[1]) } // multi-license combo with nested parens lic = "GPL-2.0-only AND ((EPL-1.0 AND BSD-4-Clause) OR MIT)" lics = getIndividualLicenses(lic) if lics == nil { t.Fatalf("expected non-nil lics, got nil") } if len(lics) != 4 { t.Fatalf("expected lics to have len 4, got %d", len(lics)) } if lics[0] != "BSD-4-Clause" { t.Errorf("expected %v, got %v", "BSD-4-Clause", lics[0]) } if lics[1] != "EPL-1.0" { t.Errorf("expected %v, got %v", "EPL-1.0", lics[1]) } if lics[2] != "GPL-2.0-only" { t.Errorf("expected %v, got %v", "GPL-2.0-only", lics[2]) } if lics[3] != "MIT" { t.Errorf("expected %v, got %v", "MIT", lics[3]) } } func TestCanGetIndividualLicensesIgnoringOperatorCase(t *testing.T) { // two-license combo with lowercase 'and' lic := "ISC and BSD-3-Clause" lics := getIndividualLicenses(lic) if lics == nil { t.Fatalf("expected non-nil lics, got nil") } // should be sorted alphabetically; 'and' should not appear if len(lics) != 2 { t.Fatalf("expected lics to have len 2, got %d", len(lics)) } if lics[0] != "BSD-3-Clause" { t.Errorf("expected %v, got %v", "BSD-3-Clause", lics[0]) } if lics[1] != "ISC" { t.Errorf("expected %v, got %v", "ISC", lics[1]) } // two-license combo with lowercase 'or' lic = "ISC or BSD-3-Clause" lics = getIndividualLicenses(lic) if lics == nil { t.Fatalf("expected non-nil lics, got nil") } // should be sorted alphabetically; 'or' should not appear if len(lics) != 2 { t.Fatalf("expected lics to have len 2, got %d", len(lics)) } if lics[0] != "BSD-3-Clause" { t.Errorf("expected %v, got %v", "BSD-3-Clause", lics[0]) } if lics[1] != "ISC" { t.Errorf("expected %v, got %v", "ISC", lics[1]) } // two-license combo with lowercase 'with' lic = "GPL-2.0-only with Classpath-exception-2.0" lics = getIndividualLicenses(lic) if lics == nil { t.Fatalf("expected non-nil lics, got nil") } // should be sorted alphabetically; 'with' should not appear if len(lics) != 2 { t.Fatalf("expected lics to have len 2, got %d", len(lics)) } if lics[0] != "Classpath-exception-2.0" { t.Errorf("expected %v, got %v", "Classpath-exception-2.0", lics[0]) } if lics[1] != "GPL-2.0-only" { t.Errorf("expected %v, got %v", "GPL-2.0-only", lics[1]) } } tools-golang-0.5.5/json/000077500000000000000000000000001463371440000151015ustar00rootroot00000000000000tools-golang-0.5.5/json/marshal/000077500000000000000000000000001463371440000165305ustar00rootroot00000000000000tools-golang-0.5.5/json/marshal/json.go000066400000000000000000000004531463371440000200320ustar00rootroot00000000000000package marshal import ( "bytes" "encoding/json" ) // JSON marshals the object _without_ escaping HTML func JSON(obj interface{}) ([]byte, error) { buf := &bytes.Buffer{} enc := json.NewEncoder(buf) enc.SetEscapeHTML(false) err := enc.Encode(obj) return bytes.TrimSpace(buf.Bytes()), err } tools-golang-0.5.5/json/marshal/json_test.go000066400000000000000000000015471463371440000210760ustar00rootroot00000000000000package marshal import ( "testing" "github.com/stretchr/testify/require" ) func Test_MarshalJSON(t *testing.T) { tests := []struct { name string in interface{} expected string }{ { name: "basic usage", in: "", expected: `""`, }, { name: "within MarshalJSON callbacks", in: s1{ s2{ s3{ Value: "", }, }, }, expected: `{"S2":{"S3":{"Value":""}}}`, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, err := JSON(test.in) require.NoError(t, err) require.Equal(t, test.expected, string(got)) }) } } type s1 struct { S2 s2 } type s2 struct { S3 s3 } func (s *s2) MarshalJSON() ([]byte, error) { return JSON(s.S3) } type s3 struct { Value string } func (s *s3) MarshalJSON() ([]byte, error) { return JSON(s.Value) } tools-golang-0.5.5/json/reader.go000066400000000000000000000035151463371440000166760ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package json import ( "bytes" "encoding/json" "fmt" "io" "github.com/spdx/tools-golang/convert" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" "github.com/spdx/tools-golang/spdx/v2/v2_2" "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // Read takes an io.Reader and returns a fully-parsed current model SPDX Document // or an error if any error is encountered. func Read(content io.Reader) (*spdx.Document, error) { doc := spdx.Document{} err := ReadInto(content, &doc) return &doc, err } // ReadInto takes an io.Reader, reads in the SPDX document at the version provided // and converts to the doc version func ReadInto(content io.Reader, doc common.AnyDocument) error { if !convert.IsPtr(doc) { return fmt.Errorf("doc to read into must be a pointer") } buf := new(bytes.Buffer) _, err := buf.ReadFrom(content) if err != nil { return err } var data interface{} err = json.Unmarshal(buf.Bytes(), &data) if err != nil { return err } val, ok := data.(map[string]interface{}) if !ok { return fmt.Errorf("not a valid SPDX JSON document") } version, ok := val["spdxVersion"] if !ok { return fmt.Errorf("JSON document does not contain spdxVersion field") } switch version { case v2_1.Version: var doc v2_1.Document err = json.Unmarshal(buf.Bytes(), &doc) if err != nil { return err } data = doc case v2_2.Version: var doc v2_2.Document err = json.Unmarshal(buf.Bytes(), &doc) if err != nil { return err } data = doc case v2_3.Version: var doc v2_3.Document err = json.Unmarshal(buf.Bytes(), &doc) if err != nil { return err } data = doc default: return fmt.Errorf("unsupported SDPX version: %s", version) } return convert.Document(data, doc) } tools-golang-0.5.5/json/reader_test.go000066400000000000000000000012021463371440000177240ustar00rootroot00000000000000package json import ( "os" "testing" ) // TestRead tests that the SPDX Reader can still parse json documents correctly // this protects against any of the custom unmarshalling code breaking given a new change set func TestRead(t *testing.T) { tt := []struct { filename string }{ {"test_fixtures/spdx2_3.json"}, } for _, tc := range tt { t.Run(tc.filename, func(t *testing.T) { file, err := os.Open(tc.filename) if err != nil { t.Errorf("error opening %s: %v", tc.filename, err) } defer file.Close() _, err = Read(file) if err != nil { t.Errorf("error reading %s: %v", tc.filename, err) } }) } } tools-golang-0.5.5/json/test_fixtures/000077500000000000000000000000001463371440000200115ustar00rootroot00000000000000tools-golang-0.5.5/json/test_fixtures/spdx2_3.json000066400000000000000000003414361463371440000222010ustar00rootroot00000000000000{ "SPDXID" : "SPDXRef-DOCUMENT", "spdxVersion" : "SPDX-2.3", "creationInfo" : { "created" : "2023-07-18T12:27:48Z", "creators" : [ "Person: Gary O'Neall", "Tool: spdx-maven-plugin" ], "licenseListVersion" : "3.21" }, "name" : "tools-java", "dataLicense" : "CC0-1.0", "documentDescribes" : [ "SPDXRef-8" ], "documentNamespace" : "test", "packages" : [ { "SPDXID" : "SPDXRef-7", "copyrightText" : "UNSPECIFIED", "description" : "A Java implementation of the JSON Schema specification", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "homepage" : "https://github.com/java-json-tools/json-schema-validator", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "Apache-2.0", "name" : "json-schema-validator", "summary" : "A Java implementation of the JSON Schema specification", "versionInfo" : "2.2.14" }, { "SPDXID" : "SPDXRef-8", "copyrightText" : "NOASSERTION", "description" : "SPDX Command Line Tools using the Spdx-Java-Library", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : true, "homepage" : "https://spdx.dev/", "licenseConcluded" : "Apache-2.0", "licenseDeclared" : "Apache-2.0", "licenseInfoFromFiles" : [ "Apache-2.0", "(Apache-2.0 AND Apache-2.0)" ], "name" : "tools-java", "originator" : "Organization: Linux Foundation", "packageFileName" : "tools-java.jar", "packageVerificationCode" : { "packageVerificationCodeValue" : "1756534e119a2fb5f683eb4ed0d7ecf253aa06ed" }, "primaryPackagePurpose" : "LIBRARY", "hasFiles" : [ "SPDXRef-28", "SPDXRef-28", "SPDXRef-29", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37", "SPDXRef-28", "SPDXRef-29", "SPDXRef-61", "SPDXRef-24", "SPDXRef-44", "SPDXRef-16", "SPDXRef-39", "SPDXRef-51", "SPDXRef-57", "SPDXRef-13", "SPDXRef-59", "SPDXRef-22", "SPDXRef-17", "SPDXRef-69", "SPDXRef-14", "SPDXRef-21", "SPDXRef-11", "SPDXRef-32", "SPDXRef-18", "SPDXRef-19", "SPDXRef-36", "SPDXRef-42", "SPDXRef-27", "SPDXRef-58", "SPDXRef-54", "SPDXRef-50", "SPDXRef-64", "SPDXRef-34", "SPDXRef-38", "SPDXRef-15", "SPDXRef-25", "SPDXRef-35", "SPDXRef-31", "SPDXRef-56", "SPDXRef-66", "SPDXRef-68", "SPDXRef-53", "SPDXRef-55", "SPDXRef-33", "SPDXRef-62", "SPDXRef-67", "SPDXRef-47", "SPDXRef-40", "SPDXRef-60", "SPDXRef-9", "SPDXRef-20", "SPDXRef-46", "SPDXRef-23", "SPDXRef-43", "SPDXRef-52", "SPDXRef-65", "SPDXRef-10", "SPDXRef-41", "SPDXRef-12", "SPDXRef-48", "SPDXRef-49", "SPDXRef-30", "SPDXRef-26", "SPDXRef-63", "SPDXRef-45", "SPDXRef-37" ], "summary" : "SPDX Command Line Tools using the Spdx-Java-Library", "supplier" : "Organization: SPDX", "versionInfo" : "1.1.8-SNAPSHOT" }, { "SPDXID" : "SPDXRef-3", "copyrightText" : "UNSPECIFIED", "description" : "Storage for SPDX documents utilizing Jackson Databind.\nThis store supports serializing and deserializing files in JSON, YAML and XML formats.", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "homepage" : "http://spdx.org", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "Apache-2.0", "name" : "spdx-jackson-store", "originator" : "Organization:SPDX", "summary" : "Storage for SPDX documents utilizing Jackson Databind.\nThis store supports serializing and deserializing files in JSON, YAML and XML formats.", "versionInfo" : "1.1.6" }, { "SPDXID" : "SPDXRef-4", "copyrightText" : "UNSPECIFIED", "description" : "Commons XMLSchema is a light weight schema object model that can be used to manipulate or\n generate XML schema.", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "NOASSERTION", "name" : "XmlSchema Core", "summary" : "Commons XMLSchema is a light weight schema object model that can be used to manipulate or\n generate XML schema." }, { "SPDXID" : "SPDXRef-5", "copyrightText" : "UNSPECIFIED", "description" : "Stores SPDX documents in Microsoft Excel formats. Supports both XLS and XLSX file types.", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "homepage" : "http://github.com/spdx/spdx-spreadsheet-store", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "Apache-2.0", "name" : "spdx-spreadsheet-store", "originator" : "Organization:SPDX", "summary" : "Stores SPDX documents in Microsoft Excel formats. Supports both XLS and XLSX file types.", "versionInfo" : "1.1.6" }, { "SPDXID" : "SPDXRef-6", "copyrightText" : "UNSPECIFIED", "description" : "SPDX store that supports serializing and deserializing SPDX tag/value files.", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "homepage" : "http://maven.apache.org", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "Apache-2.0", "name" : "spdx-tagvalue-store", "originator" : "Organization:SPDX", "summary" : "SPDX store that supports serializing and deserializing SPDX tag/value files.", "versionInfo" : "1.1.6" }, { "SPDXID" : "SPDXRef-0", "copyrightText" : "UNSPECIFIED", "description" : "JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "homepage" : "http://junit.org", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "EPL-1.0", "name" : "JUnit", "originator" : "Organization:JUnit", "summary" : "JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.", "versionInfo" : "4.13.1" }, { "SPDXID" : "SPDXRef-1", "copyrightText" : "UNSPECIFIED", "description" : "Java library which implements the Java object model for SPDX and provides useful helper functions.", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "homepage" : "https://github.com/spdx/Spdx-Java-Library", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "Apache-2.0", "name" : "java-spdx-library", "originator" : "Organization:SPDX", "summary" : "Java library which implements the Java object model for SPDX and provides useful helper functions.", "versionInfo" : "1.1.6" }, { "SPDXID" : "SPDXRef-2", "copyrightText" : "UNSPECIFIED", "description" : "This Java library implements an RDF store implementing the SPDX Java Library Storage Interface using an underlying RDF store.", "downloadLocation" : "NOASSERTION", "filesAnalyzed" : false, "homepage" : "https://github.com/spdx/spdx-java-rdf-store", "licenseConcluded" : "NOASSERTION", "licenseDeclared" : "Apache-2.0", "name" : "spdx-rdf-store", "originator" : "Organization:SPDX", "summary" : "This Java library implements an RDF store implementing the SPDX Java Library Storage Interface using an underlying RDF store.", "versionInfo" : "1.1.6" } ], "files" : [ { "SPDXID" : "SPDXRef-9", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "75ed20e7ec6452af57334a09123e7367745aeb29" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/InvalidFileNameException.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-50", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "a288e48e5d3f168d44ca6ac0b988fe870bee7356" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/AbstractFileCompareSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-51", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "3984937747f7908fd3f6ce3688ce90c81df00b03" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/package-info.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for (Apache-2.0 AND Apache-2.0)", "licenseConcluded" : "(Apache-2.0 AND Apache-2.0)", "licenseInfoInFiles" : [ "(Apache-2.0 AND Apache-2.0)" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-58", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "3b22a88678cd3e413388b151cd81b98d470bf859" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/SpdxToolsHelper.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for (Apache-2.0 AND Apache-2.0)", "licenseConcluded" : "(Apache-2.0 AND Apache-2.0)", "licenseInfoInFiles" : [ "(Apache-2.0 AND Apache-2.0)" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-59", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "66684743d721070b231425c14d92370c6a54b887" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/SpdxConverterException.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for (Apache-2.0 AND Apache-2.0)", "licenseConcluded" : "(Apache-2.0 AND Apache-2.0)", "licenseInfoInFiles" : [ "(Apache-2.0 AND Apache-2.0)" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-56", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "ec2cb0867ce69112b1a4f043106c33333e6fdd42" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/SpdxVerificationException.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for (Apache-2.0 AND Apache-2.0)", "licenseConcluded" : "(Apache-2.0 AND Apache-2.0)", "licenseInfoInFiles" : [ "(Apache-2.0 AND Apache-2.0)" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-57", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "cd1a11b5ff387b299f71962e4a766fb0456b981c" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/SpdxConverter.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-54", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "cec35ac4ee225d8af074687dd1c0d010d0a705b8" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/SpdxViewer.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-55", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "0b69b5f38bad9b2c0e7ac039c2d4db98f363a7f2" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/Verify.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-52", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "e175b5c8fc68c0aca5688fa25c3bf450959cd728" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/GenerateVerificationCode.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-53", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "71b94d742f022df3df565d0b9b2e18a36286dfaa" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/RdfSchemaToJsonSchema.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-61", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "f189abcf851acbcd00a7e709a55d04553dc2d120" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/test/java/org/spdx/tools/schema/OwlToXSDTest.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-62", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "ef0e779f7111d38eecab07e3bb324eec34b04874" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/test/java/org/spdx/tools/CompareSpdxDocsTest.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for (Apache-2.0 AND Apache-2.0)", "licenseConcluded" : "(Apache-2.0 AND Apache-2.0)", "licenseInfoInFiles" : [ "(Apache-2.0 AND Apache-2.0)" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-60", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "83f5363f812499640be247838ab3e11ce52395ec" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/test/java/org/spdx/tools/VerifyTest.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-69", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "a27f4a6ee333a76569eb266bc44be7052798a69e" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/resources/project.properties", "fileTypes" : [ "OTHER" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-67", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "74b78d4536ba3333490f5691785c2090a89ee7cb" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./resources/spdx-schema-v2.3.json", "fileTypes" : [ "OTHER" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-68", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "7df059597099bb7dcf25d2a9aedfaf4465f72d8d" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./LICENSE", "fileTypes" : [ "OTHER" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-65", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "6656403ea25de02923edc47cfe4c53511265bac6" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./resources/log4j2.xml", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-66", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "a44823d7dd9a2e250e71587c326f4d558d4f0e63" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./resources/spdx-schema-v2.2.json", "fileTypes" : [ "OTHER" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-63", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "470797430952b1812ceef11e9532005bf4d41311" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/test/java/org/spdx/tools/SpdxConverterTest.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for (Apache-2.0 AND Apache-2.0)", "licenseConcluded" : "(Apache-2.0 AND Apache-2.0)", "licenseInfoInFiles" : [ "(Apache-2.0 AND Apache-2.0)" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-64", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "74a0a937cbb05f5ec30e05dae25eda82fc02bf65" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/test/java/org/spdx/tools/GenerateVerificationCodeTest.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-18", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "8243f50e7f6b7528be71b1e9b3b17d7ff5f509b1" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/schema/OwlToXsd.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-19", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "3d26f3f89de160abdda66a660851c315199f43d0" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/schema/package-info.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-16", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "37cc11c4b3ef94f1220863dac95e14a0283b91da" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-17", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "7677c8030c2c1f6745feeaf749cc5ca1a2adb6d4" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/schema/OwlToJsonContext.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-14", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "7c48b20e78280d6e4468a7b052d4d8ed79331b60" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-15", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "b2efd916737460752161d18259dd44205c805cbd" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/schema/SchemaException.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-12", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "4cdb1350de4ebfb409a52b060b2b92a344b88a7b" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/OnlineToolException.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-13", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "a89650ca925131ce60bf16f35a9ce41c687862ad" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/RdfSchemaToXsd.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-10", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "916573baaf241c117c5fbc4bd073fb2e01c53af8" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/CompareSpdxDocs.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-11", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "7327e9256c3f56f2d5c2e9541b0db6f08652cd5c" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/SpdxVersion.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for (Apache-2.0 AND Apache-2.0)", "licenseConcluded" : "(Apache-2.0 AND Apache-2.0)", "licenseInfoInFiles" : [ "(Apache-2.0 AND Apache-2.0)" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-29", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "0a20188fd7d23f04a6b49b3ec534a3a1a95d70b1" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileLicenseCommentsSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-27", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "af9597d94516646e0d5e5b1172b18395899dfc9f" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileSpdxIdSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-28", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "58660e158461b307c467b951d5683084cb62b3a4" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileAnnotationSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-25", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "0481c8bf901e478ae104dd9aa0284405c38a7a97" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileLicenseInfoSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-26", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "a4c63213a4f59ec211663ee61da6c2c94e89abba" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/MultiDocumentSpreadsheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-23", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "f835c384afa248312c0946e4f13b3cc0e32e4b2b" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/MatchingStandardLicenses.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-24", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "17bbb8d99da76391e01095b8a0e6e9d6cc66e66c" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/AbstractSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-21", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "1924821b7ccde0331876e81774b8a5f7b489ce63" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/CONTRIBUTING.md", "fileTypes" : [ "DOCUMENTATION" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-22", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "93c732d4f0c5034c328b0e54d1308141accb49f4" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/Main.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for (Apache-2.0 AND Apache-2.0)", "licenseConcluded" : "(Apache-2.0 AND Apache-2.0)", "licenseInfoInFiles" : [ "(Apache-2.0 AND Apache-2.0)" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-20", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "d5b96882e61d24546c1d3d2506df18505d4aab33" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/RdfSchemaToJsonContext.java", "fileTypes" : [ "SOURCE" ], "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0", "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-38", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "9c259c1206a610a0d77ac299954a3b626ea358fc" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileContributorsSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-39", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "21577125874bb922a240a9c4fcbfc12981a95913" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/ExternalReferencesSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-36", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "713525527c9fad700b15ea8bce870d8b7bc86fce" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileChecksumSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-37", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "6c354e336609486b4e990234b7aa5ad6084348ec" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileConcludedSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-34", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "06585cd8b3a035447c134a3019f7f48f43ec5dc1" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/CompareHelper.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-35", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "a7a527c1a96fecdfcc4a513d592c8a65e2023865" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/DocumentSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-32", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "594adaa68dbbfd931038e1a713cc3bf585e551dd" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileCommentSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-33", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "bd2072ce80dd7b431d7783e9682bc26318ecaf30" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/CreatorSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-30", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "f05514480ae787e48201070180e8cd975ca15be2" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileAttributionSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-31", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "85d27dd745bab5b66d7b91f4cbcce8acb7552713" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/ExtractedLicenseSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-49", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "39b587027ee2bca8cbb0bf28cb34e99c334bf532" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileCopyrightSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-40", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "6c36feef2ee0fdc7c3384c6459856e914dad7a89" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/PackageSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-47", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "b2b907f2392115239e3711b57f7e6e83a39c6136" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileRelationshipSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-48", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "f938b968211f52674cf9ea0a8ce552faa18cc505" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/DocumentRelationshipSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-45", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "22eaaefbe232d2d86de2e7c76346f536c943fae9" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/VerificationSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-46", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "3b8aaf072146d61e2fc05be588eb91b4f4b6282b" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileNoticeSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-43", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "efd0f8f7b2622bf6a08bf115f0483e04cd63759c" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/FileTypeSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-44", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "de76546db4747e66c07aa53a8c009c91ca05b34c" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/NormalizedFileNameComparator.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-41", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "feb0a1738599bb2ed347959b2a97d34544dff931" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/DocumentAnnotationSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." }, { "SPDXID" : "SPDXRef-42", "checksums" : [ { "algorithm" : "SHA1", "checksumValue" : "d68229fb10fe55548df04881f3d3633f3217f102" } ], "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.", "fileContributors" : [ "Gary O'Neall" ], "fileName" : "./src/main/java/org/spdx/tools/compare/SnippetSheet.java", "fileTypes" : [ "SOURCE" ], "licenseConcluded" : "Apache-2.0", "licenseInfoInFiles" : [ "Apache-2.0" ], "noticeText" : "Licensed under the Apache License, Version 2.0 (the \"License\");\n\t\t you may not use this file except in compliance with the License.\n\t\t You may obtain a copy of the License at\n\t\t\n\t\t http://www.apache.org/licenses/LICENSE-2.0\n\t\t\n\t\t Unless required by applicable law or agreed to in writing, software\n\t\t distributed under the License is distributed on an \"AS IS\" BASIS,\n\t\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\t\t See the License for the specific language governing permissions and\n\t\t limitations under the License." } ], "relationships" : [ { "spdxElementId" : "SPDXRef-8", "relationshipType" : "TEST_CASE_OF", "relatedSpdxElement" : "SPDXRef-0" }, { "spdxElementId" : "SPDXRef-8", "relationshipType" : "DYNAMIC_LINK", "relatedSpdxElement" : "SPDXRef-1" }, { "spdxElementId" : "SPDXRef-8", "relationshipType" : "DYNAMIC_LINK", "relatedSpdxElement" : "SPDXRef-2" }, { "spdxElementId" : "SPDXRef-8", "relationshipType" : "DYNAMIC_LINK", "relatedSpdxElement" : "SPDXRef-3" }, { "spdxElementId" : "SPDXRef-8", "relationshipType" : "DYNAMIC_LINK", "relatedSpdxElement" : "SPDXRef-4" }, { "spdxElementId" : "SPDXRef-8", "relationshipType" : "DYNAMIC_LINK", "relatedSpdxElement" : "SPDXRef-5" }, { "spdxElementId" : "SPDXRef-8", "relationshipType" : "DYNAMIC_LINK", "relatedSpdxElement" : "SPDXRef-6" }, { "spdxElementId" : "SPDXRef-8", "relationshipType" : "DYNAMIC_LINK", "relatedSpdxElement" : "SPDXRef-7" }, { "spdxElementId" : "SPDXRef-9", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-50", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-51", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-58", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-59", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-56", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-57", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-54", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-55", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-52", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-53", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-61", "relationshipType" : "TEST_CASE_OF", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-62", "relationshipType" : "TEST_CASE_OF", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-60", "relationshipType" : "TEST_CASE_OF", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-69", "relationshipType" : "CONTAINED_BY", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-67", "relationshipType" : "CONTAINED_BY", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-68", "relationshipType" : "CONTAINED_BY", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-65", "relationshipType" : "CONTAINED_BY", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-66", "relationshipType" : "CONTAINED_BY", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-63", "relationshipType" : "TEST_CASE_OF", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-64", "relationshipType" : "TEST_CASE_OF", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-18", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-19", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-16", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-17", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-14", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-15", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-12", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-13", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-10", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-11", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-29", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-27", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-28", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-25", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-26", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-23", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-24", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-21", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-22", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-20", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-38", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-39", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-36", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-37", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-34", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-35", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-32", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-33", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-30", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-31", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-49", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-40", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-47", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-48", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-45", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-46", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-43", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-44", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-41", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" }, { "spdxElementId" : "SPDXRef-42", "relationshipType" : "GENERATES", "relatedSpdxElement" : "SPDXRef-8" } ] }tools-golang-0.5.5/json/writer.go000066400000000000000000000012421463371440000167430ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package json import ( "encoding/json" "io" "github.com/spdx/tools-golang/spdx/common" ) type WriteOption func(*json.Encoder) func Indent(indent string) WriteOption { return func(e *json.Encoder) { e.SetIndent("", indent) } } func EscapeHTML(escape bool) WriteOption { return func(e *json.Encoder) { e.SetEscapeHTML(escape) } } // Write takes an SPDX Document and an io.Writer, and writes the document to the writer in JSON format. func Write(doc common.AnyDocument, w io.Writer, opts ...WriteOption) error { e := json.NewEncoder(w) for _, opt := range opts { opt(e) } return e.Encode(doc) } tools-golang-0.5.5/json/writer_test.go000066400000000000000000000043351463371440000200100ustar00rootroot00000000000000package json_test import ( "bytes" "testing" "github.com/stretchr/testify/assert" "github.com/spdx/tools-golang/json" "github.com/spdx/tools-golang/spdx/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func Test_Write(t *testing.T) { tests := []struct { name string doc common.AnyDocument option []json.WriteOption want string }{ { name: "happy path", doc: spdx.Document{ SPDXVersion: "2.3", DocumentName: "test_doc", }, want: `{"spdxVersion":"2.3","dataLicense":"","SPDXID":"SPDXRef-","name":"test_doc","documentNamespace":"","creationInfo":null} `, }, { name: "happy path with Indent option", doc: spdx.Document{ SPDXVersion: "2.3", DocumentName: "test_doc", }, option: []json.WriteOption{json.Indent(" ")}, want: `{ "spdxVersion": "2.3", "dataLicense": "", "SPDXID": "SPDXRef-", "name": "test_doc", "documentNamespace": "", "creationInfo": null } `, }, { name: "happy path with EscapeHTML==true option", doc: spdx.Document{ SPDXVersion: "2.3", DocumentName: "test_doc_>", }, option: []json.WriteOption{json.EscapeHTML(true)}, want: "{\"spdxVersion\":\"2.3\",\"dataLicense\":\"\",\"SPDXID\":\"SPDXRef-\",\"name\":\"test_doc_\\u003e\",\"documentNamespace\":\"\",\"creationInfo\":null}\n", }, { name: "happy path with EscapeHTML==false option", doc: spdx.Document{ SPDXVersion: "2.3", DocumentName: "test_doc_>", }, option: []json.WriteOption{json.EscapeHTML(false)}, want: "{\"spdxVersion\":\"2.3\",\"dataLicense\":\"\",\"SPDXID\":\"SPDXRef-\",\"name\":\"test_doc_>\",\"documentNamespace\":\"\",\"creationInfo\":null}\n", }, { name: "happy path with EscapeHTML==false option", doc: spdx.Document{ SPDXVersion: "2.3", DocumentName: "test_doc_>", }, option: []json.WriteOption{json.EscapeHTML(false)}, want: "{\"spdxVersion\":\"2.3\",\"dataLicense\":\"\",\"SPDXID\":\"SPDXRef-\",\"name\":\"test_doc_>\",\"documentNamespace\":\"\",\"creationInfo\":null}\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { buf := new(bytes.Buffer) err := json.Write(tt.doc, buf, tt.option...) assert.NoError(t, err) assert.Equal(t, tt.want, buf.String()) }) } } tools-golang-0.5.5/licensediff/000077500000000000000000000000001463371440000164035ustar00rootroot00000000000000tools-golang-0.5.5/licensediff/licensediff.go000066400000000000000000000046631463371440000212160ustar00rootroot00000000000000// Package licensediff is used to generate a "diff" between the concluded // licenses in two SPDX Packages, using the filename as the match point. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package licensediff import ( "github.com/spdx/tools-golang/spdx" ) // LicensePair is a result set where we are talking about two license strings, // potentially differing, for a single filename between two SPDX Packages. type LicensePair struct { First string Second string } // MakePairs essentially just consolidates all files and LicenseConcluded // strings into a single data structure. func MakePairs(p1 *spdx.Package, p2 *spdx.Package) (map[string]LicensePair, error) { pairs := map[string]LicensePair{} // first, go through and add all files/licenses from p1 for _, f := range p1.Files { pair := LicensePair{First: f.LicenseConcluded, Second: ""} pairs[f.FileName] = pair } // now, go through all files/licenses from p2. If already // present, add as .second; if not, create new pair for _, f := range p2.Files { firstLic := "" existingPair, ok := pairs[f.FileName] if ok { // already present; update it firstLic = existingPair.First } // now, update what's there, either way pair := LicensePair{First: firstLic, Second: f.LicenseConcluded} pairs[f.FileName] = pair } return pairs, nil } // LicenseDiff is a structured version of the output of MakePairs. It is // meant to make it easier to find and report on, e.g., just the files that // have different licenses, or those that are in just one scan. type LicenseDiff struct { InBothChanged map[string]LicensePair InBothSame map[string]string InFirstOnly map[string]string InSecondOnly map[string]string } // MakeResults creates a more structured set of results from the output // of MakePairs. func MakeResults(pairs map[string]LicensePair) (*LicenseDiff, error) { diff := &LicenseDiff{ InBothChanged: map[string]LicensePair{}, InBothSame: map[string]string{}, InFirstOnly: map[string]string{}, InSecondOnly: map[string]string{}, } // walk through pairs and allocate them where they belong for filename, pair := range pairs { if pair.First == pair.Second { diff.InBothSame[filename] = pair.First } else { if pair.First == "" { diff.InSecondOnly[filename] = pair.Second } else if pair.Second == "" { diff.InFirstOnly[filename] = pair.First } else { diff.InBothChanged[filename] = pair } } } return diff, nil } tools-golang-0.5.5/licensediff/licensediff_test.go000066400000000000000000000366221463371440000222550ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package licensediff import ( "testing" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) func TestDifferCanCreateDiffPairs(t *testing.T) { // create files to be used in diff // f1 will be identical in both f1 := &spdx.File{ FileName: "/project/file1.txt", FileSPDXIdentifier: common.ElementID("File561"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "LicenseRef-We-will-ignore-LicenseInfoInFiles", }, FileCopyrightText: "We'll ignore copyright values", } // f2 will only appear in the first Package f2 := &spdx.File{ FileName: "/project/file2.txt", FileSPDXIdentifier: common.ElementID("File562"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "GPL-2.0-or-later", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // f3 will only appear in the second Package f3 := &spdx.File{ FileName: "/project/file3.txt", FileSPDXIdentifier: common.ElementID("File563"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "MPL-2.0", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // f4_1 and f4_2 will appear in first and second, // with same name, same hash and different license f4_1 := &spdx.File{ FileName: "/project/file4.txt", FileSPDXIdentifier: common.ElementID("File564"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "MIT", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } f4_2 := &spdx.File{ FileName: "/project/file4.txt", FileSPDXIdentifier: common.ElementID("File564"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "Apache-2.0 AND MIT", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // f5_1 and f5_2 will appear in first and second, // with same name, different hash and same license f5_1 := &spdx.File{ FileName: "/project/file5.txt", FileSPDXIdentifier: common.ElementID("File565"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "BSD-3-Clause", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } f5_2 := &spdx.File{ FileName: "/project/file5.txt", FileSPDXIdentifier: common.ElementID("File565"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "BSD-3-Clause", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // f6_1 and f6_2 will appear in first and second, // with same name, different hash and different license f6_1 := &spdx.File{ FileName: "/project/file6.txt", FileSPDXIdentifier: common.ElementID("File566"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "CC0-1.0", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } f6_2 := &spdx.File{ FileName: "/project/file6.txt", FileSPDXIdentifier: common.ElementID("File566"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "Unlicense", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // create Packages p1 := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "NOASSERTION", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, // fake the verification code for present purposes PackageVerificationCode: &common.PackageVerificationCode{Value: "abc123abc123"}, PackageLicenseConcluded: "NOASSERTION", PackageLicenseInfoFromFiles: []string{ "NOASSERTION", }, PackageLicenseDeclared: "NOASSERTION", PackageCopyrightText: "NOASSERTION", Files: []*spdx.File{ f1, f2, f4_1, f5_1, f6_1, }, } p2 := &spdx.Package{ PackageName: "p2", PackageSPDXIdentifier: common.ElementID("p2"), PackageDownloadLocation: "NOASSERTION", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, // fake the verification code for present purposes PackageVerificationCode: &common.PackageVerificationCode{Value: "def456def456"}, PackageLicenseConcluded: "NOASSERTION", PackageLicenseInfoFromFiles: []string{ "NOASSERTION", }, PackageLicenseDeclared: "NOASSERTION", PackageCopyrightText: "NOASSERTION", Files: []*spdx.File{ f1, f3, f4_2, f5_2, f6_2, }, } // run the diff between the two packages diffMap, err := MakePairs(p1, p2) if err != nil { t.Fatalf("Expected nil error, got %v", err) } // check that the diff results are what we expect // there should be 6 entries, one for each unique filename if len(diffMap) != 6 { t.Fatalf("Expected %d, got %d", 6, len(diffMap)) } // check each filename is present, and check its pair // pair 1 -- same in both pair1, ok := diffMap["/project/file1.txt"] if !ok { t.Fatalf("Couldn't get pair1") } if pair1.First != f1.LicenseConcluded { t.Errorf("Expected %s, got %s", f1.LicenseConcluded, pair1.First) } if pair1.Second != f1.LicenseConcluded { t.Errorf("Expected %s, got %s", f2.LicenseConcluded, pair1.Second) } // pair 2 -- only in first pair2, ok := diffMap["/project/file2.txt"] if !ok { t.Fatalf("Couldn't get pair2") } if pair2.First != f2.LicenseConcluded { t.Errorf("Expected %s, got %s", f2.LicenseConcluded, pair2.First) } if pair2.Second != "" { t.Errorf("Expected %s, got %s", "", pair2.Second) } // pair 3 -- only in second pair3, ok := diffMap["/project/file3.txt"] if !ok { t.Fatalf("Couldn't get pair3") } if pair3.First != "" { t.Errorf("Expected %s, got %s", "", pair3.First) } if pair3.Second != f3.LicenseConcluded { t.Errorf("Expected %s, got %s", f3.LicenseConcluded, pair3.Second) } // pair 4 -- in both but different license pair4, ok := diffMap["/project/file4.txt"] if !ok { t.Fatalf("Couldn't get pair4") } if pair4.First != f4_1.LicenseConcluded { t.Errorf("Expected %s, got %s", f4_1.LicenseConcluded, pair4.First) } if pair4.Second != f4_2.LicenseConcluded { t.Errorf("Expected %s, got %s", f4_2.LicenseConcluded, pair4.Second) } // pair 5 -- in both but different hash, same license pair5, ok := diffMap["/project/file5.txt"] if !ok { t.Fatalf("Couldn't get pair5") } if pair5.First != f5_1.LicenseConcluded { t.Errorf("Expected %s, got %s", f5_1.LicenseConcluded, pair5.First) } if pair5.Second != f5_2.LicenseConcluded { t.Errorf("Expected %s, got %s", f5_2.LicenseConcluded, pair5.Second) } // pair 6 -- in both but different hash, different license pair6, ok := diffMap["/project/file6.txt"] if !ok { t.Fatalf("Couldn't get pair6") } if pair6.First != f6_1.LicenseConcluded { t.Errorf("Expected %s, got %s", f6_1.LicenseConcluded, pair6.First) } if pair6.Second != f6_2.LicenseConcluded { t.Errorf("Expected %s, got %s", f6_2.LicenseConcluded, pair6.Second) } } func TestDifferCanCreateDiffStructuredResults(t *testing.T) { // create files to be used in diff // f1 will be identical in both f1 := &spdx.File{ FileName: "/project/file1.txt", FileSPDXIdentifier: common.ElementID("File561"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "LicenseRef-We-will-ignore-LicenseInfoInFiles", }, FileCopyrightText: "We'll ignore copyright values", } // f2 will only appear in the first Package f2 := &spdx.File{ FileName: "/project/file2.txt", FileSPDXIdentifier: common.ElementID("File562"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "GPL-2.0-or-later", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // f3 will only appear in the second Package f3 := &spdx.File{ FileName: "/project/file3.txt", FileSPDXIdentifier: common.ElementID("File563"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "MPL-2.0", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // f4_1 and f4_2 will appear in first and second, // with same name, same hash and different license f4_1 := &spdx.File{ FileName: "/project/file4.txt", FileSPDXIdentifier: common.ElementID("File564"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "MIT", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } f4_2 := &spdx.File{ FileName: "/project/file4.txt", FileSPDXIdentifier: common.ElementID("File564"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "Apache-2.0 AND MIT", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // f5_1 and f5_2 will appear in first and second, // with same name, different hash and same license f5_1 := &spdx.File{ FileName: "/project/file5.txt", FileSPDXIdentifier: common.ElementID("File565"), LicenseConcluded: "BSD-3-Clause", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } f5_2 := &spdx.File{ FileName: "/project/file5.txt", FileSPDXIdentifier: common.ElementID("File565"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "BSD-3-Clause", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // f6_1 and f6_2 will appear in first and second, // with same name, different hash and different license f6_1 := &spdx.File{ FileName: "/project/file6.txt", FileSPDXIdentifier: common.ElementID("File566"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "CC0-1.0", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } f6_2 := &spdx.File{ FileName: "/project/file6.txt", FileSPDXIdentifier: common.ElementID("File566"), Checksums: []common.Checksum{{ Algorithm: common.SHA1, Value: "6c92dc8bc462b6889d9b1c0bc16c54d19a2cbdd3", }, }, LicenseConcluded: "Unlicense", LicenseInfoInFiles: []string{ "NOASSERTION", }, FileCopyrightText: "NOASSERTION", } // create Packages p1 := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "NOASSERTION", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, // fake the verification code for present purposes PackageVerificationCode: &common.PackageVerificationCode{Value: "abc123abc123"}, PackageLicenseConcluded: "NOASSERTION", PackageLicenseInfoFromFiles: []string{ "NOASSERTION", }, PackageLicenseDeclared: "NOASSERTION", PackageCopyrightText: "NOASSERTION", Files: []*spdx.File{ f1, f2, f4_1, f5_1, f6_1, }, } p2 := &spdx.Package{ PackageName: "p2", PackageSPDXIdentifier: common.ElementID("p2"), PackageDownloadLocation: "NOASSERTION", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, // fake the verification code for present purposes PackageVerificationCode: &common.PackageVerificationCode{Value: "def456def456"}, PackageLicenseConcluded: "NOASSERTION", PackageLicenseInfoFromFiles: []string{ "NOASSERTION", }, PackageLicenseDeclared: "NOASSERTION", PackageCopyrightText: "NOASSERTION", Files: []*spdx.File{ f1, f3, f4_2, f5_2, f6_2, }, } // run the diff between the two packages diffMap, err := MakePairs(p1, p2) if err != nil { t.Fatalf("Expected nil error, got %v", err) } // now, create the LicenseDiff structured results from the pairs diffResults, err := MakeResults(diffMap) if err != nil { t.Fatalf("Expected nil error, got %v", err) } // check that the diff results are the expected lengths if len(diffResults.InBothChanged) != 2 { t.Fatalf("Expected %d, got %d", 2, len(diffResults.InBothChanged)) } if len(diffResults.InBothSame) != 2 { t.Fatalf("Expected %d, got %d", 2, len(diffResults.InBothSame)) } if len(diffResults.InFirstOnly) != 1 { t.Fatalf("Expected %d, got %d", 1, len(diffResults.InFirstOnly)) } if len(diffResults.InSecondOnly) != 1 { t.Fatalf("Expected %d, got %d", 1, len(diffResults.InSecondOnly)) } // check each filename is present where it belongs, and check license(s) // in both and different license: f4 and f6 // filename will map to a LicensePair check4, ok := diffResults.InBothChanged["/project/file4.txt"] if !ok { t.Fatalf("Couldn't get check4") } if check4.First != f4_1.LicenseConcluded { t.Errorf("Expected %s, got %s", f4_1.LicenseConcluded, check4.First) } if check4.Second != f4_2.LicenseConcluded { t.Errorf("Expected %s, got %s", f4_2.LicenseConcluded, check4.Second) } check6, ok := diffResults.InBothChanged["/project/file6.txt"] if !ok { t.Fatalf("Couldn't get check6") } if check6.First != f6_1.LicenseConcluded { t.Errorf("Expected %s, got %s", f6_1.LicenseConcluded, check6.First) } if check6.Second != f6_2.LicenseConcluded { t.Errorf("Expected %s, got %s", f6_2.LicenseConcluded, check6.Second) } // in both and same license: f1 and f5 // filename will map to a string check1, ok := diffResults.InBothSame["/project/file1.txt"] if !ok { t.Fatalf("Couldn't get check1") } if check1 != f1.LicenseConcluded { t.Errorf("Expected %s, got %s", f1.LicenseConcluded, check1) } check5, ok := diffResults.InBothSame["/project/file5.txt"] if !ok { t.Fatalf("Couldn't get check5") } if check5 != f5_1.LicenseConcluded { t.Errorf("Expected %s, got %s", f5_1.LicenseConcluded, check5) } if check5 != f5_2.LicenseConcluded { t.Errorf("Expected %s, got %s", f5_2.LicenseConcluded, check5) } // in first only: f2 // filename will map to a string check2, ok := diffResults.InFirstOnly["/project/file2.txt"] if !ok { t.Fatalf("Couldn't get check2") } if check2 != f2.LicenseConcluded { t.Errorf("Expected %s, got %s", f2.LicenseConcluded, check2) } // in second only: f3 // filename will map to a string check3, ok := diffResults.InSecondOnly["/project/file3.txt"] if !ok { t.Fatalf("Couldn't get check3") } if check3 != f3.LicenseConcluded { t.Errorf("Expected %s, got %s", f3.LicenseConcluded, check2) } } tools-golang-0.5.5/rdf/000077500000000000000000000000001463371440000147035ustar00rootroot00000000000000tools-golang-0.5.5/rdf/reader.go000066400000000000000000000036661463371440000165070ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package rdf import ( "errors" "fmt" "io" "github.com/spdx/gordf/rdfloader" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/convert" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" v2_2_reader "github.com/spdx/tools-golang/spdx/v2/v2_2/rdf/reader" "github.com/spdx/tools-golang/spdx/v2/v2_3" v2_3_reader "github.com/spdx/tools-golang/spdx/v2/v2_3/rdf/reader" ) // Read takes an io.Reader and returns a fully-parsed current model SPDX Document // or an error if any error is encountered. func Read(content io.Reader) (*spdx.Document, error) { doc := spdx.Document{} err := ReadInto(content, &doc) return &doc, err } // ReadInto takes an io.Reader, reads in the SPDX document at the version provided // and converts to the doc version func ReadInto(content io.Reader, doc common.AnyDocument) error { if !convert.IsPtr(doc) { return fmt.Errorf("doc to read into must be a pointer") } var rdfParserObj, err = rdfloader.LoadFromReaderObject(content) if err != nil { return err } version, err := getSpdxVersion(rdfParserObj) if err != nil { return err } var data interface{} switch version { case v2_2.Version: data, err = v2_2_reader.LoadFromGoRDFParser(rdfParserObj) case v2_3.Version: data, err = v2_3_reader.LoadFromGoRDFParser(rdfParserObj) default: return fmt.Errorf("unsupported SPDX version: '%v'", version) } if err != nil { return err } return convert.Document(data.(common.AnyDocument), doc) } func getSpdxVersion(parser *gordfParser.Parser) (string, error) { version := "" for _, node := range parser.Triples { if node.Predicate.ID == "http://spdx.org/rdf/terms#specVersion" { version = node.Object.ID break } } if version == "" { return "", errors.New("unable to determine version from RDF document") } return version, nil } tools-golang-0.5.5/rdf/reader_test.go000066400000000000000000000010241463371440000175300ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package rdf import ( "fmt" "os" "testing" "github.com/stretchr/testify/assert" "github.com/spdx/tools-golang/spdx" ) func Test_Read(t *testing.T) { fileName := "../examples/sample-docs/rdf/SPDXRdfExample-v2.2.spdx.rdf" file, err := os.Open(fileName) if err != nil { panic(fmt.Errorf("error opening File: %s", err)) } got, err := Read(file) if err != nil { t.Errorf("rdf.Read() error = %v", err) return } assert.IsType(t, &spdx.Document{}, got) } tools-golang-0.5.5/reporter/000077500000000000000000000000001463371440000157725ustar00rootroot00000000000000tools-golang-0.5.5/reporter/reporter.go000066400000000000000000000035321463371440000201660ustar00rootroot00000000000000// Package reporter contains functions to generate a basic license count // report from an in-memory SPDX Package section whose Files have been // analyzed. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reporter import ( "fmt" "io" "sort" "text/tabwriter" "github.com/spdx/tools-golang/spdx" ) // Generate takes a Package whose Files have been analyzed and an // io.Writer, and outputs to the io.Writer a tabulated count of // the number of Files for each unique LicenseConcluded in the set. func Generate(pkg *spdx.Package, w io.Writer) error { if !pkg.FilesAnalyzed { return fmt.Errorf("Package FilesAnalyzed is false") } totalFound, totalNotFound, foundCounts := countLicenses(pkg) wr := tabwriter.NewWriter(w, 0, 0, 2, ' ', tabwriter.AlignRight) fmt.Fprintf(wr, "%d\t License found\n", totalFound) fmt.Fprintf(wr, "%d\t License not found\n", totalNotFound) fmt.Fprintf(wr, "%d\t TOTAL\n", totalFound+totalNotFound) fmt.Fprintf(wr, "\n") var counts []struct { lic string count int } for k, v := range foundCounts { var entry struct { lic string count int } entry.lic = k entry.count = v counts = append(counts, entry) } sort.Slice(counts, func(i, j int) bool { return counts[i].count > counts[j].count }) for _, c := range counts { fmt.Fprintf(wr, "%d\t %s\n", c.count, c.lic) } fmt.Fprintf(wr, "%d\t TOTAL FOUND\n", totalFound) wr.Flush() return nil } func countLicenses(pkg *spdx.Package) (int, int, map[string]int) { if pkg == nil || pkg.Files == nil { return 0, 0, nil } totalFound := 0 totalNotFound := 0 foundCounts := map[string]int{} for _, f := range pkg.Files { if f.LicenseConcluded == "" || f.LicenseConcluded == "NOASSERTION" { totalNotFound++ } else { totalFound++ foundCounts[f.LicenseConcluded]++ } } return totalFound, totalNotFound, foundCounts } tools-golang-0.5.5/reporter/reporter_test.go000066400000000000000000000077401463371440000212320ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reporter import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx" ) func TestReporterCanMakeReportFromPackage(t *testing.T) { pkg := &spdx.Package{ FilesAnalyzed: true, Files: []*spdx.File{ {FileSPDXIdentifier: "File0", LicenseConcluded: "MIT"}, {FileSPDXIdentifier: "File1", LicenseConcluded: "NOASSERTION"}, {FileSPDXIdentifier: "File2", LicenseConcluded: "MIT"}, {FileSPDXIdentifier: "File3", LicenseConcluded: "MIT"}, {FileSPDXIdentifier: "File4", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File5", LicenseConcluded: "NOASSERTION"}, {FileSPDXIdentifier: "File6", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File7", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File8", LicenseConcluded: "MIT"}, {FileSPDXIdentifier: "File9", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File10", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File11", LicenseConcluded: "NOASSERTION"}, }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(` 9 License found 3 License not found 12 TOTAL 5 GPL-2.0-only 4 MIT 9 TOTAL FOUND `) // render as buffer of bytes var got bytes.Buffer err := Generate(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestReporterReturnsErrorIfPackageFilesNotAnalyzed(t *testing.T) { pkg := &spdx.Package{ FilesAnalyzed: false, } // render as buffer of bytes var got bytes.Buffer err := Generate(pkg, &got) if err == nil { t.Errorf("Expected non-nil error, got nil") } } // ===== 2.3 Utility functions ===== func TestCanGetCountsOfLicenses(t *testing.T) { pkg := &spdx.Package{ FilesAnalyzed: true, Files: []*spdx.File{ {FileSPDXIdentifier: "File0", LicenseConcluded: "MIT"}, {FileSPDXIdentifier: "File1", LicenseConcluded: "NOASSERTION"}, {FileSPDXIdentifier: "File2", LicenseConcluded: "MIT"}, {FileSPDXIdentifier: "File3", LicenseConcluded: "MIT"}, {FileSPDXIdentifier: "File4", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File5", LicenseConcluded: "NOASSERTION"}, {FileSPDXIdentifier: "File6", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File7", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File8", LicenseConcluded: "MIT"}, {FileSPDXIdentifier: "File9", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File10", LicenseConcluded: "GPL-2.0-only"}, {FileSPDXIdentifier: "File11", LicenseConcluded: "NOASSERTION"}, }, } totalFound, totalNotFound, foundCounts := countLicenses(pkg) if totalFound != 9 { t.Errorf("expected %v, got %v", 9, totalFound) } if totalNotFound != 3 { t.Errorf("expected %v, got %v", 3, totalNotFound) } if len(foundCounts) != 2 { t.Fatalf("expected %v, got %v", 2, len(foundCounts)) } // foundCounts is a map of license ID to count of licenses // confirm that the results are as expected if foundCounts["GPL-2.0-only"] != 5 { t.Errorf("expected %v, got %v", 5, foundCounts["GPL-2.0-only"]) } if foundCounts["MIT"] != 4 { t.Errorf("expected %v, got %v", 4, foundCounts["MIT"]) } } func TestNilPackageReturnsZeroCountsOfLicenses(t *testing.T) { totalFound, totalNotFound, foundCounts := countLicenses(nil) if totalFound != 0 { t.Errorf("expected %v, got %v", 0, totalFound) } if totalNotFound != 0 { t.Errorf("expected %v, got %v", 0, totalNotFound) } if len(foundCounts) != 0 { t.Fatalf("expected %v, got %v", 0, len(foundCounts)) } pkg := &spdx.Package{} totalFound, totalNotFound, foundCounts = countLicenses(pkg) if totalFound != 0 { t.Errorf("expected %v, got %v", 0, totalFound) } if totalNotFound != 0 { t.Errorf("expected %v, got %v", 0, totalNotFound) } if len(foundCounts) != 0 { t.Fatalf("expected %v, got %v", 0, len(foundCounts)) } } tools-golang-0.5.5/spdx/000077500000000000000000000000001463371440000151065ustar00rootroot00000000000000tools-golang-0.5.5/spdx/common/000077500000000000000000000000001463371440000163765ustar00rootroot00000000000000tools-golang-0.5.5/spdx/common/types.go000066400000000000000000000003011463371440000200630ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common // AnyDocument a placeholder for allowing any SPDX document to be used in function args type AnyDocument interface{} tools-golang-0.5.5/spdx/model.go000066400000000000000000000143541463371440000165440ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // Package spdx contains references to the latest spdx version package spdx import ( "github.com/spdx/tools-golang/spdx/v2/common" latest "github.com/spdx/tools-golang/spdx/v2/v2_3" ) const ( Version = latest.Version DataLicense = latest.DataLicense ) type ( Annotation = latest.Annotation ArtifactOfProject = latest.ArtifactOfProject CreationInfo = latest.CreationInfo Document = latest.Document ExternalDocumentRef = latest.ExternalDocumentRef File = latest.File OtherLicense = latest.OtherLicense Package = latest.Package PackageExternalReference = latest.PackageExternalReference Relationship = latest.Relationship Review = latest.Review Snippet = latest.Snippet ) type ( Annotator = common.Annotator Checksum = common.Checksum ChecksumAlgorithm = common.ChecksumAlgorithm Creator = common.Creator DocElementID = common.DocElementID ElementID = common.ElementID Originator = common.Originator PackageVerificationCode = common.PackageVerificationCode SnippetRange = common.SnippetRange SnippetRangePointer = common.SnippetRangePointer Supplier = common.Supplier ) const ( SHA224 = common.SHA224 SHA1 = common.SHA1 SHA256 = common.SHA256 SHA384 = common.SHA384 SHA512 = common.SHA512 MD2 = common.MD2 MD4 = common.MD4 MD5 = common.MD5 MD6 = common.MD6 SHA3_256 = common.SHA3_256 SHA3_384 = common.SHA3_384 SHA3_512 = common.SHA3_512 BLAKE2b_256 = common.BLAKE2b_256 BLAKE2b_384 = common.BLAKE2b_384 BLAKE2b_512 = common.BLAKE2b_512 BLAKE3 = common.BLAKE3 ADLER32 = common.ADLER32 ) const ( // F.2 Security types CategorySecurity = common.CategorySecurity SecurityCPE23Type = common.TypeSecurityCPE23Type SecurityCPE22Type = common.TypeSecurityCPE22Type SecurityAdvisory = common.TypeSecurityAdvisory SecurityFix = common.TypeSecurityFix SecurityUrl = common.TypeSecurityUrl SecuritySwid = common.TypeSecuritySwid // F.3 Package-Manager types CategoryPackageManager = common.CategoryPackageManager PackageManagerMavenCentral = common.TypePackageManagerMavenCentral PackageManagerNpm = common.TypePackageManagerNpm PackageManagerNuGet = common.TypePackageManagerNuGet PackageManagerBower = common.TypePackageManagerBower PackageManagerPURL = common.TypePackageManagerPURL // F.4 Persistent-Id types CategoryPersistentId = common.CategoryPersistentId TypePersistentIdSwh = common.TypePersistentIdSwh TypePersistentIdGitoid = common.TypePersistentIdGitoid // F.5 Other CategoryOther = common.CategoryOther // 11.1 Relationship field types RelationshipDescribes = common.TypeRelationshipDescribe RelationshipDescribedBy = common.TypeRelationshipDescribeBy RelationshipContains = common.TypeRelationshipContains RelationshipContainedBy = common.TypeRelationshipContainedBy RelationshipDependsOn = common.TypeRelationshipDependsOn RelationshipDependencyOf = common.TypeRelationshipDependencyOf RelationshipBuildDependencyOf = common.TypeRelationshipBuildDependencyOf RelationshipDevDependencyOf = common.TypeRelationshipDevDependencyOf RelationshipOptionalDependencyOf = common.TypeRelationshipOptionalDependencyOf RelationshipProvidedDependencyOf = common.TypeRelationshipProvidedDependencyOf RelationshipTestDependencyOf = common.TypeRelationshipTestDependencyOf RelationshipRuntimeDependencyOf = common.TypeRelationshipRuntimeDependencyOf RelationshipExampleOf = common.TypeRelationshipExampleOf RelationshipGenerates = common.TypeRelationshipGenerates RelationshipGeneratedFrom = common.TypeRelationshipGeneratedFrom RelationshipAncestorOf = common.TypeRelationshipAncestorOf RelationshipDescendantOf = common.TypeRelationshipDescendantOf RelationshipVariantOf = common.TypeRelationshipVariantOf RelationshipDistributionArtifact = common.TypeRelationshipDistributionArtifact RelationshipPatchFor = common.TypeRelationshipPatchFor RelationshipPatchApplied = common.TypeRelationshipPatchApplied RelationshipCopyOf = common.TypeRelationshipCopyOf RelationshipFileAdded = common.TypeRelationshipFileAdded RelationshipFileDeleted = common.TypeRelationshipFileDeleted RelationshipFileModified = common.TypeRelationshipFileModified RelationshipExpandedFromArchive = common.TypeRelationshipExpandedFromArchive RelationshipDynamicLink = common.TypeRelationshipDynamicLink RelationshipStaticLink = common.TypeRelationshipStaticLink RelationshipDataFileOf = common.TypeRelationshipDataFileOf RelationshipTestCaseOf = common.TypeRelationshipTestCaseOf RelationshipBuildToolOf = common.TypeRelationshipBuildToolOf RelationshipDevToolOf = common.TypeRelationshipDevToolOf RelationshipTestOf = common.TypeRelationshipTestOf RelationshipTestToolOf = common.TypeRelationshipTestToolOf RelationshipDocumentationOf = common.TypeRelationshipDocumentationOf RelationshipOptionalComponentOf = common.TypeRelationshipOptionalComponentOf RelationshipMetafileOf = common.TypeRelationshipMetafileOf RelationshipPackageOf = common.TypeRelationshipPackageOf RelationshipAmends = common.TypeRelationshipAmends RelationshipPrerequisiteFor = common.TypeRelationshipPrerequisiteFor RelationshipHasPrerequisite = common.TypeRelationshipHasPrerequisite RelationshipRequirementDescriptionFor = common.TypeRelationshipRequirementDescriptionFor RelationshipSpecificationFor = common.TypeRelationshipSpecificationFor RelationshipOther = common.TypeRelationshipOther ) tools-golang-0.5.5/spdx/v2/000077500000000000000000000000001463371440000154355ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/common/000077500000000000000000000000001463371440000167255ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/common/annotation.go000066400000000000000000000022711463371440000214300ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common import ( "fmt" "strings" "github.com/spdx/tools-golang/json/marshal" ) type Annotator struct { Annotator string // including AnnotatorType: one of "Person", "Organization" or "Tool" AnnotatorType string } // UnmarshalJSON takes an annotator in the typical one-line format and parses it into an Annotator struct. // This function is also used when unmarshalling YAML func (a *Annotator) UnmarshalJSON(data []byte) error { // annotator will simply be a string annotatorStr := string(data) annotatorStr = strings.Trim(annotatorStr, "\"") annotatorFields := strings.SplitN(annotatorStr, ": ", 2) if len(annotatorFields) != 2 { return fmt.Errorf("failed to parse Annotator '%s'", annotatorStr) } a.AnnotatorType = annotatorFields[0] a.Annotator = annotatorFields[1] return nil } // MarshalJSON converts the receiver into a slice of bytes representing an Annotator in string form. // This function is also used when marshalling to YAML func (a Annotator) MarshalJSON() ([]byte, error) { if a.Annotator != "" { return marshal.JSON(fmt.Sprintf("%s: %s", a.AnnotatorType, a.Annotator)) } return []byte{}, nil } tools-golang-0.5.5/spdx/v2/common/checksum.go000066400000000000000000000025671463371440000210700ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common // ChecksumAlgorithm represents the algorithm used to generate the file checksum in the Checksum struct. type ChecksumAlgorithm string // The checksum algorithms mentioned in the spec https://spdx.github.io/spdx-spec/4-file-information/#44-file-checksum const ( SHA224 ChecksumAlgorithm = "SHA224" SHA1 ChecksumAlgorithm = "SHA1" SHA256 ChecksumAlgorithm = "SHA256" SHA384 ChecksumAlgorithm = "SHA384" SHA512 ChecksumAlgorithm = "SHA512" MD2 ChecksumAlgorithm = "MD2" MD4 ChecksumAlgorithm = "MD4" MD5 ChecksumAlgorithm = "MD5" MD6 ChecksumAlgorithm = "MD6" SHA3_256 ChecksumAlgorithm = "SHA3-256" SHA3_384 ChecksumAlgorithm = "SHA3-384" SHA3_512 ChecksumAlgorithm = "SHA3-512" BLAKE2b_256 ChecksumAlgorithm = "BLAKE2b-256" BLAKE2b_384 ChecksumAlgorithm = "BLAKE2b-384" BLAKE2b_512 ChecksumAlgorithm = "BLAKE2b-512" BLAKE3 ChecksumAlgorithm = "BLAKE3" ADLER32 ChecksumAlgorithm = "ADLER32" ) // Checksum provides a unique identifier to match analysis information on each specific file in a package. // The Algorithm field describes the ChecksumAlgorithm used and the Value represents the file checksum type Checksum struct { Algorithm ChecksumAlgorithm `json:"algorithm"` Value string `json:"checksumValue"` } tools-golang-0.5.5/spdx/v2/common/creation_info.go000066400000000000000000000023311463371440000220720ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common import ( "fmt" "strings" "github.com/spdx/tools-golang/json/marshal" ) // Creator is a wrapper around the Creator SPDX field. The SPDX field contains two values, which requires special // handling in order to marshal/unmarshal it to/from Go data types. type Creator struct { Creator string // CreatorType should be one of "Person", "Organization", or "Tool" CreatorType string } // UnmarshalJSON takes an annotator in the typical one-line format and parses it into a Creator struct. // This function is also used when unmarshalling YAML func (c *Creator) UnmarshalJSON(data []byte) error { str := string(data) str = strings.Trim(str, "\"") fields := strings.SplitN(str, ": ", 2) if len(fields) != 2 { return fmt.Errorf("failed to parse Creator '%s'", str) } c.CreatorType = fields[0] c.Creator = fields[1] return nil } // MarshalJSON converts the receiver into a slice of bytes representing a Creator in string form. // This function is also used with marshalling to YAML func (c Creator) MarshalJSON() ([]byte, error) { if c.Creator != "" { return marshal.JSON(fmt.Sprintf("%s: %s", c.CreatorType, c.Creator)) } return []byte{}, nil } tools-golang-0.5.5/spdx/v2/common/external.go000066400000000000000000000076751463371440000211150ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common // Constants for various string types const ( // F.2 Security types CategorySecurity string = "SECURITY" TypeSecurityCPE23Type string = "cpe23Type" TypeSecurityCPE22Type string = "cpe22Type" TypeSecurityAdvisory string = "advisory" TypeSecurityFix string = "fix" TypeSecurityUrl string = "url" TypeSecuritySwid string = "swid" // F.3 Package-Manager types CategoryPackageManager string = "PACKAGE-MANAGER" TypePackageManagerMavenCentral string = "maven-central" TypePackageManagerNpm string = "npm" TypePackageManagerNuGet string = "nuget" TypePackageManagerBower string = "bower" TypePackageManagerPURL string = "purl" // F.4 Persistent-Id types CategoryPersistentId string = "PERSISTENT-ID" TypePersistentIdSwh string = "swh" TypePersistentIdGitoid string = "gitoid" // F.5 Other CategoryOther string = "OTHER" // 11.1 Relationship field types TypeRelationshipDescribe string = "DESCRIBES" TypeRelationshipDescribeBy string = "DESCRIBED_BY" TypeRelationshipContains string = "CONTAINS" TypeRelationshipContainedBy string = "CONTAINED_BY" TypeRelationshipDependsOn string = "DEPENDS_ON" TypeRelationshipDependencyOf string = "DEPENDENCY_OF" TypeRelationshipBuildDependencyOf string = "BUILD_DEPENDENCY_OF" TypeRelationshipDevDependencyOf string = "DEV_DEPENDENCY_OF" TypeRelationshipOptionalDependencyOf string = "OPTIONAL_DEPENDENCY_OF" TypeRelationshipProvidedDependencyOf string = "PROVIDED_DEPENDENCY_OF" TypeRelationshipTestDependencyOf string = "TEST_DEPENDENCY_OF" TypeRelationshipRuntimeDependencyOf string = "RUNTIME_DEPENDENCY_OF" TypeRelationshipExampleOf string = "EXAMPLE_OF" TypeRelationshipGenerates string = "GENERATES" TypeRelationshipGeneratedFrom string = "GENERATED_FROM" TypeRelationshipAncestorOf string = "ANCESTOR_OF" TypeRelationshipDescendantOf string = "DESCENDANT_OF" TypeRelationshipVariantOf string = "VARIANT_OF" TypeRelationshipDistributionArtifact string = "DISTRIBUTION_ARTIFACT" TypeRelationshipPatchFor string = "PATCH_FOR" TypeRelationshipPatchApplied string = "PATCH_APPLIED" TypeRelationshipCopyOf string = "COPY_OF" TypeRelationshipFileAdded string = "FILE_ADDED" TypeRelationshipFileDeleted string = "FILE_DELETED" TypeRelationshipFileModified string = "FILE_MODIFIED" TypeRelationshipExpandedFromArchive string = "EXPANDED_FROM_ARCHIVE" TypeRelationshipDynamicLink string = "DYNAMIC_LINK" TypeRelationshipStaticLink string = "STATIC_LINK" TypeRelationshipDataFileOf string = "DATA_FILE_OF" TypeRelationshipTestCaseOf string = "TEST_CASE_OF" TypeRelationshipBuildToolOf string = "BUILD_TOOL_OF" TypeRelationshipDevToolOf string = "DEV_TOOL_OF" TypeRelationshipTestOf string = "TEST_OF" TypeRelationshipTestToolOf string = "TEST_TOOL_OF" TypeRelationshipDocumentationOf string = "DOCUMENTATION_OF" TypeRelationshipOptionalComponentOf string = "OPTIONAL_COMPONENT_OF" TypeRelationshipMetafileOf string = "METAFILE_OF" TypeRelationshipPackageOf string = "PACKAGE_OF" TypeRelationshipAmends string = "AMENDS" TypeRelationshipPrerequisiteFor string = "PREREQUISITE_FOR" TypeRelationshipHasPrerequisite string = "HAS_PREREQUISITE" TypeRelationshipRequirementDescriptionFor string = "REQUIREMENT_DESCRIPTION_FOR" TypeRelationshipSpecificationFor string = "SPECIFICATION_FOR" TypeRelationshipOther string = "OTHER" ) tools-golang-0.5.5/spdx/v2/common/identifier.go000066400000000000000000000131411463371440000213760ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common import ( "fmt" "strings" "github.com/spdx/tools-golang/json/marshal" ) const ( spdxRefPrefix = "SPDXRef-" documentRefPrefix = "DocumentRef-" ) // ElementID represents the identifier string portion of an SPDX element // identifier. DocElementID should be used for any attributes which can // contain identifiers defined in a different SPDX document. // ElementIDs should NOT contain the mandatory 'SPDXRef-' portion. type ElementID string // MarshalJSON returns an SPDXRef- prefixed JSON string func (d ElementID) MarshalJSON() ([]byte, error) { return marshal.JSON(prefixElementId(d)) } // UnmarshalJSON validates SPDXRef- prefixes and removes them when processing ElementIDs func (d *ElementID) UnmarshalJSON(data []byte) error { // SPDX identifier will simply be a string idStr := string(data) idStr = strings.Trim(idStr, "\"") e, err := trimElementIdPrefix(idStr) if err != nil { return err } *d = e return nil } // prefixElementId adds the SPDXRef- prefix to an element ID if it does not have one func prefixElementId(id ElementID) string { val := string(id) if !strings.HasPrefix(val, spdxRefPrefix) { return spdxRefPrefix + val } return val } // trimElementIdPrefix removes the SPDXRef- prefix from an element ID string or returns an error if it // does not start with SPDXRef- func trimElementIdPrefix(id string) (ElementID, error) { // handle SPDXRef- idFields := strings.SplitN(id, spdxRefPrefix, 2) if len(idFields) != 2 { return "", fmt.Errorf("failed to parse SPDX identifier '%s'", id) } e := ElementID(idFields[1]) return e, nil } // DocElementID represents an SPDX element identifier that could be defined // in a different SPDX document, and therefore could have a "DocumentRef-" // portion, such as Relationships and Annotations. // ElementID is used for attributes in which a "DocumentRef-" portion cannot // appear, such as a Package or File definition (since it is necessarily // being defined in the present document). // DocumentRefID will be the empty string for elements defined in the // present document. // DocElementIDs should NOT contain the mandatory 'DocumentRef-' or // 'SPDXRef-' portions. // SpecialID is used ONLY if the DocElementID matches a defined set of // permitted special values for a particular field, e.g. "NONE" or // "NOASSERTION" for the right-hand side of Relationships. If SpecialID // is set, DocumentRefID and ElementRefID should be empty (and vice versa). type DocElementID struct { DocumentRefID string ElementRefID ElementID SpecialID string } // MarshalJSON converts the receiver into a slice of bytes representing a DocElementID in string form. // This function is also used when marshalling to YAML func (d DocElementID) MarshalJSON() ([]byte, error) { if d.DocumentRefID != "" && d.ElementRefID != "" { idStr := prefixElementId(d.ElementRefID) return marshal.JSON(fmt.Sprintf("%s%s:%s", documentRefPrefix, d.DocumentRefID, idStr)) } else if d.ElementRefID != "" { return marshal.JSON(prefixElementId(d.ElementRefID)) } else if d.SpecialID != "" { return marshal.JSON(d.SpecialID) } return []byte{}, fmt.Errorf("failed to marshal empty DocElementID") } // UnmarshalJSON takes a SPDX Identifier string parses it into a DocElementID struct. // This function is also used when unmarshalling YAML func (d *DocElementID) UnmarshalJSON(data []byte) (err error) { // SPDX identifier will simply be a string idStr := string(data) idStr = strings.Trim(idStr, "\"") // handle special cases if idStr == "NONE" || idStr == "NOASSERTION" { d.SpecialID = idStr return nil } var idFields []string // handle DocumentRef- if present if strings.HasPrefix(idStr, documentRefPrefix) { // strip out the "DocumentRef-" so we can get the value idFields = strings.SplitN(idStr, documentRefPrefix, 2) idStr = idFields[1] // an SPDXRef can appear after a DocumentRef, separated by a colon idFields = strings.SplitN(idStr, ":", 2) d.DocumentRefID = idFields[0] if len(idFields) == 2 { idStr = idFields[1] } else { return nil } } d.ElementRefID, err = trimElementIdPrefix(idStr) return err } // TODO: add equivalents for LicenseRef- identifiers // MakeDocElementID takes strings (without prefixes) for the DocumentRef- // and SPDXRef- identifiers, and returns a DocElementID. An empty string // should be used for the DocumentRef- portion if it is referring to the // present document. func MakeDocElementID(docRef string, eltRef string) DocElementID { return DocElementID{ DocumentRefID: docRef, ElementRefID: ElementID(eltRef), } } // MakeDocElementSpecial takes a "special" string (e.g. "NONE" or // "NOASSERTION" for the right side of a Relationship), nd returns // a DocElementID with it in the SpecialID field. Other fields will // be empty. func MakeDocElementSpecial(specialID string) DocElementID { return DocElementID{SpecialID: specialID} } // RenderElementID takes an ElementID and returns the string equivalent, // with the SPDXRef- prefix reinserted. func RenderElementID(eID ElementID) string { return spdxRefPrefix + string(eID) } // RenderDocElementID takes a DocElementID and returns the string equivalent, // with the SPDXRef- prefix (and, if applicable, the DocumentRef- prefix) // reinserted. If a SpecialID is present, it will be rendered verbatim and // DocumentRefID and ElementRefID will be ignored. func RenderDocElementID(deID DocElementID) string { if deID.SpecialID != "" { return deID.SpecialID } prefix := "" if deID.DocumentRefID != "" { prefix = documentRefPrefix + deID.DocumentRefID + ":" } return prefix + spdxRefPrefix + string(deID.ElementRefID) } tools-golang-0.5.5/spdx/v2/common/identifier_test.go000066400000000000000000000145661463371440000224510ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common import ( "encoding/json" "fmt" "reflect" "strings" "testing" "github.com/spdx/tools-golang/json/marshal" ) func Test_DocElementIDEncoding(t *testing.T) { tests := []struct { name string value DocElementID expected string err bool }{ { name: "ElementRefID", value: DocElementID{ ElementRefID: "some-id", }, expected: "SPDXRef-some-id", }, { name: "DocumentRefID:ElementRefID", value: DocElementID{ DocumentRefID: "a-doc", ElementRefID: "some-id", }, expected: "DocumentRef-a-doc:SPDXRef-some-id", }, { name: "DocumentRefID no ElementRefID", value: DocElementID{ DocumentRefID: "a-doc", }, err: true, }, { name: "SpecialID", value: DocElementID{ SpecialID: "special-id", }, expected: "special-id", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result, err := marshal.JSON(test.value) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) case test.err && err == nil: t.Fatalf("expected error but got none") case test.err: return } s := string(result) if !strings.HasPrefix(s, `"`) || !strings.HasSuffix(s, `"`) { t.Fatalf("string was not returned: %s", s) } s = strings.Trim(s, `"`) if test.expected != s { t.Fatalf("%s != %s", test.expected, s) } }) } } func Test_DocElementIDDecoding(t *testing.T) { tests := []struct { name string value string expected DocElementID err bool }{ { name: "ElementRefID", value: "SPDXRef-some-id", expected: DocElementID{ ElementRefID: "some-id", }, }, { name: "DocumentRefID:ElementRefID", value: "DocumentRef-a-doc:SPDXRef-some-id", expected: DocElementID{ DocumentRefID: "a-doc", ElementRefID: "some-id", }, }, { name: "DocumentRefID no ElementRefID", value: "DocumentRef-a-doc", expected: DocElementID{ DocumentRefID: "a-doc", }, }, { name: "DocumentRefID invalid ElementRefID", value: "DocumentRef-a-doc:invalid", err: true, }, { name: "invalid format", value: "some-id-without-spdxref", err: true, }, { name: "SpecialID NONE", value: "NONE", expected: DocElementID{ SpecialID: "NONE", }, }, { name: "SpecialID NOASSERTION", value: "NOASSERTION", expected: DocElementID{ SpecialID: "NOASSERTION", }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { out := DocElementID{} s := fmt.Sprintf(`"%s"`, test.value) err := json.Unmarshal([]byte(s), &out) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) case test.err && err == nil: t.Fatalf("expected error but got none") case test.err: return } if !reflect.DeepEqual(test.expected, out) { t.Fatalf("unexpected value: %v != %v", test.expected, out) } }) } } func Test_ElementIDEncoding(t *testing.T) { tests := []struct { name string value ElementID expected string err bool }{ { name: "appends spdxref", value: ElementID("some-id"), expected: "SPDXRef-some-id", }, { name: "appends spdxref", value: ElementID("SPDXRef-some-id"), expected: "SPDXRef-some-id", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result, err := marshal.JSON(test.value) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) case test.err && err == nil: t.Fatalf("expected error but got none") case test.err: return } s := string(result) if !strings.HasPrefix(s, `"`) || !strings.HasSuffix(s, `"`) { t.Fatalf("string was not returned: %s", s) } s = strings.Trim(s, `"`) if test.expected != s { t.Fatalf("%s != %s", test.expected, s) } }) } } func Test_ElementIDDecoding(t *testing.T) { tests := []struct { name string value string expected ElementID err bool }{ { name: "valid id", value: "SPDXRef-some-id", expected: ElementID("some-id"), }, { name: "invalid format", value: "some-id-without-spdxref", err: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { var out ElementID s := fmt.Sprintf(`"%s"`, test.value) err := json.Unmarshal([]byte(s), &out) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) case test.err && err == nil: t.Fatalf("expected error but got none") case test.err: return } if !reflect.DeepEqual(test.expected, out) { t.Fatalf("unexpected value: %v != %v", test.expected, out) } }) } } func Test_ElementIDStructEncoding(t *testing.T) { type typ struct { Id ElementID `json:"id"` } tests := []struct { name string value typ expected string err bool }{ { name: "appends spdxref", value: typ{ Id: ElementID("some-id"), }, expected: `{"id":"SPDXRef-some-id"}`, }, { name: "appends spdxref", value: typ{ Id: ElementID("SPDXRef-some-id"), }, expected: `{"id":"SPDXRef-some-id"}`, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result, err := marshal.JSON(test.value) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) case test.err && err == nil: t.Fatalf("expected error but got none") case test.err: return } s := string(result) if test.expected != s { t.Fatalf("%s != %s", test.expected, s) } }) } } func Test_ElementIDStructDecoding(t *testing.T) { type typ struct { Id ElementID `json:"id"` } tests := []struct { name string value string expected typ err bool }{ { name: "valid id", expected: typ{ Id: ElementID("some-id"), }, value: `{"id":"SPDXRef-some-id"}`, }, { name: "invalid format", value: `{"id":"some-id"}`, err: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { out := typ{} err := json.Unmarshal([]byte(test.value), &out) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) case test.err && err == nil: t.Fatalf("expected error but got none") case test.err: return } if !reflect.DeepEqual(test.expected, out) { t.Fatalf("unexpected value: %v != %v", test.expected, out) } }) } } tools-golang-0.5.5/spdx/v2/common/package.go000066400000000000000000000064571463371440000206630ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common import ( "fmt" "strings" "github.com/spdx/tools-golang/json/marshal" ) type Supplier struct { // can be "NOASSERTION" Supplier string // SupplierType can be one of "Person", "Organization", or empty if Supplier is "NOASSERTION" SupplierType string } // UnmarshalJSON takes a supplier in the typical one-line format and parses it into a Supplier struct. // This function is also used when unmarshalling YAML func (s *Supplier) UnmarshalJSON(data []byte) error { // the value is just a string presented as a slice of bytes supplierStr := string(data) supplierStr = strings.Trim(supplierStr, "\"") if supplierStr == "NOASSERTION" { s.Supplier = supplierStr return nil } supplierFields := strings.SplitN(supplierStr, ": ", 2) if len(supplierFields) != 2 { return fmt.Errorf("failed to parse Supplier '%s'", supplierStr) } s.SupplierType = supplierFields[0] s.Supplier = supplierFields[1] return nil } // MarshalJSON converts the receiver into a slice of bytes representing a Supplier in string form. // This function is also used when marshalling to YAML func (s Supplier) MarshalJSON() ([]byte, error) { if s.Supplier == "NOASSERTION" { return marshal.JSON(s.Supplier) } else if s.SupplierType != "" && s.Supplier != "" { return marshal.JSON(fmt.Sprintf("%s: %s", s.SupplierType, s.Supplier)) } return []byte{}, fmt.Errorf("failed to marshal invalid Supplier: %+v", s) } type Originator struct { // can be "NOASSERTION" Originator string // OriginatorType can be one of "Person", "Organization", or empty if Originator is "NOASSERTION" OriginatorType string } // UnmarshalJSON takes an originator in the typical one-line format and parses it into an Originator struct. // This function is also used when unmarshalling YAML func (o *Originator) UnmarshalJSON(data []byte) error { // the value is just a string presented as a slice of bytes originatorStr := string(data) originatorStr = strings.Trim(originatorStr, "\"") if originatorStr == "NOASSERTION" { o.Originator = originatorStr return nil } originatorFields := strings.SplitN(originatorStr, ":", 2) if len(originatorFields) != 2 { return fmt.Errorf("failed to parse Originator '%s'", originatorStr) } o.OriginatorType = originatorFields[0] o.Originator = strings.TrimLeft(originatorFields[1], " \t") return nil } // MarshalJSON converts the receiver into a slice of bytes representing an Originator in string form. // This function is also used when marshalling to YAML func (o Originator) MarshalJSON() ([]byte, error) { if o.Originator == "NOASSERTION" { return marshal.JSON(o.Originator) } else if o.OriginatorType != "" && o.Originator != "" { return marshal.JSON(fmt.Sprintf("%s: %s", o.OriginatorType, o.Originator)) } return []byte{}, fmt.Errorf("failed to marshal invalid Originator: %+v", o) } type PackageVerificationCode struct { // Cardinality: mandatory, one if filesAnalyzed is true / omitted; // zero (must be omitted) if filesAnalyzed is false Value string `json:"packageVerificationCodeValue"` // Spec also allows specifying files to exclude from the // verification code algorithm; intended to enable exclusion of // the SPDX document file itself. ExcludedFiles []string `json:"packageVerificationCodeExcludedFiles,omitempty"` } tools-golang-0.5.5/spdx/v2/common/package_test.go000066400000000000000000000034401463371440000217070ustar00rootroot00000000000000package common import "testing" func TestOriginator_UnmarshalJSON(t *testing.T) { tt := []struct { name string data []byte wantErr bool }{ { name: "valid originator", data: []byte("\"Person: John Doe\""), wantErr: false, }, { name: "valid originator with no space", data: []byte("\"Person:John Doe\""), wantErr: false, }, { name: "valid originator with no space - organization", data: []byte("\"Organization:SPDX\""), wantErr: false, }, { name: "valid originator with email", data: []byte("\"Organization: ExampleCodeInspect (contact@example.com)\""), wantErr: false, }, { name: "invalid originator with no type", data: []byte("\"John Doe\""), wantErr: true, }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { var o Originator err := o.UnmarshalJSON(tc.data) if (err != nil) != tc.wantErr { t.Errorf("Originator.UnmarshalJSON() error = %v, wantErr %v", err, tc.wantErr) } }) } } func TestOriginator_MarshalJSON(t *testing.T) { type mock struct { *Originator } tt := []struct { name string data Originator wantErr bool }{ { name: "valid originator", data: Originator{ Originator: "John Doe", OriginatorType: "Person", }, wantErr: false, }, { name: "originator with no type", data: Originator{ Originator: "John Doe", }, wantErr: true, }, { name: "invalid originator with type but no entity", data: Originator{ OriginatorType: "Person", }, wantErr: true, }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { _, err := tc.data.MarshalJSON() if (err != nil) != tc.wantErr { t.Errorf("Originator.MarshalJSON() error = %v, wantErr %v", err, tc.wantErr) } }) } } tools-golang-0.5.5/spdx/v2/common/snippet.go000066400000000000000000000010541463371440000207360ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package common type SnippetRangePointer struct { // 5.3: Snippet Byte Range: [start byte]:[end byte] // Cardinality: mandatory, one Offset int `json:"offset,omitempty"` // 5.4: Snippet Line Range: [start line]:[end line] // Cardinality: optional, one LineNumber int `json:"lineNumber,omitempty"` FileSPDXIdentifier ElementID `json:"reference"` } type SnippetRange struct { StartPointer SnippetRangePointer `json:"startPointer"` EndPointer SnippetRangePointer `json:"endPointer"` } tools-golang-0.5.5/spdx/v2/v2_1/000077500000000000000000000000001463371440000162045ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_1/annotation.go000066400000000000000000000021711463371440000207060ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Annotation is an Annotation section of an SPDX Document for version 2.1 of the spec. type Annotation struct { // 8.1: Annotator // Cardinality: conditional (mandatory, one) if there is an Annotation Annotator common.Annotator `json:"annotator"` // 8.2: Annotation Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationDate string `json:"annotationDate"` // 8.3: Annotation Type: "REVIEW" or "OTHER" // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationType string `json:"annotationType"` // 8.4: SPDX Identifier Reference // Cardinality: conditional (mandatory, one) if there is an Annotation // This field is not used in hierarchical data formats where the referenced element is clear, such as JSON or YAML. AnnotationSPDXIdentifier common.DocElementID `json:"-"` // 8.5: Annotation Comment // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationComment string `json:"comment"` } tools-golang-0.5.5/spdx/v2/v2_1/creation_info.go000066400000000000000000000014361463371440000213560ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // CreationInfo is a Document Creation Information section of an // SPDX Document for version 2.1 of the spec. type CreationInfo struct { // 2.7: License List Version // Cardinality: optional, one LicenseListVersion string `json:"licenseListVersion,omitempty"` // 2.8: Creators: may have multiple keys for Person, Organization // and/or Tool // Cardinality: mandatory, one or many Creators []common.Creator `json:"creators"` // 2.9: Created: data format YYYY-MM-DDThh:mm:ssZ // Cardinality: mandatory, one Created string `json:"created"` // 2.10: Creator Comment // Cardinality: optional, one CreatorComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_1/document.go000066400000000000000000000050621463371440000203540ustar00rootroot00000000000000// Package spdx contains the struct definition for an SPDX Document // and its constituent parts. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 import ( converter "github.com/anchore/go-struct-converter" "github.com/spdx/tools-golang/spdx/v2/common" ) const Version = "SPDX-2.1" const DataLicense = "CC0-1.0" // ExternalDocumentRef is a reference to an external SPDX document // as defined in section 2.6 for version 2.1 of the spec. type ExternalDocumentRef struct { // DocumentRefID is the ID string defined in the start of the // reference. It should _not_ contain the "DocumentRef-" part // of the mandatory ID string. DocumentRefID string `json:"externalDocumentId"` // URI is the URI defined for the external document URI string `json:"spdxDocument"` // Checksum is the actual hash data Checksum common.Checksum `json:"checksum"` } // Document is an SPDX Document for version 2.1 of the spec. // See https://spdx.org/sites/cpstandard/files/pages/files/spdxversion2.1.pdf type Document struct { // 2.1: SPDX Version; should be in the format "SPDX-2.1" // Cardinality: mandatory, one SPDXVersion string `json:"spdxVersion"` // 2.2: Data License; should be "CC0-1.0" // Cardinality: mandatory, one DataLicense string `json:"dataLicense"` // 2.3: SPDX Identifier; should be "DOCUMENT" to represent // mandatory identifier of SPDXRef-DOCUMENT // Cardinality: mandatory, one SPDXIdentifier common.ElementID `json:"SPDXID"` // 2.4: Document Name // Cardinality: mandatory, one DocumentName string `json:"name"` // 2.5: Document Namespace // Cardinality: mandatory, one DocumentNamespace string `json:"documentNamespace"` // 2.6: External Document References // Cardinality: optional, one or many ExternalDocumentReferences []ExternalDocumentRef `json:"externalDocumentRefs,omitempty"` // 2.11: Document Comment // Cardinality: optional, one DocumentComment string `json:"comment,omitempty"` CreationInfo *CreationInfo `json:"creationInfo"` Packages []*Package `json:"packages,omitempty"` Files []*File `json:"files,omitempty"` OtherLicenses []*OtherLicense `json:"hasExtractedLicensingInfos,omitempty"` Relationships []*Relationship `json:"relationships,omitempty"` Annotations []*Annotation `json:"annotations,omitempty"` Snippets []Snippet `json:"snippets,omitempty"` // DEPRECATED in version 2.0 of spec Reviews []*Review `json:"-"` } func (d *Document) ConvertFrom(_ interface{}) error { d.SPDXVersion = Version return nil } var _ converter.ConvertFrom = (*Document)(nil) tools-golang-0.5.5/spdx/v2/v2_1/file.go000066400000000000000000000057041463371440000174600ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // File is a File section of an SPDX Document for version 2.1 of the spec. type File struct { // 4.1: File Name // Cardinality: mandatory, one FileName string `json:"fileName"` // 4.2: File SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one FileSPDXIdentifier common.ElementID `json:"SPDXID"` // 4.3: File Types // Cardinality: optional, multiple FileTypes []string `json:"fileTypes,omitempty"` // 4.4: File Checksum: may have keys for SHA1, SHA256 and/or MD5 // Cardinality: mandatory, one SHA1, others may be optionally provided Checksums []common.Checksum `json:"checksums"` // 4.5: Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one LicenseConcluded string `json:"licenseConcluded"` // 4.6: License Information in File: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one or many LicenseInfoInFiles []string `json:"licenseInfoInFiles"` // 4.7: Comments on License // Cardinality: optional, one LicenseComments string `json:"licenseComments,omitempty"` // 4.8: Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: mandatory, one FileCopyrightText string `json:"copyrightText"` // DEPRECATED in version 2.1 of spec // 4.9-4.11: Artifact of Project variables (defined below) // Cardinality: optional, one or many ArtifactOfProjects []*ArtifactOfProject `json:"-"` // 4.12: File Comment // Cardinality: optional, one FileComment string `json:"comment,omitempty"` // 4.13: File Notice // Cardinality: optional, one FileNotice string `json:"noticeText,omitempty"` // 4.14: File Contributor // Cardinality: optional, one or many FileContributors []string `json:"fileContributors,omitempty"` // DEPRECATED in version 2.0 of spec // 4.15: File Dependencies // Cardinality: optional, one or many FileDependencies []string `json:"-"` // Snippets contained in this File // Note that Snippets could be defined in a different Document! However, // the only ones that _THIS_ document can contain are the ones that are // defined here -- so this should just be an ElementID. Snippets map[common.ElementID]*Snippet `json:"-"` Annotations []Annotation `json:"annotations,omitempty"` } // ArtifactOfProject is a DEPRECATED collection of data regarding // a Package, as defined in sections 4.9-4.11 in version 2.1 of the spec. type ArtifactOfProject struct { // DEPRECATED in version 2.1 of spec // 4.9: Artifact of Project Name // Cardinality: conditional, required if present, one per AOP Name string // DEPRECATED in version 2.1 of spec // 4.10: Artifact of Project Homepage: URL or "UNKNOWN" // Cardinality: optional, one per AOP HomePage string // DEPRECATED in version 2.1 of spec // 4.11: Artifact of Project Uniform Resource Identifier // Cardinality: optional, one per AOP URI string } tools-golang-0.5.5/spdx/v2/v2_1/other_license.go000066400000000000000000000021201463371440000213510ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 // OtherLicense is an Other License Information section of an // SPDX Document for version 2.1 of the spec. type OtherLicense struct { // 6.1: License Identifier: "LicenseRef-[idstring]" // Cardinality: conditional (mandatory, one) if license is not // on SPDX License List LicenseIdentifier string `json:"licenseId"` // 6.2: Extracted Text // Cardinality: conditional (mandatory, one) if there is a // License Identifier assigned ExtractedText string `json:"extractedText"` // 6.3: License Name: single line of text or "NOASSERTION" // Cardinality: conditional (mandatory, one) if license is not // on SPDX License List LicenseName string `json:"name,omitempty"` // 6.4: License Cross Reference // Cardinality: conditional (optional, one or many) if license // is not on SPDX License List LicenseCrossReferences []string `json:"seeAlsos,omitempty"` // 6.5: License Comment // Cardinality: optional, one LicenseComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_1/package.go000066400000000000000000000104761463371440000201360ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Package is a Package section of an SPDX Document for version 2.1 of the spec. type Package struct { // 3.1: Package Name // Cardinality: mandatory, one PackageName string `json:"name"` // 3.2: Package SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one PackageSPDXIdentifier common.ElementID `json:"SPDXID"` // 3.3: Package Version // Cardinality: optional, one PackageVersion string `json:"versionInfo,omitempty"` // 3.4: Package File Name // Cardinality: optional, one PackageFileName string `json:"packageFileName,omitempty"` // 3.5: Package Supplier: may have single result for either Person or Organization, // or NOASSERTION // Cardinality: optional, one PackageSupplier *common.Supplier `json:"supplier,omitempty"` // 3.6: Package Originator: may have single result for either Person or Organization, // or NOASSERTION // Cardinality: optional, one PackageOriginator *common.Originator `json:"originator,omitempty"` // 3.7: Package Download Location // Cardinality: mandatory, one PackageDownloadLocation string `json:"downloadLocation"` // 3.8: FilesAnalyzed // Cardinality: optional, one; default value is "true" if omitted FilesAnalyzed bool `json:"filesAnalyzed,omitempty"` // NOT PART OF SPEC: did FilesAnalyzed tag appear? IsFilesAnalyzedTagPresent bool `json:"-"` // 3.9: Package Verification Code PackageVerificationCode common.PackageVerificationCode `json:"packageVerificationCode,omitempty"` // 3.10: Package Checksum: may have keys for SHA1, SHA256 and/or MD5 // Cardinality: optional, one or many PackageChecksums []common.Checksum `json:"checksums,omitempty"` // 3.11: Package Home Page // Cardinality: optional, one PackageHomePage string `json:"homepage,omitempty"` // 3.12: Source Information // Cardinality: optional, one PackageSourceInfo string `json:"sourceInfo,omitempty"` // 3.13: Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one PackageLicenseConcluded string `json:"licenseConcluded"` // 3.14: All Licenses Info from Files: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one or many if filesAnalyzed is true / omitted; // zero (must be omitted) if filesAnalyzed is false PackageLicenseInfoFromFiles []string `json:"licenseInfoFromFiles"` // 3.15: Declared License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one PackageLicenseDeclared string `json:"licenseDeclared"` // 3.16: Comments on License // Cardinality: optional, one PackageLicenseComments string `json:"licenseComments,omitempty"` // 3.17: Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: mandatory, one PackageCopyrightText string `json:"copyrightText"` // 3.18: Package Summary Description // Cardinality: optional, one PackageSummary string `json:"summary,omitempty"` // 3.19: Package Detailed Description // Cardinality: optional, one PackageDescription string `json:"description,omitempty"` // 3.20: Package Comment // Cardinality: optional, one PackageComment string `json:"comment,omitempty"` // 3.21: Package External Reference // Cardinality: optional, one or many PackageExternalReferences []*PackageExternalReference `json:"externalRefs,omitempty"` // Files contained in this Package Files []*File `json:"files,omitempty"` Annotations []Annotation `json:"annotations,omitempty"` } // PackageExternalReference is an External Reference to additional info // about a Package, as defined in section 3.21 in version 2.1 of the spec. type PackageExternalReference struct { // category is "SECURITY", "PACKAGE-MANAGER" or "OTHER" Category string `json:"referenceCategory"` // type is an [idstring] as defined in Appendix VI; // called RefType here due to "type" being a Golang keyword RefType string `json:"referenceType"` // locator is a unique string to access the package-specific // info, metadata or content within the target location Locator string `json:"referenceLocator"` // 3.22: Package External Reference Comment // Cardinality: conditional (optional, one) for each External Reference ExternalRefComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_1/relationship.go000066400000000000000000000014241463371440000212350ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Relationship is a Relationship section of an SPDX Document for // version 2.1 of the spec. type Relationship struct { // 7.1: Relationship // Cardinality: optional, one or more; one per Relationship // one mandatory for SPDX Document with multiple packages // RefA and RefB are first and second item // Relationship is type from 7.1.1 RefA common.DocElementID `json:"spdxElementId"` RefB common.DocElementID `json:"relatedSpdxElement"` Relationship string `json:"relationshipType"` // 7.2: Relationship Comment // Cardinality: optional, one RelationshipComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_1/review.go000066400000000000000000000013321463371440000200330ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 // Review is a Review section of an SPDX Document for version 2.1 of the spec. // DEPRECATED in version 2.0 of spec; retained here for compatibility. type Review struct { // DEPRECATED in version 2.0 of spec // 9.1: Reviewer // Cardinality: optional, one Reviewer string // including AnnotatorType: one of "Person", "Organization" or "Tool" ReviewerType string // DEPRECATED in version 2.0 of spec // 9.2: Review Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: conditional (mandatory, one) if there is a Reviewer ReviewDate string // DEPRECATED in version 2.0 of spec // 9.3: Review Comment // Cardinality: optional, one ReviewComment string } tools-golang-0.5.5/spdx/v2/v2_1/snippet.go000066400000000000000000000030271463371440000202170ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_1 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Snippet is a Snippet section of an SPDX Document for version 2.1 of the spec. type Snippet struct { // 5.1: Snippet SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one SnippetSPDXIdentifier common.ElementID `json:"SPDXID"` // 5.2: Snippet from File SPDX Identifier // Cardinality: mandatory, one SnippetFromFileSPDXIdentifier common.ElementID `json:"snippetFromFile"` // Ranges denotes the start/end byte offsets or line numbers that the snippet is relevant to Ranges []common.SnippetRange `json:"ranges"` // 5.5: Snippet Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one SnippetLicenseConcluded string `json:"licenseConcluded"` // 5.6: License Information in Snippet: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one or many LicenseInfoInSnippet []string `json:"licenseInfoInSnippets,omitempty"` // 5.7: Snippet Comments on License // Cardinality: optional, one SnippetLicenseComments string `json:"licenseComments,omitempty"` // 5.8: Snippet Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: mandatory, one SnippetCopyrightText string `json:"copyrightText"` // 5.9: Snippet Comment // Cardinality: optional, one SnippetComment string `json:"comment,omitempty"` // 5.10: Snippet Name // Cardinality: optional, one SnippetName string `json:"name,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/000077500000000000000000000000001463371440000200145ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/000077500000000000000000000000001463371440000212565ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_annotation.go000066400000000000000000000020531463371440000251510ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" ) func (parser *tvParser) parsePairForAnnotation(tag string, value string) error { if parser.ann == nil { return fmt.Errorf("no annotation struct created in parser ann pointer") } switch tag { case "Annotator": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if subkey == "Person" || subkey == "Organization" || subkey == "Tool" { parser.ann.Annotator.AnnotatorType = subkey parser.ann.Annotator.Annotator = subvalue return nil } return fmt.Errorf("unrecognized Annotator type %v", subkey) case "AnnotationDate": parser.ann.AnnotationDate = value case "AnnotationType": parser.ann.AnnotationType = value case "SPDXREF": deID, err := extractDocElementID(value) if err != nil { return err } parser.ann.AnnotationSPDXIdentifier = deID case "AnnotationComment": parser.ann.AnnotationComment = value default: return fmt.Errorf("received unknown tag %v in Annotation section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_annotation_test.go000066400000000000000000000107731463371440000262200ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Annotation section tests ===== func TestParserFailsIfAnnotationNotSet(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairForAnnotation("Annotator", "Person: John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected error when calling parsePairFromAnnotation without setting ann pointer") } } func TestParserFailsIfAnnotationTagUnknown(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // start with valid annotator err := parser.parsePair("Annotator", "Person: John Doe (jdoe@example.com)") if err != nil { t.Errorf("expected nil error, got %v", err) } // parse invalid tag, using parsePairForAnnotation( err = parser.parsePairForAnnotation("blah", "oops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotationFieldsWithoutAnnotation(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("AnnotationDate", "2018-09-15T17:25:00Z") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationDate without Annotator first") } err = parser.parsePair("AnnotationType", "REVIEW") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationType without Annotator first") } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err == nil { t.Errorf("expected error when calling parsePair for SPDXREF without Annotator first") } err = parser.parsePair("AnnotationComment", "comment whatever") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationComment without Annotator first") } } func TestParserCanParseAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Annotator without email address err := parser.parsePair("Annotator", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.Annotator.Annotator != "John Doe" { t.Errorf("got %v for Annotator, expected John Doe", parser.ann.Annotator) } if parser.ann.Annotator.AnnotatorType != "Person" { t.Errorf("got %v for AnnotatorType, expected Person", parser.ann.Annotator.AnnotatorType) } // Annotation Date dt := "2018-09-15T17:32:00Z" err = parser.parsePair("AnnotationDate", dt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationDate != dt { t.Errorf("got %v for AnnotationDate, expected %v", parser.ann.AnnotationDate, dt) } // Annotation type aType := "REVIEW" err = parser.parsePair("AnnotationType", aType) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationType != aType { t.Errorf("got %v for AnnotationType, expected %v", parser.ann.AnnotationType, aType) } // SPDX Identifier Reference ref := "SPDXRef-30" err = parser.parsePair("SPDXREF", ref) if err != nil { t.Errorf("expected nil error, got %v", err) } deID := parser.ann.AnnotationSPDXIdentifier if deID.DocumentRefID != "" || deID.ElementRefID != "30" { t.Errorf("got %v for SPDXREF, expected %v", parser.ann.AnnotationSPDXIdentifier, "30") } // Annotation Comment cmt := "this is a comment" err = parser.parsePair("AnnotationComment", cmt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationComment != cmt { t.Errorf("got %v for AnnotationComment, expected %v", parser.ann.AnnotationComment, cmt) } } func TestParserFailsIfAnnotatorInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotatorTypeInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Human: John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotationRefInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // start with valid annotator err := parser.parsePair("Annotator", "Person: John Doe (jdoe@example.com)") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePair("SPDXREF", "blah:other") if err == nil { t.Errorf("expected non-nil error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_creation_info.go000066400000000000000000000116161463371440000256230ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func (parser *tvParser) parsePairFromCreationInfo(tag string, value string) error { // fail if not in Creation Info parser state if parser.st != psCreationInfo { return fmt.Errorf("got invalid state %v in parsePairFromCreationInfo", parser.st) } // create an SPDX Creation Info data struct if we don't have one already if parser.doc.CreationInfo == nil { parser.doc.CreationInfo = &v2_1.CreationInfo{} } ci := parser.doc.CreationInfo switch tag { case "LicenseListVersion": ci.LicenseListVersion = value case "Creator": subkey, subvalue, err := extractSubs(value) if err != nil { return err } creator := common.Creator{Creator: subvalue} switch subkey { case "Person", "Organization", "Tool": creator.CreatorType = subkey default: return fmt.Errorf("unrecognized Creator type %v", subkey) } ci.Creators = append(ci.Creators, creator) case "Created": ci.Created = value case "CreatorComment": ci.CreatorComment = value // tag for going on to package section case "PackageName": // error if last file does not have an identifier // this may be a null case: can we ever have a "last file" in // the "creation info" state? should go on to "file" state // even when parsing unpackaged files. if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.st = psPackage parser.pkg = &v2_1.Package{ FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, } return parser.parsePairFromPackage(tag, value) // tag for going on to _unpackaged_ file section case "FileName": // leave pkg as nil, so that packages will be placed in Files parser.st = psFile parser.pkg = nil return parser.parsePairFromFile(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_1.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_1.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) default: return fmt.Errorf("received unknown tag %v in CreationInfo section", tag) } return nil } // ===== Helper functions ===== func extractExternalDocumentReference(value string) (string, string, string, string, error) { sp := strings.Split(value, " ") // remove any that are just whitespace keepSp := []string{} for _, s := range sp { ss := strings.TrimSpace(s) if ss != "" { keepSp = append(keepSp, ss) } } var documentRefID, uri, alg, checksum string // now, should have 4 items (or 3, if Alg and Checksum were joined) // and should be able to map them if len(keepSp) == 4 { documentRefID = keepSp[0] uri = keepSp[1] alg = keepSp[2] // check that colon is present for alg, and remove it if !strings.HasSuffix(alg, ":") { return "", "", "", "", fmt.Errorf("algorithm does not end with colon") } alg = strings.TrimSuffix(alg, ":") checksum = keepSp[3] } else if len(keepSp) == 3 { documentRefID = keepSp[0] uri = keepSp[1] // split on colon into alg and checksum parts := strings.SplitN(keepSp[2], ":", 2) if len(parts) != 2 { return "", "", "", "", fmt.Errorf("missing colon separator between algorithm and checksum") } alg = parts[0] checksum = parts[1] } else { return "", "", "", "", fmt.Errorf("expected 4 elements, got %d", len(keepSp)) } // additionally, we should be able to parse the first element as a // DocumentRef- ID string, and we should remove that prefix if !strings.HasPrefix(documentRefID, "DocumentRef-") { return "", "", "", "", fmt.Errorf("expected first element to have DocumentRef- prefix") } documentRefID = strings.TrimPrefix(documentRefID, "DocumentRef-") if documentRefID == "" { return "", "", "", "", fmt.Errorf("document identifier has nothing after prefix") } return documentRefID, uri, alg, checksum, nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_creation_info_test.go000066400000000000000000000321521463371440000266600ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Parser creation info state change tests ===== func TestParserCIMovesToPackageAfterParsingPackageNameTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } pkgName := "testPkg" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the package should NOT be in the SPDX Document's map of packages, // because it doesn't have an SPDX identifier yet if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } } func TestParserCIMovesToFileAfterParsingFileNameTagWithNoPackages(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("FileName", "testFile") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } // and current package should be nil, meaning Files are placed in the // Files map instead of in a Package if parser.pkg != nil { t.Fatalf("expected pkg to be nil, got non-nil pkg") } } func TestParserCIMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } } func TestParserCIMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } } func TestParserCIStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } } func TestParserCIStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this spdx file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } } func TestParserFailsParsingCreationInfoWithInvalidState(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psPackage, } err := parser.parsePairFromCreationInfo("SPDXVersion", "SPDX-2.1") if err == nil { t.Errorf("expected non-nil error, got nil") } } // ===== Creation Info section tests ===== func TestParserHasCreationInfoAfterCallToParseFirstTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("LicenseListVersion", "3.9") if err != nil { t.Errorf("got error when calling parsePairFromCreationInfo: %v", err) } if parser.doc.CreationInfo == nil { t.Errorf("doc.CreationInfo is still nil after parsing first pair") } } func TestParserCanParseCreationInfoTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // License List Version err := parser.parsePairFromCreationInfo("LicenseListVersion", "2.2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.LicenseListVersion != "2.2" { t.Errorf("got %v for LicenseListVersion", parser.doc.CreationInfo.LicenseListVersion) } // Creators: Persons refPersons := []string{ "Person: Person A", "Person: Person B", } err = parser.parsePairFromCreationInfo("Creator", refPersons[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refPersons[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 2 || parser.doc.CreationInfo.Creators[0].Creator != "Person A" || parser.doc.CreationInfo.Creators[1].Creator != "Person B" { t.Errorf("got %+v for Creators", parser.doc.CreationInfo.Creators) } // Creators: Organizations refOrgs := []string{ "Organization: Organization A", "Organization: Organization B", } err = parser.parsePairFromCreationInfo("Creator", refOrgs[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refOrgs[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 4 || parser.doc.CreationInfo.Creators[2].Creator != "Organization A" || parser.doc.CreationInfo.Creators[3].Creator != "Organization B" { t.Errorf("got %+v for CreatorOrganizations", parser.doc.CreationInfo.Creators) } // Creators: Tools refTools := []string{ "Tool: Tool A", "Tool: Tool B", } err = parser.parsePairFromCreationInfo("Creator", refTools[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refTools[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 6 || parser.doc.CreationInfo.Creators[4].Creator != "Tool A" || parser.doc.CreationInfo.Creators[5].Creator != "Tool B" { t.Errorf("got %v for CreatorTools", parser.doc.CreationInfo.Creators) } // Created date err = parser.parsePairFromCreationInfo("Created", "2018-09-10T11:46:00Z") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.Created != "2018-09-10T11:46:00Z" { t.Errorf("got %v for Created", parser.doc.CreationInfo.Created) } // Creator Comment err = parser.parsePairFromCreationInfo("CreatorComment", "Blah whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.CreatorComment != "Blah whatever" { t.Errorf("got %v for CreatorComment", parser.doc.CreationInfo.CreatorComment) } } func TestParserInvalidCreatorTagsFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("Creator", "blah: somebody") if err == nil { t.Errorf("expected error from parsing invalid Creator format, got nil") } err = parser.parsePairFromCreationInfo("Creator", "Tool with no colons") if err == nil { t.Errorf("expected error from parsing invalid Creator format, got nil") } } func TestParserCreatorTagWithMultipleColonsPasses(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("Creator", "Tool: tool1:2:3") if err != nil { t.Errorf("unexpected error from parsing valid Creator format") } } func TestParserCIUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserCICreatesRelationship(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserCICreatesAnnotation(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } // ===== Helper function tests ===== func TestCanExtractExternalDocumentReference(t *testing.T) { refstring := "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1:d6a770ba38583ed4bb4525bd96e50461655d2759" wantDocumentRefID := "spdx-tool-1.2" wantURI := "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" wantAlg := "SHA1" wantChecksum := "d6a770ba38583ed4bb4525bd96e50461655d2759" gotDocumentRefID, gotURI, gotAlg, gotChecksum, err := extractExternalDocumentReference(refstring) if err != nil { t.Errorf("got non-nil error: %v", err) } if wantDocumentRefID != gotDocumentRefID { t.Errorf("wanted document ref ID %s, got %s", wantDocumentRefID, gotDocumentRefID) } if wantURI != gotURI { t.Errorf("wanted URI %s, got %s", wantURI, gotURI) } if wantAlg != gotAlg { t.Errorf("wanted alg %s, got %s", wantAlg, gotAlg) } if wantChecksum != gotChecksum { t.Errorf("wanted checksum %s, got %s", wantChecksum, gotChecksum) } } func TestCanExtractExternalDocumentReferenceWithExtraWhitespace(t *testing.T) { refstring := " DocumentRef-spdx-tool-1.2 \t http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 \t SHA1: \t d6a770ba38583ed4bb4525bd96e50461655d2759" wantDocumentRefID := "spdx-tool-1.2" wantURI := "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" wantAlg := "SHA1" wantChecksum := "d6a770ba38583ed4bb4525bd96e50461655d2759" gotDocumentRefID, gotURI, gotAlg, gotChecksum, err := extractExternalDocumentReference(refstring) if err != nil { t.Errorf("got non-nil error: %v", err) } if wantDocumentRefID != gotDocumentRefID { t.Errorf("wanted document ref ID %s, got %s", wantDocumentRefID, gotDocumentRefID) } if wantURI != gotURI { t.Errorf("wanted URI %s, got %s", wantURI, gotURI) } if wantAlg != gotAlg { t.Errorf("wanted alg %s, got %s", wantAlg, gotAlg) } if wantChecksum != gotChecksum { t.Errorf("wanted checksum %s, got %s", wantChecksum, gotChecksum) } } func TestFailsExternalDocumentReferenceWithInvalidFormats(t *testing.T) { invalidRefs := []string{ "whoops", "DocumentRef-", "DocumentRef- ", "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 d6a770ba38583ed4bb4525bd96e50461655d2759", "DocumentRef-spdx-tool-1.2", "spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1:d6a770ba38583ed4bb4525bd96e50461655d2759", } for _, refstring := range invalidRefs { _, _, _, _, err := extractExternalDocumentReference(refstring) if err == nil { t.Errorf("expected non-nil error for %s, got nil", refstring) } } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_file.go000066400000000000000000000113741463371440000237240ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func (parser *tvParser) parsePairFromFile(tag string, value string) error { // expire fileAOP for anything other than an AOPHomePage or AOPURI // (we'll actually handle the HomePage and URI further below) if tag != "ArtifactOfProjectHomePage" && tag != "ArtifactOfProjectURI" { parser.fileAOP = nil } switch tag { // tag for creating new file section case "FileName": // check if the previous file contained a spdxId or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.file = &v2_1.File{} parser.file.FileName = value // tag for creating new package section and going back to parsing Package case "PackageName": // check if the previous file contained a spdxId or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.st = psPackage parser.file = nil return parser.parsePairFromPackage(tag, value) // tag for going on to snippet section case "SnippetSPDXID": parser.st = psSnippet return parser.parsePairFromSnippet(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tags for file data case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.file.FileSPDXIdentifier = eID if parser.pkg == nil { if parser.doc.Files == nil { parser.doc.Files = []*v2_1.File{} } parser.doc.Files = append(parser.doc.Files, parser.file) } else { if parser.pkg.Files == nil { parser.pkg.Files = []*v2_1.File{} } parser.pkg.Files = append(parser.pkg.Files, parser.file) } case "FileType": parser.file.FileTypes = append(parser.file.FileTypes, value) case "FileChecksum": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if parser.file.Checksums == nil { parser.file.Checksums = []common.Checksum{} } switch common.ChecksumAlgorithm(subkey) { case common.SHA1, common.SHA256, common.MD5: algorithm := common.ChecksumAlgorithm(subkey) parser.file.Checksums = append(parser.file.Checksums, common.Checksum{Algorithm: algorithm, Value: subvalue}) default: return fmt.Errorf("got unknown checksum type %s", subkey) } case "LicenseConcluded": parser.file.LicenseConcluded = value case "LicenseInfoInFile": parser.file.LicenseInfoInFiles = append(parser.file.LicenseInfoInFiles, value) case "LicenseComments": parser.file.LicenseComments = value case "FileCopyrightText": parser.file.FileCopyrightText = value case "ArtifactOfProjectName": parser.fileAOP = &v2_1.ArtifactOfProject{} parser.file.ArtifactOfProjects = append(parser.file.ArtifactOfProjects, parser.fileAOP) parser.fileAOP.Name = value case "ArtifactOfProjectHomePage": if parser.fileAOP == nil { return fmt.Errorf("no current ArtifactOfProject found") } parser.fileAOP.HomePage = value case "ArtifactOfProjectURI": if parser.fileAOP == nil { return fmt.Errorf("no current ArtifactOfProject found") } parser.fileAOP.URI = value case "FileComment": parser.file.FileComment = value case "FileNotice": parser.file.FileNotice = value case "FileContributor": parser.file.FileContributors = append(parser.file.FileContributors, value) case "FileDependency": parser.file.FileDependencies = append(parser.file.FileDependencies, value) // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_1.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_1.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in File section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_file_test.go000066400000000000000000000745351463371440000247730ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Parser file section state change tests ===== func TestParserFileStartsNewFileAfterParsingFileNameTag(t *testing.T) { // create the first file fileOldName := "f1.txt" parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: fileOldName, FileSPDXIdentifier: "f1"}, } fileOld := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, fileOld) // the Package's Files should have this one only if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } // now add a new file fileName := "f2.txt" err := parser.parsePair("FileName", fileName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } // and a file should be created if parser.file == nil { t.Fatalf("parser didn't create new file") } // and the file name should be as expected if parser.file.FileName != fileName { t.Errorf("expected file name %s, got %s", fileName, parser.file.FileName) } // and the Package's Files should still be of size 1 and not have this new // one yet, since it hasn't seen an SPDX identifier if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } // now parse an SPDX identifier tag err = parser.parsePair("SPDXID", "SPDXRef-f2ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // and the Package's Files should now be of size 2 and have this new one if len(parser.pkg.Files) != 2 { t.Fatalf("expected 2 files, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } if parser.pkg.Files[1] != parser.file { t.Errorf("expected file %v in Files[f2ID], got %v", parser.file, parser.pkg.Files[1]) } if parser.pkg.Files[1].FileName != fileName { t.Errorf("expected file name %s in Files[f2ID], got %s", fileName, parser.pkg.Files[1].FileName) } } func TestParserFileAddsToPackageOrUnpackagedFiles(t *testing.T) { // start with no packages parser := tvParser{ doc: &v2_1.Document{}, st: psCreationInfo, } // add a file and SPDX identifier fileName := "f2.txt" err := parser.parsePair("FileName", fileName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-f2ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } fileOld := parser.file // should have been added to Files if len(parser.doc.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.doc.Files)) } if parser.doc.Files[0] != fileOld { t.Errorf("expected file %v in Files[f2ID], got %v", fileOld, parser.doc.Files[0]) } // now create a package and a new file err = parser.parsePair("PackageName", "package1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-pkg1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("FileName", "f3.txt") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-f3ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // Files should still be size 1 and have old file only if len(parser.doc.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.doc.Files)) } if parser.doc.Files[0] != fileOld { t.Errorf("expected file %v in Files[f2ID], got %v", fileOld, parser.doc.Files[0]) } // and new package should have gotten the new file if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != parser.file { t.Errorf("expected file %v in Files[f3ID], got %v", parser.file, parser.pkg.Files[0]) } } func TestParserFileStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { // create the first file and package p1Name := "package1" f1Name := "f1.txt" parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: p1Name, PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: f1Name, FileSPDXIdentifier: "f1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, p1) parser.pkg.Files = append(parser.pkg.Files, f1) // now add a new package p2Name := "package2" err := parser.parsePair("PackageName", p2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should go back to Package if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new pkg") } // and the package name should be as expected if parser.pkg.PackageName != p2Name { t.Errorf("expected package name %s, got %s", p2Name, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the new Package should have no files if len(parser.pkg.Files) != 0 { t.Errorf("Expected no files in pkg.Files, got %d", len(parser.pkg.Files)) } // and the Document's Packages should still be of size 1 and not have this // new one, because no SPDX identifier has been seen yet if len(parser.doc.Packages) != 1 { t.Fatalf("expected 1 package, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != p1 { t.Errorf("Expected package %v in Packages[package1], got %v", p1, parser.doc.Packages[0]) } if parser.doc.Packages[0].PackageName != p1Name { t.Errorf("expected package name %s in Packages[package1], got %s", p1Name, parser.doc.Packages[0].PackageName) } // and the first Package's Files should be of size 1 and have f1 only if len(parser.doc.Packages[0].Files) != 1 { t.Errorf("Expected 1 file in Packages[package1].Files, got %d", len(parser.doc.Packages[0].Files)) } if parser.doc.Packages[0].Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.doc.Packages[0].Files[0]) } if parser.doc.Packages[0].Files[0].FileName != f1Name { t.Errorf("expected file name %s in Files[f1], got %s", f1Name, parser.doc.Packages[0].Files[0].FileName) } // and the current file should be nil if parser.file != nil { t.Errorf("Expected nil for parser.file, got %v", parser.file) } } func TestParserFileMovesToSnippetAfterParsingSnippetSPDXIDTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) fileCurrent := parser.file err := parser.parsePair("SnippetSPDXID", "SPDXRef-Test1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and current file should remain what it was if parser.file != fileCurrent { t.Fatalf("expected file to remain %v, got %v", fileCurrent, parser.file) } } func TestParserFileMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserFileMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserFileStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } } func TestParserFileStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } } // ===== File data section tests ===== func TestParserCanParseFileTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileName != "f1.txt" { t.Errorf("got %v for FileName", parser.file.FileName) } // should not yet be added to the Packages file list, because we haven't // seen an SPDX identifier yet if len(parser.pkg.Files) != 0 { t.Errorf("expected 0 files, got %d", len(parser.pkg.Files)) } // File SPDX Identifier err = parser.parsePairFromFile("SPDXID", "SPDXRef-f1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileSPDXIdentifier != "f1" { t.Errorf("got %v for FileSPDXIdentifier", parser.file.FileSPDXIdentifier) } // should now be added to the Packages file list if len(parser.pkg.Files) != 1 { t.Errorf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != parser.file { t.Errorf("expected Files[f1] to be %v, got %v", parser.file, parser.pkg.Files[0]) } // File Type fileTypes := []string{ "TEXT", "DOCUMENTATION", } for _, ty := range fileTypes { err = parser.parsePairFromFile("FileType", ty) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, typeWant := range fileTypes { flagFound := false for _, typeCheck := range parser.file.FileTypes { if typeWant == typeCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileTypes", typeWant) } } if len(fileTypes) != len(parser.file.FileTypes) { t.Errorf("expected %d types in FileTypes, got %d", len(fileTypes), len(parser.file.FileTypes)) } // File Checksums codeSha1 := "85ed0817af83a24ad8da68c2b5094de69833983c" sumSha1 := "SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c" codeSha256 := "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" sumSha256 := "SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" codeMd5 := "624c1abb3664f4b35547e7c73864ad24" sumMd5 := "MD5: 624c1abb3664f4b35547e7c73864ad24" err = parser.parsePairFromFile("FileChecksum", sumSha1) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("FileChecksum", sumSha256) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("FileChecksum", sumMd5) if err != nil { t.Errorf("expected nil error, got %v", err) } for _, checksum := range parser.file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != codeSha1 { t.Errorf("expected %s for FileChecksumSHA1, got %s", codeSha1, checksum.Value) } case common.SHA256: if checksum.Value != codeSha256 { t.Errorf("expected %s for FileChecksumSHA1, got %s", codeSha256, checksum.Value) } case common.MD5: if checksum.Value != codeMd5 { t.Errorf("expected %s for FileChecksumSHA1, got %s", codeMd5, checksum.Value) } } } // Concluded License err = parser.parsePairFromFile("LicenseConcluded", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.LicenseConcluded != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for LicenseConcluded", parser.file.LicenseConcluded) } // License Information in File lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromFile("LicenseInfoInFile", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.file.LicenseInfoInFiles { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseInfoInFiles", licWant) } } if len(lics) != len(parser.file.LicenseInfoInFiles) { t.Errorf("expected %d licenses in LicenseInfoInFiles, got %d", len(lics), len(parser.file.LicenseInfoInFiles)) } // Comments on License err = parser.parsePairFromFile("LicenseComments", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.LicenseComments != "this is a comment" { t.Errorf("got %v for LicenseComments", parser.file.LicenseComments) } // Copyright Text err = parser.parsePairFromFile("FileCopyrightText", "copyright (c) me") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileCopyrightText != "copyright (c) me" { t.Errorf("got %v for FileCopyrightText", parser.file.FileCopyrightText) } // Artifact of Projects: Name, HomePage and URI // Artifact set 1 err = parser.parsePairFromFile("ArtifactOfProjectName", "project1") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/1/") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/1/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 2 -- just name err = parser.parsePairFromFile("ArtifactOfProjectName", "project2") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 3 -- just name and home page err = parser.parsePairFromFile("ArtifactOfProjectName", "project3") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/3/") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 4 -- just name and URI err = parser.parsePairFromFile("ArtifactOfProjectName", "project4") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/4/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.file.ArtifactOfProjects) != 4 { t.Fatalf("expected len %d, got %d", 4, len(parser.file.ArtifactOfProjects)) } aop := parser.file.ArtifactOfProjects[0] if aop.Name != "project1" { t.Errorf("expected %v, got %v", "project1", aop.Name) } if aop.HomePage != "http://example.com/1/" { t.Errorf("expected %v, got %v", "http://example.com/1/", aop.HomePage) } if aop.URI != "http://example.com/1/uri.whatever" { t.Errorf("expected %v, got %v", "http://example.com/1/uri.whatever", aop.URI) } aop = parser.file.ArtifactOfProjects[1] if aop.Name != "project2" { t.Errorf("expected %v, got %v", "project2", aop.Name) } if aop.HomePage != "" { t.Errorf("expected %v, got %v", "", aop.HomePage) } if aop.URI != "" { t.Errorf("expected %v, got %v", "", aop.URI) } aop = parser.file.ArtifactOfProjects[2] if aop.Name != "project3" { t.Errorf("expected %v, got %v", "project3", aop.Name) } if aop.HomePage != "http://example.com/3/" { t.Errorf("expected %v, got %v", "http://example.com/3/", aop.HomePage) } if aop.URI != "" { t.Errorf("expected %v, got %v", "", aop.URI) } aop = parser.file.ArtifactOfProjects[3] if aop.Name != "project4" { t.Errorf("expected %v, got %v", "project4", aop.Name) } if aop.HomePage != "" { t.Errorf("expected %v, got %v", "", aop.HomePage) } if aop.URI != "http://example.com/4/uri.whatever" { t.Errorf("expected %v, got %v", "http://example.com/4/uri.whatever", aop.URI) } // File Comment err = parser.parsePairFromFile("FileComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileComment != "this is a comment" { t.Errorf("got %v for FileComment", parser.file.FileComment) } // File Notice err = parser.parsePairFromFile("FileNotice", "this is a Notice") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileNotice != "this is a Notice" { t.Errorf("got %v for FileNotice", parser.file.FileNotice) } // File Contributor contribs := []string{ "John Doe jdoe@example.com", "EvilCorp", } for _, contrib := range contribs { err = parser.parsePairFromFile("FileContributor", contrib) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, contribWant := range contribs { flagFound := false for _, contribCheck := range parser.file.FileContributors { if contribWant == contribCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileContributors", contribWant) } } if len(contribs) != len(parser.file.FileContributors) { t.Errorf("expected %d contribenses in FileContributors, got %d", len(contribs), len(parser.file.FileContributors)) } // File Dependencies deps := []string{ "f-1.txt", "g.txt", } for _, dep := range deps { err = parser.parsePairFromFile("FileDependency", dep) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, depWant := range deps { flagFound := false for _, depCheck := range parser.file.FileDependencies { if depWant == depCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileDependency", depWant) } } if len(deps) != len(parser.file.FileDependencies) { t.Errorf("expected %d depenses in FileDependency, got %d", len(deps), len(parser.file.FileDependencies)) } } func TestParserFileCreatesRelationshipInDocument(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserFileCreatesAnnotationInDocument(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } func TestParserFileUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromFile("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestFileAOPPointerChangesAfterTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromFile("ArtifactOfProjectName", "project1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP == nil { t.Errorf("expected non-nil AOP pointer, got nil") } curPtr := parser.fileAOP // now, a home page; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/1/") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != curPtr { t.Errorf("expected no change in AOP pointer, was %v, got %v", curPtr, parser.fileAOP) } // a URI; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/1/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != curPtr { t.Errorf("expected no change in AOP pointer, was %v, got %v", curPtr, parser.fileAOP) } // now, another artifact name; pointer should change but be non-nil // now, a home page; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectName", "project2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP == curPtr { t.Errorf("expected change in AOP pointer, got no change") } // finally, an unrelated tag; pointer should go away err = parser.parsePairFromFile("FileComment", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != nil { t.Errorf("expected nil AOP pointer, got %v", parser.fileAOP) } } func TestParserFailsIfInvalidSPDXIDInFileSection(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid SPDX Identifier err = parser.parsePairFromFile("SPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidChecksumFormatInFileSection(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid format for checksum line, missing colon err = parser.parsePairFromFile("FileChecksum", "SHA1 85ed0817af83a24ad8da68c2b5094de69833983c") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownChecksumTypeInFileSection(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // unknown checksum type err = parser.parsePairFromFile("FileChecksum", "Special: 85ed0817af83a24ad8da68c2b5094de69833983c") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfArtifactHomePageBeforeArtifactName(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // artifact home page appears before artifact name err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "https://example.com") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfArtifactURIBeforeArtifactName(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // artifact home page appears before artifact name err = parser.parsePairFromFile("ArtifactOfProjectURI", "https://example.com") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowError(t *testing.T) { // case 1: The previous file (packaged or unpackaged) does not contain spdxID parser1 := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, file: &v2_1.File{FileName: "FileName"}, } err := parser1.parsePair("FileName", "f2") if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } // case 2: Invalid file with snippet // Last unpackaged file before a snippet starts sid1 := common.ElementID("s1") fileName := "f2.txt" parser2 := tvParser{ doc: &v2_1.Document{}, st: psCreationInfo, file: &v2_1.File{FileName: fileName}, } err = parser2.parsePair("SnippetSPDXID", string(sid1)) if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } // case 3 : Invalid File without snippets // Last unpackaged file before a package starts // Last file of a package and New package starts parser3 := tvParser{ doc: &v2_1.Document{}, st: psCreationInfo, } fileName = "f3.txt" err = parser3.parsePair("FileName", fileName) if err != nil { t.Errorf("%s", err) } err = parser3.parsePair("PackageName", "p2") if err == nil { t.Errorf("files withoutSpdx Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_other_license.go000066400000000000000000000035361463371440000256310ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func (parser *tvParser) parsePairFromOtherLicense(tag string, value string) error { switch tag { // tag for creating new other license section case "LicenseID": parser.otherLic = &v2_1.OtherLicense{} parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.otherLic.LicenseIdentifier = value case "ExtractedText": parser.otherLic.ExtractedText = value case "LicenseName": parser.otherLic.LicenseName = value case "LicenseCrossReference": parser.otherLic.LicenseCrossReferences = append(parser.otherLic.LicenseCrossReferences, value) case "LicenseComment": parser.otherLic.LicenseComment = value // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_1.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_1.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in OtherLicense section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_other_license_test.go000066400000000000000000000306351463371440000266700ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Parser other license section state change tests ===== func TestParserOLStartsNewOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { // create the first other license olid1 := "LicenseRef-Lic11" olname1 := "License 11" parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psOtherLicense, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: olid1, LicenseName: olname1, }, } olic1 := parser.otherLic parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // the Document's OtherLicenses should have this one only if parser.doc.OtherLicenses[0] != olic1 { t.Errorf("Expected other license %v in OtherLicenses[0], got %v", olic1, parser.doc.OtherLicenses[0]) } if parser.doc.OtherLicenses[0].LicenseName != olname1 { t.Errorf("expected other license name %s in OtherLicenses[0], got %s", olname1, parser.doc.OtherLicenses[0].LicenseName) } // now add a new other license olid2 := "LicenseRef-22" olname2 := "License 22" err := parser.parsePair("LicenseID", olid2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and an other license should be created if parser.otherLic == nil { t.Fatalf("parser didn't create new other license") } // also parse the new license's name err = parser.parsePair("LicenseName", olname2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still be correct if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and the other license name should be as expected if parser.otherLic.LicenseName != olname2 { t.Errorf("expected other license name %s, got %s", olname2, parser.otherLic.LicenseName) } // and the Document's Other Licenses should be of size 2 and have these two if len(parser.doc.OtherLicenses) != 2 { t.Fatalf("Expected OtherLicenses to have len 2, got %d", len(parser.doc.OtherLicenses)) } if parser.doc.OtherLicenses[0] != olic1 { t.Errorf("Expected other license %v in OtherLicenses[0], got %v", olic1, parser.doc.OtherLicenses[0]) } if parser.doc.OtherLicenses[0].LicenseIdentifier != olid1 { t.Errorf("expected other license ID %s in OtherLicenses[0], got %s", olid1, parser.doc.OtherLicenses[0].LicenseIdentifier) } if parser.doc.OtherLicenses[0].LicenseName != olname1 { t.Errorf("expected other license name %s in OtherLicenses[0], got %s", olname1, parser.doc.OtherLicenses[0].LicenseName) } if parser.doc.OtherLicenses[1] != parser.otherLic { t.Errorf("Expected other license %v in OtherLicenses[1], got %v", parser.otherLic, parser.doc.OtherLicenses[1]) } if parser.doc.OtherLicenses[1].LicenseIdentifier != olid2 { t.Errorf("expected other license ID %s in OtherLicenses[1], got %s", olid2, parser.doc.OtherLicenses[1].LicenseIdentifier) } if parser.doc.OtherLicenses[1].LicenseName != olname2 { t.Errorf("expected other license name %s in OtherLicenses[1], got %s", olname2, parser.doc.OtherLicenses[1].LicenseName) } } func TestParserOLMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psOtherLicense, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserOtherLicenseStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psOtherLicense, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-whatever", LicenseName: "the whatever license", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserOtherLicenseStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psOtherLicense, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-whatever", LicenseName: "the whatever license", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } func TestParserOLFailsAfterParsingOtherSectionTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psOtherLicense, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // can't go back to old sections err := parser.parsePair("SPDXVersion", "SPDX-2.1") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("PackageName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("FileName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } } // ===== Other License data section tests ===== func TestParserCanParseOtherLicenseTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psOtherLicense, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // License Identifier err := parser.parsePairFromOtherLicense("LicenseID", "LicenseRef-Lic11") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseIdentifier != "LicenseRef-Lic11" { t.Errorf("got %v for LicenseID", parser.otherLic.LicenseIdentifier) } // Extracted Text err = parser.parsePairFromOtherLicense("ExtractedText", "You are permitted to do anything with the software, hooray!") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.ExtractedText != "You are permitted to do anything with the software, hooray!" { t.Errorf("got %v for ExtractedText", parser.otherLic.ExtractedText) } // License Name err = parser.parsePairFromOtherLicense("LicenseName", "License 11") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseName != "License 11" { t.Errorf("got %v for LicenseName", parser.otherLic.LicenseName) } // License Cross Reference crossRefs := []string{ "https://example.com/1", "https://example.com/2", "https://example.com/3", } for _, cr := range crossRefs { err = parser.parsePairFromOtherLicense("LicenseCrossReference", cr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, refWant := range crossRefs { flagFound := false for _, refCheck := range parser.otherLic.LicenseCrossReferences { if refWant == refCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseCrossReferences", refWant) } } if len(crossRefs) != len(parser.otherLic.LicenseCrossReferences) { t.Errorf("expected %d types in LicenseCrossReferences, got %d", len(crossRefs), len(parser.otherLic.LicenseCrossReferences)) } // License Comment err = parser.parsePairFromOtherLicense("LicenseComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseComment != "this is a comment" { t.Errorf("got %v for LicenseComment", parser.otherLic.LicenseComment) } } func TestParserOLUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psOtherLicense, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePairFromOtherLicense("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_package.go000066400000000000000000000162701463371440000244000ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func (parser *tvParser) parsePairFromPackage(tag string, value string) error { // expire pkgExtRef for anything other than a comment // (we'll actually handle the comment further below) if tag != "ExternalRefComment" { parser.pkgExtRef = nil } switch tag { case "PackageName": // if package already has a name, create and go on to a new package if parser.pkg == nil || parser.pkg.PackageName != "" { // check if the previous package contained an spdxId or not if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName) } parser.pkg = &v2_1.Package{ FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, } } parser.pkg.PackageName = value // tag for going on to file section case "FileName": parser.st = psFile return parser.parsePairFromFile(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.pkg.PackageSPDXIdentifier = eID if parser.doc.Packages == nil { parser.doc.Packages = []*v2_1.Package{} } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) case "PackageVersion": parser.pkg.PackageVersion = value case "PackageFileName": parser.pkg.PackageFileName = value case "PackageSupplier": parser.pkg.PackageSupplier = &common.Supplier{} if value == "NOASSERTION" { parser.pkg.PackageSupplier.Supplier = value break } subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person", "Organization": parser.pkg.PackageSupplier.Supplier = subvalue parser.pkg.PackageSupplier.SupplierType = subkey default: return fmt.Errorf("unrecognized PackageSupplier type %v", subkey) } case "PackageOriginator": parser.pkg.PackageOriginator = &common.Originator{} if value == "NOASSERTION" { parser.pkg.PackageOriginator.Originator = value break } subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person", "Organization": parser.pkg.PackageOriginator.Originator = subvalue parser.pkg.PackageOriginator.OriginatorType = subkey default: return fmt.Errorf("unrecognized PackageOriginator type %v", subkey) } case "PackageDownloadLocation": parser.pkg.PackageDownloadLocation = value case "FilesAnalyzed": parser.pkg.IsFilesAnalyzedTagPresent = true if value == "false" { parser.pkg.FilesAnalyzed = false } else if value == "true" { parser.pkg.FilesAnalyzed = true } case "PackageVerificationCode": parser.pkg.PackageVerificationCode = extractCodeAndExcludes(value) case "PackageChecksum": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if parser.pkg.PackageChecksums == nil { parser.pkg.PackageChecksums = []common.Checksum{} } switch common.ChecksumAlgorithm(subkey) { case common.SHA1, common.SHA256, common.MD5: algorithm := common.ChecksumAlgorithm(subkey) parser.pkg.PackageChecksums = append(parser.pkg.PackageChecksums, common.Checksum{Algorithm: algorithm, Value: subvalue}) default: return fmt.Errorf("got unknown checksum type %s", subkey) } case "PackageHomePage": parser.pkg.PackageHomePage = value case "PackageSourceInfo": parser.pkg.PackageSourceInfo = value case "PackageLicenseConcluded": parser.pkg.PackageLicenseConcluded = value case "PackageLicenseInfoFromFiles": parser.pkg.PackageLicenseInfoFromFiles = append(parser.pkg.PackageLicenseInfoFromFiles, value) case "PackageLicenseDeclared": parser.pkg.PackageLicenseDeclared = value case "PackageLicenseComments": parser.pkg.PackageLicenseComments = value case "PackageCopyrightText": parser.pkg.PackageCopyrightText = value case "PackageSummary": parser.pkg.PackageSummary = value case "PackageDescription": parser.pkg.PackageDescription = value case "PackageComment": parser.pkg.PackageComment = value case "ExternalRef": parser.pkgExtRef = &v2_1.PackageExternalReference{} parser.pkg.PackageExternalReferences = append(parser.pkg.PackageExternalReferences, parser.pkgExtRef) category, refType, locator, err := extractPackageExternalReference(value) if err != nil { return err } parser.pkgExtRef.Category = category parser.pkgExtRef.RefType = refType parser.pkgExtRef.Locator = locator case "ExternalRefComment": if parser.pkgExtRef == nil { return fmt.Errorf("no current ExternalRef found") } parser.pkgExtRef.ExternalRefComment = value // now, expire pkgExtRef anyway because it can have at most one comment parser.pkgExtRef = nil // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_1.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_1.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in Package section", tag) } return nil } // ===== Helper functions ===== func extractCodeAndExcludes(value string) common.PackageVerificationCode { // FIXME this should probably be done using regular expressions instead // split by paren + word "excludes:" sp := strings.SplitN(value, "(excludes:", 2) if len(sp) < 2 { // not found; return the whole string as just the code return common.PackageVerificationCode{Value: value, ExcludedFiles: []string{}} } // if we're here, code is in first part and excludes filename is in // second part, with trailing paren code := strings.TrimSpace(sp[0]) parsedSp := strings.SplitN(sp[1], ")", 2) fileName := strings.TrimSpace(parsedSp[0]) return common.PackageVerificationCode{Value: code, ExcludedFiles: []string{fileName}} } func extractPackageExternalReference(value string) (string, string, string, error) { sp := strings.Split(value, " ") // remove any that are just whitespace keepSp := []string{} for _, s := range sp { ss := strings.TrimSpace(s) if ss != "" { keepSp = append(keepSp, ss) } } // now, should have 3 items and should be able to map them if len(keepSp) != 3 { return "", "", "", fmt.Errorf("expected 3 elements, got %d", len(keepSp)) } return keepSp[0], keepSp[1], keepSp[2], nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_package_test.go000066400000000000000000001065501463371440000254400ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Parser package section state change tests ===== func TestParserPackageStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { // create the first package pkgOldName := "p1" parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: pkgOldName, PackageSPDXIdentifier: "p1"}, } pkgOld := parser.pkg parser.doc.Packages = append(parser.doc.Packages, pkgOld) // now add a new package pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and it should not be pkgOld if parser.pkg == pkgOld { t.Errorf("expected new package, got pkgOld") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should still be of size 1 and have pkgOld only if parser.doc.Packages[0] != pkgOld { t.Errorf("Expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } } func TestParserPackageStartsNewPackageAfterParsingPackageNameTagWhileInUnpackaged(t *testing.T) { // pkg is nil, so that Files appearing before the first PackageName tag // are added to Files instead of Packages parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psFile, pkg: nil, } // the Document's Packages should be empty if len(parser.doc.Packages) != 0 { t.Errorf("Expected zero packages, got %d", len(parser.doc.Packages)) } // now add a new package pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should be of size 0, because the prior was // unpackaged files and this one won't be added until an SPDXID is seen if len(parser.doc.Packages) != 0 { t.Errorf("Expected %v packages in doc, got %v", 0, len(parser.doc.Packages)) } } func TestParserPackageMovesToFileAfterParsingFileNameTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) pkgCurrent := parser.pkg err := parser.parsePair("FileName", "testFile") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } // and current package should remain what it was if parser.pkg != pkgCurrent { t.Fatalf("expected package to remain %v, got %v", pkgCurrent, parser.pkg) } } func TestParserPackageMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserPackageMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserPackageStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } } func TestParserPackageStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this package") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } } // ===== Package data section tests ===== func TestParserCanParsePackageTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // should not yet be in Packages map, b/c no SPDX identifier if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } // Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageName != "p1" { t.Errorf("got %v for PackageName", parser.pkg.PackageName) } // still should not yet be in Packages map, b/c no SPDX identifier if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } // Package SPDX Identifier err = parser.parsePairFromPackage("SPDXID", "SPDXRef-p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // "SPDXRef-" prefix should be removed from the item if parser.pkg.PackageSPDXIdentifier != "p1" { t.Errorf("got %v for PackageSPDXIdentifier", parser.pkg.PackageSPDXIdentifier) } // and it should now be added to the Packages map if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != parser.pkg { t.Errorf("expected to point to parser.pkg, got %v", parser.doc.Packages[0]) } // Package Version err = parser.parsePairFromPackage("PackageVersion", "2.1.1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVersion != "2.1.1" { t.Errorf("got %v for PackageVersion", parser.pkg.PackageVersion) } // Package File Name err = parser.parsePairFromPackage("PackageFileName", "p1-2.1.1.tar.gz") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageFileName != "p1-2.1.1.tar.gz" { t.Errorf("got %v for PackageFileName", parser.pkg.PackageFileName) } // Package Supplier // SKIP -- separate tests for subvalues below // Package Originator // SKIP -- separate tests for subvalues below // Package Download Location err = parser.parsePairFromPackage("PackageDownloadLocation", "https://example.com/whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageDownloadLocation != "https://example.com/whatever" { t.Errorf("got %v for PackageDownloadLocation", parser.pkg.PackageDownloadLocation) } // Files Analyzed err = parser.parsePairFromPackage("FilesAnalyzed", "false") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.FilesAnalyzed != false { t.Errorf("got %v for FilesAnalyzed", parser.pkg.FilesAnalyzed) } if parser.pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("got %v for IsFilesAnalyzedTagPresent", parser.pkg.IsFilesAnalyzedTagPresent) } // Package Verification Code // SKIP -- separate tests for "excludes", or not, below // Package Checksums codeSha1 := "85ed0817af83a24ad8da68c2b5094de69833983c" sumSha1 := "SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c" codeSha256 := "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" sumSha256 := "SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" codeMd5 := "624c1abb3664f4b35547e7c73864ad24" sumMd5 := "MD5: 624c1abb3664f4b35547e7c73864ad24" err = parser.parsePairFromPackage("PackageChecksum", sumSha1) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromPackage("PackageChecksum", sumSha256) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromPackage("PackageChecksum", sumMd5) if err != nil { t.Errorf("expected nil error, got %v", err) } for _, checksum := range parser.pkg.PackageChecksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != codeSha1 { t.Errorf("expected %s for PackageChecksum SHA1, got %s", codeSha1, checksum.Value) } case common.SHA256: if checksum.Value != codeSha256 { t.Errorf("expected %s for PackageChecksum SHA256, got %s", codeSha256, checksum.Value) } case common.MD5: if checksum.Value != codeMd5 { t.Errorf("expected %s for PackageChecksum MD5, got %s", codeMd5, checksum.Value) } } } // Package Home Page err = parser.parsePairFromPackage("PackageHomePage", "https://example.com/whatever2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageHomePage != "https://example.com/whatever2" { t.Errorf("got %v for PackageHomePage", parser.pkg.PackageHomePage) } // Package Source Info err = parser.parsePairFromPackage("PackageSourceInfo", "random comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSourceInfo != "random comment" { t.Errorf("got %v for PackageSourceInfo", parser.pkg.PackageSourceInfo) } // Package License Concluded err = parser.parsePairFromPackage("PackageLicenseConcluded", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseConcluded != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for PackageLicenseConcluded", parser.pkg.PackageLicenseConcluded) } // All Licenses Info From Files lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromPackage("PackageLicenseInfoFromFiles", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.pkg.PackageLicenseInfoFromFiles { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in PackageLicenseInfoFromFiles", licWant) } } if len(lics) != len(parser.pkg.PackageLicenseInfoFromFiles) { t.Errorf("expected %d licenses in PackageLicenseInfoFromFiles, got %d", len(lics), len(parser.pkg.PackageLicenseInfoFromFiles)) } // Package License Declared err = parser.parsePairFromPackage("PackageLicenseDeclared", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseDeclared != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for PackageLicenseDeclared", parser.pkg.PackageLicenseDeclared) } // Package License Comments err = parser.parsePairFromPackage("PackageLicenseComments", "this is a license comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseComments != "this is a license comment" { t.Errorf("got %v for PackageLicenseComments", parser.pkg.PackageLicenseComments) } // Package Copyright Text err = parser.parsePairFromPackage("PackageCopyrightText", "Copyright (c) me myself and i") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageCopyrightText != "Copyright (c) me myself and i" { t.Errorf("got %v for PackageCopyrightText", parser.pkg.PackageCopyrightText) } // Package Summary err = parser.parsePairFromPackage("PackageSummary", "i wrote this package") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSummary != "i wrote this package" { t.Errorf("got %v for PackageSummary", parser.pkg.PackageSummary) } // Package Description err = parser.parsePairFromPackage("PackageDescription", "i wrote this package a lot") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageDescription != "i wrote this package a lot" { t.Errorf("got %v for PackageDescription", parser.pkg.PackageDescription) } // Package Comment err = parser.parsePairFromPackage("PackageComment", "i scanned this package") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageComment != "i scanned this package" { t.Errorf("got %v for PackageComment", parser.pkg.PackageComment) } // Package External References and Comments ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" ref1Category := "SECURITY" ref1Type := common.TypeSecurityCPE23Type ref1Locator := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" ref1Comment := "this is comment #1" ref2 := "OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3alpha" ref2Category := "OTHER" ref2Type := "LocationRef-acmeforge" ref2Locator := "acmecorp/acmenator/4.1.3alpha" ref2Comment := "this is comment #2" err = parser.parsePairFromPackage("ExternalRef", ref1) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.pkg.PackageExternalReferences) != 1 { t.Errorf("expected 1 external reference, got %d", len(parser.pkg.PackageExternalReferences)) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil pkgExtRef, got nil") } if parser.pkg.PackageExternalReferences[0] == nil { t.Errorf("expected non-nil PackageExternalReferences[0], got nil") } if parser.pkgExtRef != parser.pkg.PackageExternalReferences[0] { t.Errorf("expected pkgExtRef to match PackageExternalReferences[0], got no match") } err = parser.parsePairFromPackage("ExternalRefComment", ref1Comment) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromPackage("ExternalRef", ref2) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.pkg.PackageExternalReferences) != 2 { t.Errorf("expected 2 external references, got %d", len(parser.pkg.PackageExternalReferences)) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil pkgExtRef, got nil") } if parser.pkg.PackageExternalReferences[1] == nil { t.Errorf("expected non-nil PackageExternalReferences[1], got nil") } if parser.pkgExtRef != parser.pkg.PackageExternalReferences[1] { t.Errorf("expected pkgExtRef to match PackageExternalReferences[1], got no match") } err = parser.parsePairFromPackage("ExternalRefComment", ref2Comment) if err != nil { t.Errorf("expected nil error, got %v", err) } // finally, check these values gotRef1 := parser.pkg.PackageExternalReferences[0] if gotRef1.Category != ref1Category { t.Errorf("expected ref1 category to be %s, got %s", gotRef1.Category, ref1Category) } if gotRef1.RefType != ref1Type { t.Errorf("expected ref1 type to be %s, got %s", gotRef1.RefType, ref1Type) } if gotRef1.Locator != ref1Locator { t.Errorf("expected ref1 locator to be %s, got %s", gotRef1.Locator, ref1Locator) } if gotRef1.ExternalRefComment != ref1Comment { t.Errorf("expected ref1 comment to be %s, got %s", gotRef1.ExternalRefComment, ref1Comment) } gotRef2 := parser.pkg.PackageExternalReferences[1] if gotRef2.Category != ref2Category { t.Errorf("expected ref2 category to be %s, got %s", gotRef2.Category, ref2Category) } if gotRef2.RefType != ref2Type { t.Errorf("expected ref2 type to be %s, got %s", gotRef2.RefType, ref2Type) } if gotRef2.Locator != ref2Locator { t.Errorf("expected ref2 locator to be %s, got %s", gotRef2.Locator, ref2Locator) } if gotRef2.ExternalRefComment != ref2Comment { t.Errorf("expected ref2 comment to be %s, got %s", gotRef2.ExternalRefComment, ref2Comment) } } func TestParserCanParsePackageSupplierPersonTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: Person err := parser.parsePairFromPackage("PackageSupplier", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "John Doe" { t.Errorf("got %v for PackageSupplierPerson", parser.pkg.PackageSupplier.Supplier) } } func TestParserCanParsePackageSupplierOrganizationTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: Organization err := parser.parsePairFromPackage("PackageSupplier", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "John Doe, Inc." { t.Errorf("got %v for PackageSupplierOrganization", parser.pkg.PackageSupplier.Supplier) } } func TestParserCanParsePackageSupplierNOASSERTIONTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: NOASSERTION err := parser.parsePairFromPackage("PackageSupplier", "NOASSERTION") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "NOASSERTION" { t.Errorf("got false for PackageSupplierNOASSERTION") } } func TestParserCanParsePackageOriginatorPersonTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: Person err := parser.parsePairFromPackage("PackageOriginator", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "John Doe" { t.Errorf("got %v for PackageOriginatorPerson", parser.pkg.PackageOriginator.Originator) } } func TestParserCanParsePackageOriginatorOrganizationTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: Organization err := parser.parsePairFromPackage("PackageOriginator", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "John Doe, Inc." { t.Errorf("got %v for PackageOriginatorOrganization", parser.pkg.PackageOriginator.Originator) } } func TestParserCanParsePackageOriginatorNOASSERTIONTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: NOASSERTION err := parser.parsePairFromPackage("PackageOriginator", "NOASSERTION") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "NOASSERTION" { t.Errorf("got false for PackageOriginatorNOASSERTION") } } func TestParserCanParsePackageVerificationCodeTagWithExcludes(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Verification Code with excludes parenthetical code := "d6a770ba38583ed4bb4525bd96e50461655d2758" fileName := "./package.spdx" fullCodeValue := "d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx)" err := parser.parsePairFromPackage("PackageVerificationCode", fullCodeValue) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVerificationCode.Value != code { t.Errorf("got %v for PackageVerificationCode", parser.pkg.PackageVerificationCode) } if len(parser.pkg.PackageVerificationCode.ExcludedFiles) != 1 || parser.pkg.PackageVerificationCode.ExcludedFiles[0] != fileName { t.Errorf("got %v for PackageVerificationCodeExcludedFile", parser.pkg.PackageVerificationCode.ExcludedFiles) } } func TestParserCanParsePackageVerificationCodeTagWithoutExcludes(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Verification Code without excludes parenthetical code := "d6a770ba38583ed4bb4525bd96e50461655d2758" err := parser.parsePairFromPackage("PackageVerificationCode", code) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVerificationCode.Value != code { t.Errorf("got %v for PackageVerificationCode", parser.pkg.PackageVerificationCode) } if len(parser.pkg.PackageVerificationCode.ExcludedFiles) != 0 { t.Errorf("got %v for PackageVerificationCodeExcludedFile", parser.pkg.PackageVerificationCode.ExcludedFiles) } } func TestPackageExternalRefPointerChangesAfterTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" err := parser.parsePairFromPackage("ExternalRef", ref1) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil external reference pointer, got nil") } // now, a comment; pointer should go away err = parser.parsePairFromPackage("ExternalRefComment", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef != nil { t.Errorf("expected nil external reference pointer, got non-nil") } ref2 := "Other LocationRef-something https://example.com/whatever" err = parser.parsePairFromPackage("ExternalRef", ref2) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil external reference pointer, got nil") } // and some other random tag makes the pointer go away too err = parser.parsePairFromPackage("PackageSummary", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef != nil { t.Errorf("expected nil external reference pointer, got non-nil") } } func TestParserPackageCreatesRelationshipInDocument(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserPackageCreatesAnnotationInDocument(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } func TestParserPackageUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePairFromPackage("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserFailsIfInvalidSPDXIDInPackageSection(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid ID format err = parser.parsePairFromPackage("SPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageSupplierFormat(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid supplier format err = parser.parsePairFromPackage("PackageSupplier", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownPackageSupplierType(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid supplier type err = parser.parsePairFromPackage("PackageSupplier", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageOriginatorFormat(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid originator format err = parser.parsePairFromPackage("PackageOriginator", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownPackageOriginatorType(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid originator type err = parser.parsePairFromPackage("PackageOriginator", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserSetsFilesAnalyzedTagsCorrectly(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // set tag err = parser.parsePairFromPackage("FilesAnalyzed", "true") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.FilesAnalyzed != true { t.Errorf("expected %v, got %v", true, parser.pkg.FilesAnalyzed) } if parser.pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("expected %v, got %v", true, parser.pkg.IsFilesAnalyzedTagPresent) } } func TestParserFailsIfInvalidPackageChecksumFormat(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid checksum format err = parser.parsePairFromPackage("PackageChecksum", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageChecksumType(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid checksum type err = parser.parsePairFromPackage("PackageChecksum", "whoops: blah") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidExternalRefFormat(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid external ref format err = parser.parsePairFromPackage("ExternalRef", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfExternalRefCommentBeforeExternalRef(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // external ref comment before external ref err = parser.parsePairFromPackage("ExternalRefComment", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } // ===== Helper function tests ===== func TestCanCheckAndExtractExcludesFilenameAndCode(t *testing.T) { code := "d6a770ba38583ed4bb4525bd96e50461655d2758" fileName := "./package.spdx" fullCodeValue := "d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx)" gotCode := extractCodeAndExcludes(fullCodeValue) if gotCode.Value != code { t.Errorf("got %v for gotCode", gotCode) } if len(gotCode.ExcludedFiles) != 1 || gotCode.ExcludedFiles[0] != fileName { t.Errorf("got %v for gotFileName", gotCode.ExcludedFiles) } } func TestCanExtractPackageExternalReference(t *testing.T) { ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" category := "SECURITY" refType := common.TypeSecurityCPE23Type location := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" gotCategory, gotRefType, gotLocation, err := extractPackageExternalReference(ref1) if err != nil { t.Errorf("got non-nil error: %v", err) } if gotCategory != category { t.Errorf("expected category %s, got %s", category, gotCategory) } if gotRefType != refType { t.Errorf("expected refType %s, got %s", refType, gotRefType) } if gotLocation != location { t.Errorf("expected location %s, got %s", location, gotLocation) } } func TestCanExtractPackageExternalReferenceWithExtraWhitespace(t *testing.T) { ref1 := " SECURITY \t cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* \t " category := "SECURITY" refType := common.TypeSecurityCPE23Type location := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" gotCategory, gotRefType, gotLocation, err := extractPackageExternalReference(ref1) if err != nil { t.Errorf("got non-nil error: %v", err) } if gotCategory != category { t.Errorf("expected category %s, got %s", category, gotCategory) } if gotRefType != refType { t.Errorf("expected refType %s, got %s", refType, gotRefType) } if gotLocation != location { t.Errorf("expected location %s, got %s", location, gotLocation) } } func TestFailsPackageExternalRefWithInvalidFormat(t *testing.T) { _, _, _, err := extractPackageExternalReference("whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserPackageWithoutSpdxIdentifierThrowsError(t *testing.T) { // More than one package, the previous package doesn't contain the SPDXID pkgOldName := "p1" parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psPackage, pkg: &v2_1.Package{PackageName: pkgOldName}, } pkgOld := parser.pkg parser.doc.Packages = append(parser.doc.Packages, pkgOld) // the Document's Packages should have this one only if parser.doc.Packages[0] != pkgOld { t.Errorf("expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err == nil { t.Errorf("package without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_relationship.go000066400000000000000000000022101463371440000254730ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" ) func (parser *tvParser) parsePairForRelationship(tag string, value string) error { if parser.rln == nil { return fmt.Errorf("no relationship struct created in parser rln pointer") } if tag == "Relationship" { // parse the value to see if it's a valid relationship format sp := strings.SplitN(value, " ", -1) // filter out any purely-whitespace items var rp []string for _, v := range sp { v = strings.TrimSpace(v) if v != "" { rp = append(rp, v) } } if len(rp) != 3 { return fmt.Errorf("invalid relationship format for %s", value) } aID, err := extractDocElementID(strings.TrimSpace(rp[0])) if err != nil { return err } parser.rln.RefA = aID parser.rln.Relationship = strings.TrimSpace(rp[1]) bID, err := extractDocElementID(strings.TrimSpace(rp[2])) if err != nil { return err } parser.rln.RefB = bID return nil } if tag == "RelationshipComment" { parser.rln.RelationshipComment = value return nil } return fmt.Errorf("received unknown tag %v in Relationship section", tag) } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_relationship_test.go000066400000000000000000000113011463371440000265330ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Relationship section tests ===== func TestParserFailsIfRelationshipNotSet(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairForRelationship("Relationship", "SPDXRef-A CONTAINS SPDXRef-B") if err == nil { t.Errorf("expected error when calling parsePairFromRelationship without setting rln pointer") } } func TestParserFailsIfRelationshipCommentWithoutRelationship(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("RelationshipComment", "comment whatever") if err == nil { t.Errorf("expected error when calling parsePair for RelationshipComment without Relationship first") } } func TestParserCanParseRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Relationship err := parser.parsePair("Relationship", "SPDXRef-something CONTAINS DocumentRef-otherdoc:SPDXRef-something-else") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rln.RefA.DocumentRefID != "" || parser.rln.RefA.ElementRefID != "something" { t.Errorf("got %v for first part of Relationship, expected something", parser.rln.RefA) } if parser.rln.RefB.DocumentRefID != "otherdoc" || parser.rln.RefB.ElementRefID != "something-else" { t.Errorf("got %v for second part of Relationship, expected otherdoc:something-else", parser.rln.RefB) } if parser.rln.Relationship != "CONTAINS" { t.Errorf("got %v for Relationship type, expected CONTAINS", parser.rln.Relationship) } // Relationship Comment cmt := "this is a comment" err = parser.parsePair("RelationshipComment", cmt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rln.RelationshipComment != cmt { t.Errorf("got %v for RelationshipComment, expected %v", parser.rln.RelationshipComment, cmt) } } func TestParserInvalidRelationshipTagsNoValueFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // no items parser.rln = nil err := parser.parsePair("Relationship", "") if err == nil { t.Errorf("expected error for empty items in relationship, got nil") } } func TestParserInvalidRelationshipTagsOneValueFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // one item parser.rln = nil err := parser.parsePair("Relationship", "DESCRIBES") if err == nil { t.Errorf("expected error for only one item in relationship, got nil") } } func TestParserInvalidRelationshipTagsTwoValuesFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // two items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-DOCUMENT DESCRIBES") if err == nil { t.Errorf("expected error for only two items in relationship, got nil") } } func TestParserInvalidRelationshipTagsThreeValuesSucceed(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // three items but with interspersed additional whitespace parser.rln = nil err := parser.parsePair("Relationship", " SPDXRef-DOCUMENT \t DESCRIBES SPDXRef-something-else ") if err != nil { t.Errorf("expected pass for three items in relationship w/ extra whitespace, got: %v", err) } } func TestParserInvalidRelationshipTagsFourValuesFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // four items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-a DESCRIBES SPDXRef-b SPDXRef-c") if err == nil { t.Errorf("expected error for more than three items in relationship, got nil") } } func TestParserInvalidRelationshipTagsInvalidRefIDs(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // four items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-a DESCRIBES b") if err == nil { t.Errorf("expected error for missing SPDXRef- prefix, got nil") } parser.rln = nil err = parser.parsePair("Relationship", "a DESCRIBES SPDXRef-b") if err == nil { t.Errorf("expected error for missing SPDXRef- prefix, got nil") } } func TestParserFailsToParseUnknownTagInRelationshipSection(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Relationship err := parser.parsePair("Relationship", "SPDXRef-something CONTAINS DocumentRef-otherdoc:SPDXRef-something-else") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid tag err = parser.parsePairForRelationship("blah", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_review.go000066400000000000000000000035541463371440000243070ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func (parser *tvParser) parsePairFromReview(tag string, value string) error { switch tag { // tag for creating new review section case "Reviewer": parser.rev = &v2_1.Review{} parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Person" case "Organization": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Organization" case "Tool": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Tool" default: return fmt.Errorf("unrecognized Reviewer type %v", subkey) } case "ReviewDate": parser.rev.ReviewDate = value case "ReviewComment": parser.rev.ReviewComment = value // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_1.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_1.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) default: return fmt.Errorf("received unknown tag %v in Review section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_review_test.go000066400000000000000000000342301463371440000253410ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Parser review section state change tests ===== func TestParserReviewStartsNewReviewAfterParsingReviewerTag(t *testing.T) { // create the first review rev1 := "John Doe" parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{ Reviewer: rev1, ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) r1 := parser.rev // the Document's Reviews should have this one only if len(parser.doc.Reviews) != 1 { t.Errorf("Expected only one review, got %d", len(parser.doc.Reviews)) } if parser.doc.Reviews[0] != r1 { t.Errorf("Expected review %v in Reviews[0], got %v", r1, parser.doc.Reviews[0]) } if parser.doc.Reviews[0].Reviewer != rev1 { t.Errorf("expected review name %s in Reviews[0], got %s", rev1, parser.doc.Reviews[0].Reviewer) } // now add a new review rev2 := "Steve" rp2 := "Person: Steve" err := parser.parsePair("Reviewer", rp2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } // and a review should be created if parser.rev == nil { t.Fatalf("parser didn't create new review") } // and the reviewer's name should be as expected if parser.rev.Reviewer != rev2 { t.Errorf("expected reviewer name %s, got %s", rev2, parser.rev.Reviewer) } // and the Document's reviews should be of size 2 and have these two if len(parser.doc.Reviews) != 2 { t.Fatalf("Expected Reviews to have len 2, got %d", len(parser.doc.Reviews)) } if parser.doc.Reviews[0] != r1 { t.Errorf("Expected review %v in Reviews[0], got %v", r1, parser.doc.Reviews[0]) } if parser.doc.Reviews[0].Reviewer != rev1 { t.Errorf("expected reviewer name %s in Reviews[0], got %s", rev1, parser.doc.Reviews[0].Reviewer) } if parser.doc.Reviews[1] != parser.rev { t.Errorf("Expected review %v in Reviews[1], got %v", parser.rev, parser.doc.Reviews[1]) } if parser.doc.Reviews[1].Reviewer != rev2 { t.Errorf("expected reviewer name %s in Reviews[1], got %s", rev2, parser.doc.Reviews[1].Reviewer) } } func TestParserReviewStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{ Reviewer: "Jane Doe", ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserReviewStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{ Reviewer: "Jane Doe", ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } func TestParserReviewFailsAfterParsingOtherSectionTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // can't go back to old sections err := parser.parsePair("SPDXVersion", "SPDX-2.1") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("PackageName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("FileName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("LicenseID", "LicenseRef-Lic22") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } } // ===== Review data section tests ===== func TestParserCanParseReviewTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer (DEPRECATED) // handled in subsequent subtests // Review Date (DEPRECATED) err := parser.parsePairFromReview("ReviewDate", "2018-09-23T08:30:00Z") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.ReviewDate != "2018-09-23T08:30:00Z" { t.Errorf("got %v for ReviewDate", parser.rev.ReviewDate) } // Review Comment (DEPRECATED) err = parser.parsePairFromReview("ReviewComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.ReviewComment != "this is a comment" { t.Errorf("got %v for ReviewComment", parser.rev.ReviewComment) } } func TestParserCanParseReviewerPersonTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Person err := parser.parsePairFromReview("Reviewer", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "John Doe" { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Person" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserCanParseReviewerOrganizationTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Organization err := parser.parsePairFromReview("Reviewer", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "John Doe, Inc." { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Organization" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserCanParseReviewerToolTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Tool err := parser.parsePairFromReview("Reviewer", "Tool: scannertool - 1.2.12") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "scannertool - 1.2.12" { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Tool" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserFailsIfReviewerInvalidFormat(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, rev: &v2_1.Review{}, } parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("Reviewer", "oops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfReviewerUnknownType(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, rev: &v2_1.Review{}, } parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("Reviewer", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserReviewUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psReview, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_1.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_1.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_snippet.go000066400000000000000000000106261463371440000244660ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strconv" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func (parser *tvParser) parsePairFromSnippet(tag string, value string) error { switch tag { // tag for creating new snippet section case "SnippetSPDXID": // check here whether the previous file contained an SPDX ID or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.snippet = &v2_1.Snippet{} eID, err := extractElementID(value) if err != nil { return err } // FIXME: how should we handle where not associated with current file? if parser.file != nil { if parser.file.Snippets == nil { parser.file.Snippets = map[common.ElementID]*v2_1.Snippet{} } parser.file.Snippets[eID] = parser.snippet } parser.snippet.SnippetSPDXIdentifier = eID // tag for creating new file section and going back to parsing File case "FileName": parser.st = psFile parser.snippet = nil return parser.parsePairFromFile(tag, value) // tag for creating new package section and going back to parsing Package case "PackageName": parser.st = psPackage parser.file = nil parser.snippet = nil return parser.parsePairFromPackage(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tags for snippet data case "SnippetFromFileSPDXID": deID, err := extractDocElementID(value) if err != nil { return err } parser.snippet.SnippetFromFileSPDXIdentifier = deID.ElementRefID case "SnippetByteRange": byteStart, byteEnd, err := extractSubs(value) if err != nil { return err } bIntStart, err := strconv.Atoi(byteStart) if err != nil { return err } bIntEnd, err := strconv.Atoi(byteEnd) if err != nil { return err } if parser.snippet.Ranges == nil { parser.snippet.Ranges = []common.SnippetRange{} } byteRange := common.SnippetRange{StartPointer: common.SnippetRangePointer{Offset: bIntStart}, EndPointer: common.SnippetRangePointer{Offset: bIntEnd}} parser.snippet.Ranges = append(parser.snippet.Ranges, byteRange) case "SnippetLineRange": lineStart, lineEnd, err := extractSubs(value) if err != nil { return err } lInttStart, err := strconv.Atoi(lineStart) if err != nil { return err } lInttEnd, err := strconv.Atoi(lineEnd) if err != nil { return err } if parser.snippet.Ranges == nil { parser.snippet.Ranges = []common.SnippetRange{} } lineRange := common.SnippetRange{StartPointer: common.SnippetRangePointer{LineNumber: lInttStart}, EndPointer: common.SnippetRangePointer{LineNumber: lInttEnd}} parser.snippet.Ranges = append(parser.snippet.Ranges, lineRange) case "SnippetLicenseConcluded": parser.snippet.SnippetLicenseConcluded = value case "LicenseInfoInSnippet": parser.snippet.LicenseInfoInSnippet = append(parser.snippet.LicenseInfoInSnippet, value) case "SnippetLicenseComments": parser.snippet.SnippetLicenseComments = value case "SnippetCopyrightText": parser.snippet.SnippetCopyrightText = value case "SnippetComment": parser.snippet.SnippetComment = value case "SnippetName": parser.snippet.SnippetName = value // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_1.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_1.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in Snippet section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parse_snippet_test.go000066400000000000000000000553131463371440000255270ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Parser snippet section state change tests ===== func TestParserSnippetStartsNewSnippetAfterParsingSnippetSPDXIDTag(t *testing.T) { // create the first snippet sid1 := common.ElementID("s1") parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{SnippetSPDXIdentifier: sid1}, } s1 := parser.snippet parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets[sid1] = parser.snippet // the File's Snippets should have this one only if len(parser.file.Snippets) != 1 { t.Errorf("Expected len(Snippets) to be 1, got %d", len(parser.file.Snippets)) } if parser.file.Snippets["s1"] != s1 { t.Errorf("Expected snippet %v in Snippets[s1], got %v", s1, parser.file.Snippets["s1"]) } if parser.file.Snippets["s1"].SnippetSPDXIdentifier != sid1 { t.Errorf("expected snippet ID %s in Snippets[s1], got %s", sid1, parser.file.Snippets["s1"].SnippetSPDXIdentifier) } // now add a new snippet err := parser.parsePair("SnippetSPDXID", "SPDXRef-s2") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and a snippet should be created if parser.snippet == nil { t.Fatalf("parser didn't create new snippet") } // and the snippet ID should be as expected if parser.snippet.SnippetSPDXIdentifier != "s2" { t.Errorf("expected snippet ID %s, got %s", "s2", parser.snippet.SnippetSPDXIdentifier) } // and the File's Snippets should be of size 2 and have these two if len(parser.file.Snippets) != 2 { t.Errorf("Expected len(Snippets) to be 2, got %d", len(parser.file.Snippets)) } if parser.file.Snippets["s1"] != s1 { t.Errorf("Expected snippet %v in Snippets[s1], got %v", s1, parser.file.Snippets["s1"]) } if parser.file.Snippets["s1"].SnippetSPDXIdentifier != sid1 { t.Errorf("expected snippet ID %s in Snippets[s1], got %s", sid1, parser.file.Snippets["s1"].SnippetSPDXIdentifier) } if parser.file.Snippets["s2"] != parser.snippet { t.Errorf("Expected snippet %v in Snippets[s2], got %v", parser.snippet, parser.file.Snippets["s2"]) } if parser.file.Snippets["s2"].SnippetSPDXIdentifier != "s2" { t.Errorf("expected snippet ID %s in Snippets[s2], got %s", "s2", parser.file.Snippets["s2"].SnippetSPDXIdentifier) } } func TestParserSnippetStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{SnippetSPDXIdentifier: "s1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet // now add a new package p2Name := "package2" err := parser.parsePair("PackageName", p2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should go back to Package if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new pkg") } // and the package name should be as expected if parser.pkg.PackageName != p2Name { t.Errorf("expected package name %s, got %s", p2Name, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should still be of size 1 b/c no SPDX // identifier has been seen yet if len(parser.doc.Packages) != 1 { t.Errorf("Expected len(Packages) to be 1, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != p1 { t.Errorf("Expected package %v in Packages[package1], got %v", p1, parser.doc.Packages[0]) } if parser.doc.Packages[0].PackageName != "package1" { t.Errorf("expected package name %s in Packages[package1], got %s", "package1", parser.doc.Packages[0].PackageName) } // and the first Package's Files should be of size 1 and have f1 only if len(parser.doc.Packages[0].Files) != 1 { t.Errorf("Expected 1 file in Packages[package1].Files, got %d", len(parser.doc.Packages[0].Files)) } if parser.doc.Packages[0].Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.doc.Packages[0].Files[0]) } if parser.doc.Packages[0].Files[0].FileName != "f1.txt" { t.Errorf("expected file name %s in Files[f1], got %s", "f1.txt", parser.doc.Packages[0].Files[0].FileName) } // and the new Package should have no files if len(parser.pkg.Files) != 0 { t.Errorf("Expected no files in Packages[1].Files, got %d", len(parser.pkg.Files)) } // and the current file should be nil if parser.file != nil { t.Errorf("Expected nil for parser.file, got %v", parser.file) } // and the current snippet should be nil if parser.snippet != nil { t.Errorf("Expected nil for parser.snippet, got %v", parser.snippet) } } func TestParserSnippetMovesToFileAfterParsingFileNameTag(t *testing.T) { f1Name := "f1.txt" parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{SnippetSPDXIdentifier: "s1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet f2Name := "f2.txt" err := parser.parsePair("FileName", f2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and current package should remain what it was if parser.pkg != p1 { t.Fatalf("expected package to remain %v, got %v", p1, parser.pkg) } // and a file should be created if parser.file == nil { t.Fatalf("parser didn't create new file") } // and the file name should be as expected if parser.file.FileName != f2Name { t.Errorf("expected file name %s, got %s", f2Name, parser.file.FileName) } // and the Package's Files should still be of size 1 since we haven't seen // an SPDX identifier yet for this new file if len(parser.pkg.Files) != 1 { t.Errorf("Expected len(Files) to be 1, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != f1Name { t.Errorf("expected file name %s in Files[f1], got %s", f1Name, parser.pkg.Files[0].FileName) } // and the current snippet should be nil if parser.snippet != nil { t.Errorf("Expected nil for parser.snippet, got %v", parser.snippet) } } func TestParserSnippetMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserSnippetMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserSnippetStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } } func TestParserSnippetStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } // ===== Snippet data section tests ===== func TestParserCanParseSnippetTags(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetSPDXIdentifier != "s1" { t.Errorf("got %v for SnippetSPDXIdentifier", parser.snippet.SnippetSPDXIdentifier) } // Snippet from File SPDX Identifier err = parser.parsePairFromSnippet("SnippetFromFileSPDXID", "SPDXRef-f1") if err != nil { t.Errorf("expected nil error, got %v", err) } wantDeID := common.DocElementID{DocumentRefID: "", ElementRefID: common.ElementID("f1")} if parser.snippet.SnippetFromFileSPDXIdentifier != wantDeID.ElementRefID { t.Errorf("got %v for SnippetFromFileSPDXIdentifier", parser.snippet.SnippetFromFileSPDXIdentifier) } // Snippet Byte Range err = parser.parsePairFromSnippet("SnippetByteRange", "20:320") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.Ranges[0].StartPointer.Offset != 20 { t.Errorf("got %v for SnippetByteRangeStart", parser.snippet.Ranges[0].StartPointer.Offset) } if parser.snippet.Ranges[0].EndPointer.Offset != 320 { t.Errorf("got %v for SnippetByteRangeEnd", parser.snippet.Ranges[0].EndPointer.Offset) } // Snippet Line Range err = parser.parsePairFromSnippet("SnippetLineRange", "5:12") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.Ranges[1].StartPointer.LineNumber != 5 { t.Errorf("got %v for SnippetLineRangeStart", parser.snippet.Ranges[1].StartPointer.LineNumber) } if parser.snippet.Ranges[1].EndPointer.LineNumber != 12 { t.Errorf("got %v for SnippetLineRangeEnd", parser.snippet.Ranges[1].EndPointer.LineNumber) } // Snippet Concluded License err = parser.parsePairFromSnippet("SnippetLicenseConcluded", "BSD-3-Clause") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetLicenseConcluded != "BSD-3-Clause" { t.Errorf("got %v for SnippetLicenseConcluded", parser.snippet.SnippetLicenseConcluded) } // License Information in Snippet lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromSnippet("LicenseInfoInSnippet", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.snippet.LicenseInfoInSnippet { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseInfoInSnippet", licWant) } } if len(lics) != len(parser.snippet.LicenseInfoInSnippet) { t.Errorf("expected %d licenses in LicenseInfoInSnippet, got %d", len(lics), len(parser.snippet.LicenseInfoInSnippet)) } // Snippet Comments on License err = parser.parsePairFromSnippet("SnippetLicenseComments", "this is a comment about the licenses") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetLicenseComments != "this is a comment about the licenses" { t.Errorf("got %v for SnippetLicenseComments", parser.snippet.SnippetLicenseComments) } // Snippet Copyright Text err = parser.parsePairFromSnippet("SnippetCopyrightText", "copyright (c) John Doe and friends") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetCopyrightText != "copyright (c) John Doe and friends" { t.Errorf("got %v for SnippetCopyrightText", parser.snippet.SnippetCopyrightText) } // Snippet Comment err = parser.parsePairFromSnippet("SnippetComment", "this is a comment about the snippet") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetComment != "this is a comment about the snippet" { t.Errorf("got %v for SnippetComment", parser.snippet.SnippetComment) } // Snippet Name err = parser.parsePairFromSnippet("SnippetName", "from some other package called abc") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetName != "from some other package called abc" { t.Errorf("got %v for SnippetName", parser.snippet.SnippetName) } } func TestParserSnippetUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromSnippet("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserFailsForInvalidSnippetSPDXID(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // invalid Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetFromFileSPDXID(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid From File identifier err = parser.parsePairFromSnippet("SnippetFromFileSPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetByteValues(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid byte formats and values err = parser.parsePairFromSnippet("SnippetByteRange", "200 210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetByteRange", "a:210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetByteRange", "200:a") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetLineValues(t *testing.T) { parser := tvParser{ doc: &v2_1.Document{Packages: []*v2_1.Package{}}, st: psSnippet, pkg: &v2_1.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_1.File{}}, file: &v2_1.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_1.Snippet{}}, snippet: &v2_1.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid byte formats and values err = parser.parsePairFromSnippet("SnippetLineRange", "200 210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetLineRange", "a:210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetLineRange", "200:a") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowErrorWithSnippets(t *testing.T) { // Invalid file with snippet // Last unpackaged file before the snippet starts // Last file of a package and new snippet starts fileName := "f2.txt" sid1 := common.ElementID("s1") parser2 := tvParser{ doc: &v2_1.Document{}, st: psCreationInfo, file: &v2_1.File{FileName: fileName}, } err := parser2.parsePair("SnippetSPDXID", string(sid1)) if err == nil { t.Errorf("files without SPDX Identifiers getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parser.go000066400000000000000000000061321463371440000231030ustar00rootroot00000000000000// Package parser2v1 contains functions to read, load and parse // SPDX tag-value files. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" "github.com/spdx/tools-golang/tagvalue/reader" ) // ParseTagValues takes a list of (tag, value) pairs, parses it and returns // a pointer to a parsed SPDX Document. func ParseTagValues(tvs []reader.TagValuePair) (*spdx.Document, error) { parser := tvParser{} for _, tv := range tvs { err := parser.parsePair(tv.Tag, tv.Value) if err != nil { return nil, err } } if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return nil, fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId { return nil, fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName) } return parser.doc, nil } func (parser *tvParser) parsePair(tag string, value string) error { switch parser.st { case psStart: return parser.parsePairFromStart(tag, value) case psCreationInfo: return parser.parsePairFromCreationInfo(tag, value) case psPackage: return parser.parsePairFromPackage(tag, value) case psFile: return parser.parsePairFromFile(tag, value) case psSnippet: return parser.parsePairFromSnippet(tag, value) case psOtherLicense: return parser.parsePairFromOtherLicense(tag, value) case psReview: return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("parser state %v not recognized when parsing (%s, %s)", parser.st, tag, value) } } func (parser *tvParser) parsePairFromStart(tag string, value string) error { // fail if not in Start parser state if parser.st != psStart { return fmt.Errorf("got invalid state %v in parsePairFromStart", parser.st) } // create an SPDX Document data struct if we don't have one already if parser.doc == nil { parser.doc = &spdx.Document{ ExternalDocumentReferences: []spdx.ExternalDocumentRef{}, } } switch tag { case "SPDXVersion": parser.doc.SPDXVersion = value case "DataLicense": parser.doc.DataLicense = value case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.doc.SPDXIdentifier = eID case "DocumentName": parser.doc.DocumentName = value case "DocumentNamespace": parser.doc.DocumentNamespace = value case "ExternalDocumentRef": documentRefID, uri, alg, checksum, err := extractExternalDocumentReference(value) if err != nil { return err } edr := spdx.ExternalDocumentRef{ DocumentRefID: documentRefID, URI: uri, Checksum: common.Checksum{Algorithm: common.ChecksumAlgorithm(alg), Value: checksum}, } parser.doc.ExternalDocumentReferences = append(parser.doc.ExternalDocumentReferences, edr) case "DocumentComment": parser.doc.DocumentComment = value default: // move to Creation Info parser state parser.st = psCreationInfo return parser.parsePairFromCreationInfo(tag, value) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/parser_test.go000066400000000000000000000056161463371440000241500ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" "github.com/spdx/tools-golang/tagvalue/reader" ) // ===== Parser exported entry point tests ===== func TestParserCanParseTagValues(t *testing.T) { var tvPairs []reader.TagValuePair // create some pairs tvPair1 := reader.TagValuePair{Tag: "SPDXVersion", Value: "SPDX-2.1"} tvPairs = append(tvPairs, tvPair1) tvPair2 := reader.TagValuePair{Tag: "DataLicense", Value: spdx.DataLicense} tvPairs = append(tvPairs, tvPair2) tvPair3 := reader.TagValuePair{Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"} tvPairs = append(tvPairs, tvPair3) // now parse them doc, err := ParseTagValues(tvPairs) if err != nil { t.Errorf("got error when calling ParseTagValues: %v", err) } if doc.SPDXVersion != "SPDX-2.1" { t.Errorf("expected SPDXVersion to be SPDX-2.1, got %v", doc.SPDXVersion) } if doc.DataLicense != spdx.DataLicense { t.Errorf("expected DataLicense to be CC0-1.0, got %v", doc.DataLicense) } if doc.SPDXIdentifier != "DOCUMENT" { t.Errorf("expected SPDXIdentifier to be DOCUMENT, got %v", doc.SPDXIdentifier) } } // ===== Parser initialization tests ===== func TestParserInitCreatesResetStatus(t *testing.T) { parser := tvParser{} if parser.st != psStart { t.Errorf("parser did not begin in start state") } if parser.doc != nil { t.Errorf("parser did not begin with nil document") } } func TestParserHasDocumentAfterCallToParseFirstTag(t *testing.T) { parser := tvParser{} err := parser.parsePair("SPDXVersion", "SPDX-2.1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.doc == nil { t.Errorf("doc is still nil after parsing first pair") } } func TestParserStartFailsToParseIfInInvalidState(t *testing.T) { parser := tvParser{st: psReview} err := parser.parsePairFromStart("SPDXVersion", "SPDX-2.1") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowErrorAtCompleteParse(t *testing.T) { // case: checks the last file // Last unpackaged file no packages in doc // Last file of last package in the doc tvPairs := []reader.TagValuePair{ {Tag: "SPDXVersion", Value: "SPDX-2.1"}, {Tag: "DataLicense", Value: spdx.DataLicense}, {Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"}, {Tag: "FileName", Value: "f1"}, } _, err := ParseTagValues(tvPairs) if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } } func TestParserPackageWithoutSpdxIdThrowErrorAtCompleteParse(t *testing.T) { // case: Checks the last package tvPairs := []reader.TagValuePair{ {Tag: "SPDXVersion", Value: "SPDX-2.1"}, {Tag: "DataLicense", Value: spdx.DataLicense}, {Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"}, {Tag: "PackageName", Value: "p1"}, } _, err := ParseTagValues(tvPairs) if err == nil { t.Errorf("package without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/types.go000066400000000000000000000022771463371440000227610ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" ) type tvParser struct { // document into which data is being parsed doc *v2_1.Document // current parser state st tvParserState // current SPDX item being filled in, if any pkg *v2_1.Package pkgExtRef *v2_1.PackageExternalReference file *v2_1.File fileAOP *v2_1.ArtifactOfProject snippet *v2_1.Snippet otherLic *v2_1.OtherLicense rln *v2_1.Relationship ann *v2_1.Annotation rev *v2_1.Review // don't need creation info pointer b/c only one, // and we can get to it via doc.CreationInfo } // parser state (SPDX document version 2.1) type tvParserState int const ( // at beginning of document psStart tvParserState = iota // in document creation info section psCreationInfo // in package data section psPackage // in file data section (including "unpackaged" files) psFile // in snippet data section (including "unpackaged" files) psSnippet // in other license section psOtherLicense // in review section psReview ) const nullSpdxElementId = common.ElementID("") tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/util.go000066400000000000000000000064571463371440000225760ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" ) // used to extract key / value from embedded substrings // returns subkey, subvalue, nil if no error, or "", "", error otherwise func extractSubs(value string) (string, string, error) { // parse the value to see if it's a valid subvalue format sp := strings.SplitN(value, ":", 2) if len(sp) == 1 { return "", "", fmt.Errorf("invalid subvalue format for %s (no colon found)", value) } subkey := strings.TrimSpace(sp[0]) subvalue := strings.TrimSpace(sp[1]) return subkey, subvalue, nil } // used to extract DocumentRef and SPDXRef values from an SPDX Identifier // which can point either to this document or to a different one func extractDocElementID(value string) (common.DocElementID, error) { docRefID := "" idStr := value // check prefix to see if it's a DocumentRef ID if strings.HasPrefix(idStr, "DocumentRef-") { // extract the part that comes between "DocumentRef-" and ":" strs := strings.Split(idStr, ":") // should be exactly two, part before and part after if len(strs) < 2 { return common.DocElementID{}, fmt.Errorf("no colon found although DocumentRef- prefix present") } if len(strs) > 2 { return common.DocElementID{}, fmt.Errorf("more than one colon found") } // trim the prefix and confirm non-empty docRefID = strings.TrimPrefix(strs[0], "DocumentRef-") if docRefID == "" { return common.DocElementID{}, fmt.Errorf("document identifier has nothing after prefix") } // and use remainder for element ID parsing idStr = strs[1] } // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(idStr, "SPDXRef-") { return common.DocElementID{}, fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(idStr, ":") { // we know this means there was no DocumentRef- prefix, because // we would have handled multiple colons above if it was return common.DocElementID{}, fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(idStr, "SPDXRef-") if eltRefID == "" { return common.DocElementID{}, fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.DocElementID{DocumentRefID: docRefID, ElementRefID: common.ElementID(eltRefID)}, nil } // used to extract SPDXRef values only from an SPDX Identifier which can point // to this document only. Use extractDocElementID for parsing IDs that can // refer either to this document or a different one. func extractElementID(value string) (common.ElementID, error) { // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(value, "SPDXRef-") { return common.ElementID(""), fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(value, ":") { return common.ElementID(""), fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(value, "SPDXRef-") if eltRefID == "" { return common.ElementID(""), fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.ElementID(eltRefID), nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/reader/util_test.go000066400000000000000000000103501463371440000236200ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" ) // ===== Helper function tests ===== func TestCanExtractSubvalues(t *testing.T) { subkey, subvalue, err := extractSubs("SHA1: abc123") if err != nil { t.Errorf("got error when calling extractSubs: %v", err) } if subkey != "SHA1" { t.Errorf("got %v for subkey", subkey) } if subvalue != "abc123" { t.Errorf("got %v for subvalue", subvalue) } } func TestReturnsErrorForInvalidSubvalueFormat(t *testing.T) { _, _, err := extractSubs("blah") if err == nil { t.Errorf("expected error when calling extractSubs for invalid format (0 colons), got nil") } } func TestCanExtractDocumentAndElementRefsFromID(t *testing.T) { // test with valid ID in this document helperForExtractDocElementID(t, "SPDXRef-file1", false, "", "file1") // test with valid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file2", false, "doc2", "file2") // test with invalid ID in this document helperForExtractDocElementID(t, "a:SPDXRef-file1", true, "", "") helperForExtractDocElementID(t, "file1", true, "", "") helperForExtractDocElementID(t, "SPDXRef-", true, "", "") helperForExtractDocElementID(t, "SPDXRef-file1:", true, "", "") // test with invalid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:a", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:SPDXRef-file1", true, "", "") // test with invalid formats helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file1:file2", true, "", "") } func helperForExtractDocElementID(t *testing.T, tst string, wantErr bool, wantDoc string, wantElt string) { deID, err := extractDocElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if deID.DocumentRefID != wantDoc { if wantDoc == "" { t.Errorf("testing %v: want empty string for DocumentRefID, got %v", tst, deID.DocumentRefID) } else { t.Errorf("testing %v: want %v for DocumentRefID, got %v", tst, wantDoc, deID.DocumentRefID) } } if deID.ElementRefID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, deID.ElementRefID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, deID.ElementRefID) } } } func TestCanExtractElementRefsOnlyFromID(t *testing.T) { // test with valid ID in this document helperForExtractElementID(t, "SPDXRef-file1", false, "file1") // test with valid ID in another document helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-file2", true, "") // test with invalid ID in this document helperForExtractElementID(t, "a:SPDXRef-file1", true, "") helperForExtractElementID(t, "file1", true, "") helperForExtractElementID(t, "SPDXRef-", true, "") helperForExtractElementID(t, "SPDXRef-file1:", true, "") // test with invalid ID in another document helperForExtractElementID(t, "DocumentRef-doc2", true, "") helperForExtractElementID(t, "DocumentRef-doc2:", true, "") helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-", true, "") helperForExtractElementID(t, "DocumentRef-doc2:a", true, "") helperForExtractElementID(t, "DocumentRef-:", true, "") helperForExtractElementID(t, "DocumentRef-:SPDXRef-file1", true, "") } func helperForExtractElementID(t *testing.T, tst string, wantErr bool, wantElt string) { eID, err := extractElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if eID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, eID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, eID) } } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/000077500000000000000000000000001463371440000213305ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_annotation.go000066400000000000000000000016011463371440000250450ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func renderAnnotation(ann *spdx.Annotation, w io.Writer) error { if ann.Annotator.Annotator != "" && ann.Annotator.AnnotatorType != "" { fmt.Fprintf(w, "Annotator: %s: %s\n", ann.Annotator.AnnotatorType, ann.Annotator.Annotator) } if ann.AnnotationDate != "" { fmt.Fprintf(w, "AnnotationDate: %s\n", ann.AnnotationDate) } if ann.AnnotationType != "" { fmt.Fprintf(w, "AnnotationType: %s\n", ann.AnnotationType) } annIDStr := common.RenderDocElementID(ann.AnnotationSPDXIdentifier) if annIDStr != "SPDXRef-" { fmt.Fprintf(w, "SPDXREF: %s\n", annIDStr) } if ann.AnnotationComment != "" { fmt.Fprintf(w, "AnnotationComment: %s\n", textify(ann.AnnotationComment)) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_annotation_test.go000066400000000000000000000065211463371440000261120ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Annotation section Saver tests ===== func TestSaverAnnotationSavesTextForPerson(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Person", Annotator: "John Doe"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Person: John Doe AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverAnnotationSavesTextForOrganization(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Organization", Annotator: "John Doe, Inc."}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Organization: John Doe, Inc. AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverAnnotationSavesTextForTool(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Tool", Annotator: "magictool-1.1"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Tool: magictool-1.1 AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } // note that the annotation has no optional or multiple fields tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_creation_info.go000066400000000000000000000012771463371440000255230ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func renderCreationInfo(ci *spdx.CreationInfo, w io.Writer) error { if ci.LicenseListVersion != "" { fmt.Fprintf(w, "LicenseListVersion: %s\n", ci.LicenseListVersion) } for _, creator := range ci.Creators { fmt.Fprintf(w, "Creator: %s: %s\n", creator.CreatorType, creator.Creator) } if ci.Created != "" { fmt.Fprintf(w, "Created: %s\n", ci.Created) } if ci.CreatorComment != "" { fmt.Fprintf(w, "CreatorComment: %s\n", textify(ci.CreatorComment)) } // add blank newline b/c end of a main section fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_creation_info_test.go000066400000000000000000000057331463371440000265630ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Creation Info section Saver tests ===== func TestSaverCISavesText(t *testing.T) { ci := &spdx.CreationInfo{ LicenseListVersion: "2.0", Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, {Creator: "Jane Doe (janedoe@example.com)", CreatorType: "Person"}, {Creator: "John Doe, Inc.", CreatorType: "Organization"}, {Creator: "Jane Doe LLC", CreatorType: "Organization"}, {Creator: "magictool1-1.0", CreatorType: "Tool"}, {Creator: "magictool2-1.0", CreatorType: "Tool"}, {Creator: "magictool3-1.0", CreatorType: "Tool"}, }, Created: "2018-10-10T06:20:00Z", CreatorComment: "this is a creator comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseListVersion: 2.0 Creator: Person: John Doe Creator: Person: Jane Doe (janedoe@example.com) Creator: Organization: John Doe, Inc. Creator: Organization: Jane Doe LLC Creator: Tool: magictool1-1.0 Creator: Tool: magictool2-1.0 Creator: Tool: magictool3-1.0 Created: 2018-10-10T06:20:00Z CreatorComment: this is a creator comment `) // render as buffer of bytes var got bytes.Buffer err := renderCreationInfo(ci, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverCIOmitsOptionalFieldsIfEmpty(t *testing.T) { // --- need at least one creator; do first for Persons --- ci1 := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, }, Created: "2018-10-10T06:20:00Z", } // what we want to get, as a buffer of bytes want1 := bytes.NewBufferString(`Creator: Person: John Doe Created: 2018-10-10T06:20:00Z `) // render as buffer of bytes var got1 bytes.Buffer err := renderCreationInfo(ci1, &got1) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c1 := bytes.Compare(want1.Bytes(), got1.Bytes()) if c1 != 0 { t.Errorf("Expected %v, got %v", want1.String(), got1.String()) } // --- need at least one creator; now switch to organization --- ci2 := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe, Inc.", CreatorType: "Organization"}, }, Created: "2018-10-10T06:20:00Z", } // what we want to get, as a buffer of bytes want2 := bytes.NewBufferString(`Creator: Organization: John Doe, Inc. Created: 2018-10-10T06:20:00Z `) // render as buffer of bytes var got2 bytes.Buffer err = renderCreationInfo(ci2, &got2) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c2 := bytes.Compare(want2.Bytes(), got2.Bytes()) if c2 != 0 { t.Errorf("Expected %v, got %v", want2.String(), got2.String()) } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_document.go000066400000000000000000000057451463371440000245260ustar00rootroot00000000000000// Package saver2v1 contains functions to render and write a tag-value // formatted version of an in-memory SPDX document and its sections // (version 2.1). // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // RenderDocument is the main entry point to take an SPDX in-memory // Document (version 2.1), and render it to the received io.Writer. // It is only exported in order to be available to the tvsaver package, // and typically does not need to be called by client code. func RenderDocument(doc *spdx.Document, w io.Writer) error { if doc.CreationInfo == nil { return fmt.Errorf("Document had nil CreationInfo section") } if doc.SPDXVersion != "" { fmt.Fprintf(w, "SPDXVersion: %s\n", doc.SPDXVersion) } if doc.DataLicense != "" { fmt.Fprintf(w, "DataLicense: %s\n", doc.DataLicense) } if doc.SPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(doc.SPDXIdentifier)) } if doc.DocumentName != "" { fmt.Fprintf(w, "DocumentName: %s\n", doc.DocumentName) } if doc.DocumentNamespace != "" { fmt.Fprintf(w, "DocumentNamespace: %s\n", doc.DocumentNamespace) } // print EDRs in order sorted by identifier sort.Slice(doc.ExternalDocumentReferences, func(i, j int) bool { return doc.ExternalDocumentReferences[i].DocumentRefID < doc.ExternalDocumentReferences[j].DocumentRefID }) for _, edr := range doc.ExternalDocumentReferences { fmt.Fprintf(w, "ExternalDocumentRef: DocumentRef-%s %s %s:%s\n", edr.DocumentRefID, edr.URI, edr.Checksum.Algorithm, edr.Checksum.Value) } if doc.DocumentComment != "" { fmt.Fprintf(w, "DocumentComment: %s\n", textify(doc.DocumentComment)) } renderCreationInfo(doc.CreationInfo, w) if len(doc.Files) > 0 { fmt.Fprintf(w, "##### Unpackaged files\n\n") sort.Slice(doc.Files, func(i, j int) bool { return doc.Files[i].FileSPDXIdentifier < doc.Files[j].FileSPDXIdentifier }) for _, fi := range doc.Files { renderFile(fi, w) } } // sort Packages by identifier sort.Slice(doc.Packages, func(i, j int) bool { return doc.Packages[i].PackageSPDXIdentifier < doc.Packages[j].PackageSPDXIdentifier }) for _, pkg := range doc.Packages { fmt.Fprintf(w, "##### Package: %s\n\n", pkg.PackageName) renderPackage(pkg, w) } if len(doc.OtherLicenses) > 0 { fmt.Fprintf(w, "##### Other Licenses\n\n") for _, ol := range doc.OtherLicenses { renderOtherLicense(ol, w) } } if len(doc.Relationships) > 0 { fmt.Fprintf(w, "##### Relationships\n\n") for _, rln := range doc.Relationships { renderRelationship(rln, w) } fmt.Fprintf(w, "\n") } if len(doc.Annotations) > 0 { fmt.Fprintf(w, "##### Annotations\n\n") for _, ann := range doc.Annotations { renderAnnotation(ann, w) fmt.Fprintf(w, "\n") } } if len(doc.Reviews) > 0 { fmt.Fprintf(w, "##### Reviews\n\n") for _, rev := range doc.Reviews { renderReview(rev, w) } } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_document_test.go000066400000000000000000000240161463371440000255550ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== entire Document Saver tests ===== func TestSaverDocumentSavesText(t *testing.T) { // Creation Info section ci := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, }, Created: "2018-10-10T06:20:00Z", } // unpackaged files f1 := &spdx.File{ FileName: "/tmp/whatever1.txt", FileSPDXIdentifier: common.ElementID("File1231"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983c", Algorithm: common.SHA1}}, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{"Apache-2.0"}, FileCopyrightText: "Copyright (c) Jane Doe", } f2 := &spdx.File{ FileName: "/tmp/whatever2.txt", FileSPDXIdentifier: common.ElementID("File1232"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983d", Algorithm: common.SHA1}}, LicenseConcluded: "MIT", LicenseInfoInFiles: []string{"MIT"}, FileCopyrightText: "Copyright (c) John Doe", } unFiles := []*spdx.File{ f1, f2, } // Package 1: packaged files with snippets sn1 := &spdx.Snippet{ SnippetSPDXIdentifier: "Snippet19", SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "FileHasSnippets").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}}}, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } sn2 := &spdx.Snippet{ SnippetSPDXIdentifier: "Snippet20", SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "FileHasSnippets").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 268}, EndPointer: common.SnippetRangePointer{Offset: 309}}}, SnippetLicenseConcluded: "WTFPL", SnippetCopyrightText: "NOASSERTION", } f3 := &spdx.File{ FileName: "/tmp/file-with-snippets.txt", FileSPDXIdentifier: common.ElementID("FileHasSnippets"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983e", Algorithm: common.SHA1}}, LicenseConcluded: "GPL-2.0-or-later AND WTFPL", LicenseInfoInFiles: []string{ "Apache-2.0", "GPL-2.0-or-later", "WTFPL", }, FileCopyrightText: "Copyright (c) Jane Doe", Snippets: map[common.ElementID]*spdx.Snippet{ common.ElementID("Snippet19"): sn1, common.ElementID("Snippet20"): sn2, }, } f4 := &spdx.File{ FileName: "/tmp/another-file.txt", FileSPDXIdentifier: common.ElementID("FileAnother"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983f", Algorithm: common.SHA1}}, LicenseConcluded: "BSD-3-Clause", LicenseInfoInFiles: []string{"BSD-3-Clause"}, FileCopyrightText: "Copyright (c) Jane Doe LLC", } pkgWith := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageLicenseConcluded: "GPL-2.0-or-later AND BSD-3-Clause AND WTFPL", PackageLicenseInfoFromFiles: []string{ "Apache-2.0", "GPL-2.0-or-later", "WTFPL", "BSD-3-Clause", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", Files: []*spdx.File{ f3, f4, }, } // Other Licenses 1 and 2 ol1 := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", } ol2 := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-2", ExtractedText: `License 2 text - this is a license that does some stuff`, LicenseName: "License 2", } // Relationships rln1 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p1"), Relationship: "DESCRIBES", } rln2 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "File1231"), Relationship: "DESCRIBES", } rln3 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "File1232"), Relationship: "DESCRIBES", } // Annotations ann1 := &spdx.Annotation{ Annotator: common.Annotator{Annotator: "John Doe", AnnotatorType: "Person"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } ann2 := &spdx.Annotation{ Annotator: common.Annotator{Annotator: "John Doe, Inc.", AnnotatorType: "Organization"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "p1"), AnnotationComment: "This is an annotation about Package p1", } // Reviews rev1 := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", } rev2 := &spdx.Review{ Reviewer: "Jane Doe LLC", ReviewerType: "Organization", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: "I have reviewed this SPDX document and it is awesome", } // now, build the document doc := &spdx.Document{ SPDXVersion: "SPDX-2.1", DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), DocumentName: "spdx-go-0.0.1.abcdef", DocumentNamespace: "https://github.com/swinslow/spdx-docs/spdx-go/spdx-go-0.0.1.abcdef.whatever", CreationInfo: ci, Packages: []*spdx.Package{ pkgWith, }, Files: unFiles, OtherLicenses: []*spdx.OtherLicense{ ol1, ol2, }, Relationships: []*spdx.Relationship{ rln1, rln2, rln3, }, Annotations: []*spdx.Annotation{ ann1, ann2, }, Reviews: []*spdx.Review{ rev1, rev2, }, } want := bytes.NewBufferString(`SPDXVersion: SPDX-2.1 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: spdx-go-0.0.1.abcdef DocumentNamespace: https://github.com/swinslow/spdx-docs/spdx-go/spdx-go-0.0.1.abcdef.whatever Creator: Person: John Doe Created: 2018-10-10T06:20:00Z ##### Unpackaged files FileName: /tmp/whatever1.txt SPDXID: SPDXRef-File1231 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe FileName: /tmp/whatever2.txt SPDXID: SPDXRef-File1232 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983d LicenseConcluded: MIT LicenseInfoInFile: MIT FileCopyrightText: Copyright (c) John Doe ##### Package: p1 PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: true PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 PackageLicenseConcluded: GPL-2.0-or-later AND BSD-3-Clause AND WTFPL PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseInfoFromFiles: WTFPL PackageLicenseInfoFromFiles: BSD-3-Clause PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. FileName: /tmp/another-file.txt SPDXID: SPDXRef-FileAnother FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983f LicenseConcluded: BSD-3-Clause LicenseInfoInFile: BSD-3-Clause FileCopyrightText: Copyright (c) Jane Doe LLC FileName: /tmp/file-with-snippets.txt SPDXID: SPDXRef-FileHasSnippets FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983e LicenseConcluded: GPL-2.0-or-later AND WTFPL LicenseInfoInFile: Apache-2.0 LicenseInfoInFile: GPL-2.0-or-later LicenseInfoInFile: WTFPL FileCopyrightText: Copyright (c) Jane Doe SnippetSPDXID: SPDXRef-Snippet19 SnippetFromFileSPDXID: SPDXRef-FileHasSnippets SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetSPDXID: SPDXRef-Snippet20 SnippetFromFileSPDXID: SPDXRef-FileHasSnippets SnippetByteRange: 268:309 SnippetLicenseConcluded: WTFPL SnippetCopyrightText: NOASSERTION ##### Other Licenses LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 LicenseID: LicenseRef-2 ExtractedText: License 2 text - this is a license that does some stuff LicenseName: License 2 ##### Relationships Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-p1 Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File1231 Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File1232 ##### Annotations Annotator: Person: John Doe AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document Annotator: Organization: John Doe, Inc. AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-p1 AnnotationComment: This is an annotation about Package p1 ##### Reviews Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z Reviewer: Organization: Jane Doe LLC ReviewDate: 2018-10-14T10:28:00Z ReviewComment: I have reviewed this SPDX document and it is awesome `) // render as buffer of bytes var got bytes.Buffer err := RenderDocument(doc, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected {{{%v}}}, got {{{%v}}}", want.String(), got.String()) } } func TestSaverDocumentReturnsErrorIfNilCreationInfo(t *testing.T) { doc := &spdx.Document{} var got bytes.Buffer err := RenderDocument(doc, &got) if err == nil { t.Errorf("Expected error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_file.go000066400000000000000000000040071463371440000236150ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func renderFile(f *spdx.File, w io.Writer) error { if f.FileName != "" { fmt.Fprintf(w, "FileName: %s\n", f.FileName) } if f.FileSPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(f.FileSPDXIdentifier)) } for _, s := range f.FileTypes { fmt.Fprintf(w, "FileType: %s\n", s) } for _, checksum := range f.Checksums { fmt.Fprintf(w, "FileChecksum: %s: %s\n", checksum.Algorithm, checksum.Value) } if f.LicenseConcluded != "" { fmt.Fprintf(w, "LicenseConcluded: %s\n", f.LicenseConcluded) } for _, s := range f.LicenseInfoInFiles { fmt.Fprintf(w, "LicenseInfoInFile: %s\n", s) } if f.LicenseComments != "" { fmt.Fprintf(w, "LicenseComments: %s\n", textify(f.LicenseComments)) } if f.FileCopyrightText != "" { fmt.Fprintf(w, "FileCopyrightText: %s\n", textify(f.FileCopyrightText)) } for _, aop := range f.ArtifactOfProjects { fmt.Fprintf(w, "ArtifactOfProjectName: %s\n", aop.Name) if aop.HomePage != "" { fmt.Fprintf(w, "ArtifactOfProjectHomePage: %s\n", aop.HomePage) } if aop.URI != "" { fmt.Fprintf(w, "ArtifactOfProjectURI: %s\n", aop.URI) } } if f.FileComment != "" { fmt.Fprintf(w, "FileComment: %s\n", textify(f.FileComment)) } if f.FileNotice != "" { fmt.Fprintf(w, "FileNotice: %s\n", textify(f.FileNotice)) } for _, s := range f.FileContributors { fmt.Fprintf(w, "FileContributor: %s\n", s) } for _, s := range f.FileDependencies { fmt.Fprintf(w, "FileDependency: %s\n", s) } fmt.Fprintf(w, "\n") // also render any snippets for this file // get slice of Snippet identifiers so we can sort them snippetKeys := []string{} for k := range f.Snippets { snippetKeys = append(snippetKeys, string(k)) } sort.Strings(snippetKeys) for _, sID := range snippetKeys { s := f.Snippets[common.ElementID(sID)] renderSnippet(s, w) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_file_test.go000066400000000000000000000215521463371440000246600ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== File section Saver tests ===== func TestSaverFileSavesText(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), FileTypes: []string{ "TEXT", "DOCUMENTATION", }, Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, {Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"}, {Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", "Apache-1.1", }, LicenseComments: "this is a license comment(s)", FileCopyrightText: "Copyright (c) Jane Doe", ArtifactOfProjects: []*spdx.ArtifactOfProject{ &spdx.ArtifactOfProject{ Name: "project1", HomePage: "http://example.com/1/", URI: "http://example.com/1/uri.whatever", }, &spdx.ArtifactOfProject{ Name: "project2", }, &spdx.ArtifactOfProject{ Name: "project3", HomePage: "http://example.com/3/", }, &spdx.ArtifactOfProject{ Name: "project4", URI: "http://example.com/4/uri.whatever", }, }, FileComment: "this is a file comment", FileNotice: "This file may be used under either Apache-2.0 or Apache-1.1.", FileContributors: []string{ "John Doe jdoe@example.com", "EvilCorp", }, FileDependencies: []string{ "f-1.txt", "g.txt", }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileType: TEXT FileType: DOCUMENTATION FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c FileChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd FileChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 LicenseInfoInFile: Apache-1.1 LicenseComments: this is a license comment(s) FileCopyrightText: Copyright (c) Jane Doe ArtifactOfProjectName: project1 ArtifactOfProjectHomePage: http://example.com/1/ ArtifactOfProjectURI: http://example.com/1/uri.whatever ArtifactOfProjectName: project2 ArtifactOfProjectName: project3 ArtifactOfProjectHomePage: http://example.com/3/ ArtifactOfProjectName: project4 ArtifactOfProjectURI: http://example.com/4/uri.whatever FileComment: this is a file comment FileNotice: This file may be used under either Apache-2.0 or Apache-1.1. FileContributor: John Doe jdoe@example.com FileContributor: EvilCorp FileDependency: f-1.txt FileDependency: g.txt `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileSavesSnippetsAlso(t *testing.T) { sn1 := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet19"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File123").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}}}, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } sn2 := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet20"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File123").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 268}, EndPointer: common.SnippetRangePointer{Offset: 309}}}, SnippetLicenseConcluded: "WTFPL", SnippetCopyrightText: "NOASSERTION", } sns := map[common.ElementID]*spdx.Snippet{ common.ElementID("Snippet19"): sn1, common.ElementID("Snippet20"): sn2, } f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", Snippets: sns, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe SnippetSPDXID: SPDXRef-Snippet19 SnippetFromFileSPDXID: SPDXRef-File123 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetSPDXID: SPDXRef-Snippet20 SnippetFromFileSPDXID: SPDXRef-File123 SnippetByteRange: 268:309 SnippetLicenseConcluded: WTFPL SnippetCopyrightText: NOASSERTION `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileOmitsOptionalFieldsIfEmpty(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileWrapsCopyrightMultiLine(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: `Copyright (c) Jane Doe Copyright (c) John Doe`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe Copyright (c) John Doe `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileWrapsCommentsAndNoticesMultiLine(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseComments: `this is a multi-line license comment`, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", FileComment: `this is a multi-line file comment`, FileNotice: `This file may be used under either Apache-2.0 or Apache-1.1.`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 LicenseComments: this is a multi-line license comment FileCopyrightText: Copyright (c) Jane Doe FileComment: this is a multi-line file comment FileNotice: This file may be used under either Apache-2.0 or Apache-1.1. `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_other_license.go000066400000000000000000000013421463371440000255200ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func renderOtherLicense(ol *spdx.OtherLicense, w io.Writer) error { if ol.LicenseIdentifier != "" { fmt.Fprintf(w, "LicenseID: %s\n", ol.LicenseIdentifier) } if ol.ExtractedText != "" { fmt.Fprintf(w, "ExtractedText: %s\n", textify(ol.ExtractedText)) } if ol.LicenseName != "" { fmt.Fprintf(w, "LicenseName: %s\n", ol.LicenseName) } for _, s := range ol.LicenseCrossReferences { fmt.Fprintf(w, "LicenseCrossReference: %s\n", s) } if ol.LicenseComment != "" { fmt.Fprintf(w, "LicenseComment: %s\n", textify(ol.LicenseComment)) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_other_license_test.go000066400000000000000000000037461463371440000265710ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Other License section Saver tests ===== func TestSaverOtherLicenseSavesText(t *testing.T) { ol := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", LicenseCrossReferences: []string{ "http://example.com/License1/", "http://example.com/License1AnotherURL/", }, LicenseComment: "this is a license comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 LicenseCrossReference: http://example.com/License1/ LicenseCrossReference: http://example.com/License1AnotherURL/ LicenseComment: this is a license comment `) // render as buffer of bytes var got bytes.Buffer err := renderOtherLicense(ol, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverOtherLicenseOmitsOptionalFieldsIfEmpty(t *testing.T) { ol := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 `) // render as buffer of bytes var got bytes.Buffer err := renderOtherLicense(ol, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_package.go000066400000000000000000000073761463371440000243050ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "strings" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func renderPackage(pkg *spdx.Package, w io.Writer) error { if pkg.PackageName != "" { fmt.Fprintf(w, "PackageName: %s\n", pkg.PackageName) } if pkg.PackageSPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(pkg.PackageSPDXIdentifier)) } if pkg.PackageVersion != "" { fmt.Fprintf(w, "PackageVersion: %s\n", pkg.PackageVersion) } if pkg.PackageFileName != "" { fmt.Fprintf(w, "PackageFileName: %s\n", pkg.PackageFileName) } if pkg.PackageSupplier != nil && pkg.PackageSupplier.Supplier != "" { if pkg.PackageSupplier.SupplierType == "" { fmt.Fprintf(w, "PackageSupplier: %s\n", pkg.PackageSupplier.Supplier) } else { fmt.Fprintf(w, "PackageSupplier: %s: %s\n", pkg.PackageSupplier.SupplierType, pkg.PackageSupplier.Supplier) } } if pkg.PackageOriginator != nil && pkg.PackageOriginator.Originator != "" { if pkg.PackageOriginator.OriginatorType == "" { fmt.Fprintf(w, "PackageOriginator: %s\n", pkg.PackageOriginator.Originator) } else { fmt.Fprintf(w, "PackageOriginator: %s: %s\n", pkg.PackageOriginator.OriginatorType, pkg.PackageOriginator.Originator) } } if pkg.PackageDownloadLocation != "" { fmt.Fprintf(w, "PackageDownloadLocation: %s\n", pkg.PackageDownloadLocation) } if pkg.FilesAnalyzed { if pkg.IsFilesAnalyzedTagPresent { fmt.Fprintf(w, "FilesAnalyzed: true\n") } } else { fmt.Fprintf(w, "FilesAnalyzed: false\n") } if pkg.PackageVerificationCode.Value != "" && pkg.FilesAnalyzed { if len(pkg.PackageVerificationCode.ExcludedFiles) == 0 { fmt.Fprintf(w, "PackageVerificationCode: %s\n", pkg.PackageVerificationCode.Value) } else { fmt.Fprintf(w, "PackageVerificationCode: %s (excludes: %s)\n", pkg.PackageVerificationCode.Value, strings.Join(pkg.PackageVerificationCode.ExcludedFiles, ", ")) } } for _, checksum := range pkg.PackageChecksums { fmt.Fprintf(w, "PackageChecksum: %s: %s\n", checksum.Algorithm, checksum.Value) } if pkg.PackageHomePage != "" { fmt.Fprintf(w, "PackageHomePage: %s\n", pkg.PackageHomePage) } if pkg.PackageSourceInfo != "" { fmt.Fprintf(w, "PackageSourceInfo: %s\n", textify(pkg.PackageSourceInfo)) } if pkg.PackageLicenseConcluded != "" { fmt.Fprintf(w, "PackageLicenseConcluded: %s\n", pkg.PackageLicenseConcluded) } if pkg.FilesAnalyzed { for _, s := range pkg.PackageLicenseInfoFromFiles { fmt.Fprintf(w, "PackageLicenseInfoFromFiles: %s\n", s) } } if pkg.PackageLicenseDeclared != "" { fmt.Fprintf(w, "PackageLicenseDeclared: %s\n", pkg.PackageLicenseDeclared) } if pkg.PackageLicenseComments != "" { fmt.Fprintf(w, "PackageLicenseComments: %s\n", textify(pkg.PackageLicenseComments)) } if pkg.PackageCopyrightText != "" { fmt.Fprintf(w, "PackageCopyrightText: %s\n", textify(pkg.PackageCopyrightText)) } if pkg.PackageSummary != "" { fmt.Fprintf(w, "PackageSummary: %s\n", textify(pkg.PackageSummary)) } if pkg.PackageDescription != "" { fmt.Fprintf(w, "PackageDescription: %s\n", textify(pkg.PackageDescription)) } if pkg.PackageComment != "" { fmt.Fprintf(w, "PackageComment: %s\n", textify(pkg.PackageComment)) } for _, s := range pkg.PackageExternalReferences { fmt.Fprintf(w, "ExternalRef: %s %s %s\n", s.Category, s.RefType, s.Locator) if s.ExternalRefComment != "" { fmt.Fprintf(w, "ExternalRefComment: %s\n", textify(s.ExternalRefComment)) } } fmt.Fprintf(w, "\n") // also render any files for this package sort.Slice(pkg.Files, func(i, j int) bool { return pkg.Files[i].FileSPDXIdentifier < pkg.Files[j].FileSPDXIdentifier }) for _, fi := range pkg.Files { renderFile(fi, w) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_package_test.go000066400000000000000000000410321463371440000253270ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Package section Saver tests ===== func TestSaverPackageSavesTextCombo1(t *testing.T) { // include package external refs // test Supplier:Organization, Originator:Person // FilesAnalyzed true, IsFilesAnalyzedTagPresent true // PackageVerificationCodeExcludedFile has string // NOTE, this is an entirely made up CPE and the format is likely invalid per1 := &spdx.PackageExternalReference{ Category: "SECURITY", RefType: "cpe22Type", Locator: "cpe:/a:john_doe_inc:p1:0.1.0", ExternalRefComment: "this is an external ref comment #1", } // NOTE, this is an entirely made up NPM per2 := &spdx.PackageExternalReference{ Category: "PACKAGE-MANAGER", RefType: "npm", Locator: "p1@0.1.0", ExternalRefComment: `this is a multi-line external ref comment`, } per3 := &spdx.PackageExternalReference{ Category: "OTHER", RefType: "anything", Locator: "anything-without-spaces-can-go-here", // no ExternalRefComment for this one } pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{SupplierType: "Organization", Supplier: "John Doe, Inc."}, PackageOriginator: &common.Originator{Originator: "John Doe", OriginatorType: "Person"}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: common.PackageVerificationCode{ Value: "0123456789abcdef0123456789abcdef01234567", ExcludedFiles: []string{"p1-0.1.0.spdx"}, }, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", PackageExternalReferences: []*spdx.PackageExternalReference{ per1, per2, per3, }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: Organization: John Doe, Inc. PackageOriginator: Person: John Doe PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: true PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 (excludes: p1-0.1.0.spdx) PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseInfoFromFiles: Apache-1.1 PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment ExternalRef: SECURITY cpe22Type cpe:/a:john_doe_inc:p1:0.1.0 ExternalRefComment: this is an external ref comment #1 ExternalRef: PACKAGE-MANAGER npm p1@0.1.0 ExternalRefComment: this is a multi-line external ref comment ExternalRef: OTHER anything anything-without-spaces-can-go-here `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesTextCombo2(t *testing.T) { // no package external refs // test Supplier:NOASSERTION, Originator:Organization // FilesAnalyzed true, IsFilesAnalyzedTagPresent false // PackageVerificationCodeExcludedFile is empty pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{Supplier: "NOASSERTION"}, PackageOriginator: &common.Originator{OriginatorType: "Organization", Originator: "John Doe, Inc."}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, PackageVerificationCode: common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: NOASSERTION PackageOriginator: Organization: John Doe, Inc. PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseInfoFromFiles: Apache-1.1 PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesTextCombo3(t *testing.T) { // no package external refs // test Supplier:Person, Originator:NOASSERTION // FilesAnalyzed false, IsFilesAnalyzedTagPresent true // PackageVerificationCodeExcludedFile is empty pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{Supplier: "John Doe", SupplierType: "Person"}, PackageOriginator: &common.Originator{Originator: "NOASSERTION"}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output // since FilesAnalyzed is false PackageVerificationCode: common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: Person: John Doe PackageOriginator: NOASSERTION PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSaveOmitsOptionalFieldsIfEmpty(t *testing.T) { pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output, // even if present in model, since FilesAnalyzed is false PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // even if present in model, since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesFilesIfPresent(t *testing.T) { f1 := &spdx.File{ FileName: "/tmp/whatever1.txt", FileSPDXIdentifier: common.ElementID("File1231"), Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{"Apache-2.0"}, FileCopyrightText: "Copyright (c) Jane Doe", } f2 := &spdx.File{ FileName: "/tmp/whatever2.txt", FileSPDXIdentifier: common.ElementID("File1232"), Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983d", }, }, LicenseConcluded: "MIT", LicenseInfoInFiles: []string{"MIT"}, FileCopyrightText: "Copyright (c) John Doe", } pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output, // even if present in model, since FilesAnalyzed is false PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // even if present in model, since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", Files: []*spdx.File{ f1, f2, }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. FileName: /tmp/whatever1.txt SPDXID: SPDXRef-File1231 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe FileName: /tmp/whatever2.txt SPDXID: SPDXRef-File1232 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983d LicenseConcluded: MIT LicenseInfoInFile: MIT FileCopyrightText: Copyright (c) John Doe `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageWrapsCopyrightMultiLine(t *testing.T) { pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: `Copyright (c) John Doe, Inc. Copyright Jane Doe`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. Copyright Jane Doe `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_relationship.go000066400000000000000000000012251463371440000253760ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func renderRelationship(rln *spdx.Relationship, w io.Writer) error { rlnAStr := common.RenderDocElementID(rln.RefA) rlnBStr := common.RenderDocElementID(rln.RefB) if rlnAStr != "SPDXRef-" && rlnBStr != "SPDXRef-" && rln.Relationship != "" { fmt.Fprintf(w, "Relationship: %s %s %s\n", rlnAStr, rln.Relationship, rlnBStr) } if rln.RelationshipComment != "" { fmt.Fprintf(w, "RelationshipComment: %s\n", textify(rln.RelationshipComment)) } return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_relationship_test.go000066400000000000000000000050361463371440000264410ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Relationship section Saver tests ===== func TestSaverRelationshipSavesText(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", RelationshipComment: "this is a comment", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2 RelationshipComment: this is a comment `) // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipOmitsOptionalFieldsIfEmpty(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString("Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2\n") // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipWrapsCommentMultiLine(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", RelationshipComment: `this is a multi-line comment`, } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2 RelationshipComment: this is a multi-line comment `) // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_review.go000066400000000000000000000010541463371440000241760ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func renderReview(rev *spdx.Review, w io.Writer) error { if rev.Reviewer != "" && rev.ReviewerType != "" { fmt.Fprintf(w, "Reviewer: %s: %s\n", rev.ReviewerType, rev.Reviewer) } if rev.ReviewDate != "" { fmt.Fprintf(w, "ReviewDate: %s\n", rev.ReviewDate) } if rev.ReviewComment != "" { fmt.Fprintf(w, "ReviewComment: %s\n", textify(rev.ReviewComment)) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_review_test.go000066400000000000000000000043431463371440000252410ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Review section Saver tests ===== func TestSaverReviewSavesText(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: "this is a review comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z ReviewComment: this is a review comment `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverReviewOmitsOptionalFieldsIfEmpty(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverReviewWrapsMultiLine(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: `this is a multi-line review comment`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z ReviewComment: this is a multi-line review comment `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_snippet.go000066400000000000000000000032601463371440000243600ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) func renderSnippet(sn *spdx.Snippet, w io.Writer) error { if sn.SnippetSPDXIdentifier != "" { fmt.Fprintf(w, "SnippetSPDXID: %s\n", common.RenderElementID(sn.SnippetSPDXIdentifier)) } snFromFileIDStr := common.RenderElementID(sn.SnippetFromFileSPDXIdentifier) if snFromFileIDStr != "" { fmt.Fprintf(w, "SnippetFromFileSPDXID: %s\n", snFromFileIDStr) } for _, snippetRange := range sn.Ranges { if snippetRange.StartPointer.Offset != 0 && snippetRange.EndPointer.Offset != 0 { fmt.Fprintf(w, "SnippetByteRange: %d:%d\n", snippetRange.StartPointer.Offset, snippetRange.EndPointer.Offset) } if snippetRange.StartPointer.LineNumber != 0 && snippetRange.EndPointer.LineNumber != 0 { fmt.Fprintf(w, "SnippetLineRange: %d:%d\n", snippetRange.StartPointer.LineNumber, snippetRange.EndPointer.LineNumber) } } if sn.SnippetLicenseConcluded != "" { fmt.Fprintf(w, "SnippetLicenseConcluded: %s\n", sn.SnippetLicenseConcluded) } for _, s := range sn.LicenseInfoInSnippet { fmt.Fprintf(w, "LicenseInfoInSnippet: %s\n", s) } if sn.SnippetLicenseComments != "" { fmt.Fprintf(w, "SnippetLicenseComments: %s\n", textify(sn.SnippetLicenseComments)) } if sn.SnippetCopyrightText != "" { fmt.Fprintf(w, "SnippetCopyrightText: %s\n", textify(sn.SnippetCopyrightText)) } if sn.SnippetComment != "" { fmt.Fprintf(w, "SnippetComment: %s\n", textify(sn.SnippetComment)) } if sn.SnippetName != "" { fmt.Fprintf(w, "SnippetName: %s\n", sn.SnippetName) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/save_snippet_test.go000066400000000000000000000101221463371440000254120ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_1" ) // ===== Snippet section Saver tests ===== func TestSaverSnippetSavesText(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{LineNumber: 3}, EndPointer: common.SnippetRangePointer{LineNumber: 8}, }, { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", LicenseInfoInSnippet: []string{ "GPL-2.0-or-later", "MIT", }, SnippetLicenseComments: "this is a comment(s) about the snippet license", SnippetCopyrightText: "Copyright (c) John Doe 20x6", SnippetComment: "this is a snippet comment", SnippetName: "from John's program", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetLineRange: 3:8 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later LicenseInfoInSnippet: GPL-2.0-or-later LicenseInfoInSnippet: MIT SnippetLicenseComments: this is a comment(s) about the snippet license SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetComment: this is a snippet comment SnippetName: from John's program `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverSnippetOmitsOptionalFieldsIfEmpty(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverSnippetWrapsCopyrightMultiline(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: `Copyright (c) John Doe 20x6 Copyright (c) John Doe 20x6`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 Copyright (c) John Doe 20x6 `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/util.go000066400000000000000000000003451463371440000226360ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "strings" ) func textify(s string) string { if strings.Contains(s, "\n") { return fmt.Sprintf("%s", s) } return s } tools-golang-0.5.5/spdx/v2/v2_1/tagvalue/writer/util_test.go000066400000000000000000000011011463371440000236640ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "testing" ) // ===== Utility function tests ===== func TestTextifyWrapsStringWithNewline(t *testing.T) { s := `this text has a newline in it` want := `this text has a newline in it` got := textify(s) if want != got { t.Errorf("Expected %s, got %s", want, got) } } func TestTextifyDoesNotWrapsStringWithNoNewline(t *testing.T) { s := `this text has no newline in it` want := s got := textify(s) if want != got { t.Errorf("Expected %s, got %s", want, got) } } tools-golang-0.5.5/spdx/v2/v2_2/000077500000000000000000000000001463371440000162055ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/annotation.go000066400000000000000000000021761463371440000207140ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Annotation is an Annotation section of an SPDX Document for version 2.2 of the spec. type Annotation struct { // 12.1: Annotator // Cardinality: conditional (mandatory, one) if there is an Annotation Annotator common.Annotator `json:"annotator"` // 12.2: Annotation Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationDate string `json:"annotationDate"` // 12.3: Annotation Type: "REVIEW" or "OTHER" // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationType string `json:"annotationType"` // 12.4: SPDX Identifier Reference // Cardinality: conditional (mandatory, one) if there is an Annotation // This field is not used in hierarchical data formats where the referenced element is clear, such as JSON or YAML. AnnotationSPDXIdentifier common.DocElementID `json:"-"` // 12.5: Annotation Comment // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationComment string `json:"comment"` } tools-golang-0.5.5/spdx/v2/v2_2/creation_info.go000066400000000000000000000014361463371440000213570ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // CreationInfo is a Document Creation Information section of an // SPDX Document for version 2.2 of the spec. type CreationInfo struct { // 6.7: License List Version // Cardinality: optional, one LicenseListVersion string `json:"licenseListVersion,omitempty"` // 6.8: Creators: may have multiple keys for Person, Organization // and/or Tool // Cardinality: mandatory, one or many Creators []common.Creator `json:"creators"` // 6.9: Created: data format YYYY-MM-DDThh:mm:ssZ // Cardinality: mandatory, one Created string `json:"created"` // 6.10: Creator Comment // Cardinality: optional, one CreatorComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_2/document.go000066400000000000000000000114671463371440000203630ustar00rootroot00000000000000// Package spdx contains the struct definition for an SPDX Document // and its constituent parts. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 import ( "encoding/json" "fmt" converter "github.com/anchore/go-struct-converter" "github.com/spdx/tools-golang/spdx/v2/common" ) const Version = "SPDX-2.2" const DataLicense = "CC0-1.0" // ExternalDocumentRef is a reference to an external SPDX document // as defined in section 6.6 for version 2.2 of the spec. type ExternalDocumentRef struct { // DocumentRefID is the ID string defined in the start of the // reference. It should _not_ contain the "DocumentRef-" part // of the mandatory ID string. DocumentRefID string `json:"externalDocumentId"` // URI is the URI defined for the external document URI string `json:"spdxDocument"` // Checksum is the actual hash data Checksum common.Checksum `json:"checksum"` } // Document is an SPDX Document for version 2.2 of the spec. // See https://spdx.github.io/spdx-spec/v2-draft/ (DRAFT) type Document struct { // 6.1: SPDX Version; should be in the format "SPDX-2.2" // Cardinality: mandatory, one SPDXVersion string `json:"spdxVersion"` // 6.2: Data License; should be "CC0-1.0" // Cardinality: mandatory, one DataLicense string `json:"dataLicense"` // 6.3: SPDX Identifier; should be "DOCUMENT" to represent // mandatory identifier of SPDXRef-DOCUMENT // Cardinality: mandatory, one SPDXIdentifier common.ElementID `json:"SPDXID"` // 6.4: Document Name // Cardinality: mandatory, one DocumentName string `json:"name"` // 6.5: Document Namespace // Cardinality: mandatory, one DocumentNamespace string `json:"documentNamespace"` // 6.6: External Document References // Cardinality: optional, one or many ExternalDocumentReferences []ExternalDocumentRef `json:"externalDocumentRefs,omitempty"` // 6.11: Document Comment // Cardinality: optional, one DocumentComment string `json:"comment,omitempty"` CreationInfo *CreationInfo `json:"creationInfo"` Packages []*Package `json:"packages,omitempty"` Files []*File `json:"files,omitempty"` OtherLicenses []*OtherLicense `json:"hasExtractedLicensingInfos,omitempty"` Relationships []*Relationship `json:"relationships,omitempty"` Annotations []*Annotation `json:"annotations,omitempty"` Snippets []Snippet `json:"snippets,omitempty"` // DEPRECATED in version 2.0 of spec Reviews []*Review `json:"-"` } func (d *Document) ConvertFrom(_ interface{}) error { d.SPDXVersion = Version return nil } var _ converter.ConvertFrom = (*Document)(nil) func (d *Document) UnmarshalJSON(b []byte) error { type doc Document type extras struct { DocumentDescribes []common.DocElementID `json:"documentDescribes"` } var d2 doc if err := json.Unmarshal(b, &d2); err != nil { return err } var e extras if err := json.Unmarshal(b, &e); err != nil { return err } *d = Document(d2) relationshipExists := map[string]bool{} serializeRel := func(r *Relationship) string { refA := r.RefA refB := r.RefB rel := r.Relationship // we need to serialize the opposite for CONTAINED_BY and DESCRIBED_BY // so that it will match when we try to de-duplicate during deserialization. switch r.Relationship { case common.TypeRelationshipContainedBy: rel = common.TypeRelationshipContains refA = r.RefB refB = r.RefA case common.TypeRelationshipDescribeBy: rel = common.TypeRelationshipDescribe refA = r.RefB refB = r.RefA } return fmt.Sprintf("%v-%v->%v", common.RenderDocElementID(refA), rel, common.RenderDocElementID(refB)) } // remove null relationships for i := 0; i < len(d.Relationships); i++ { if d.Relationships[i] == nil { d.Relationships = append(d.Relationships[0:i], d.Relationships[i+1:]...) i-- } } // index current list of relationships to ensure no duplication for _, r := range d.Relationships { relationshipExists[serializeRel(r)] = true } // build relationships for documentDescribes field for _, id := range e.DocumentDescribes { r := &Relationship{ RefA: common.DocElementID{ ElementRefID: d.SPDXIdentifier, }, RefB: id, Relationship: common.TypeRelationshipDescribe, } if !relationshipExists[serializeRel(r)] { d.Relationships = append(d.Relationships, r) relationshipExists[serializeRel(r)] = true } } // build relationships for package hasFiles field for _, p := range d.Packages { for _, f := range p.hasFiles { r := &Relationship{ RefA: common.DocElementID{ ElementRefID: p.PackageSPDXIdentifier, }, RefB: f, Relationship: common.TypeRelationshipContains, } if !relationshipExists[serializeRel(r)] { d.Relationships = append(d.Relationships, r) relationshipExists[serializeRel(r)] = true } } p.hasFiles = nil } return nil } var _ json.Unmarshaler = (*Document)(nil) tools-golang-0.5.5/spdx/v2/v2_2/example/000077500000000000000000000000001463371440000176405ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/example/example.go000066400000000000000000000563161463371440000216350ustar00rootroot00000000000000package example import ( "fmt" converter "github.com/anchore/go-struct-converter" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // Copy provides a deep copy of the example func Copy() spdx.Document { out := spdx.Document{} err := converter.Convert(example, &out) if err != nil { panic(fmt.Errorf("unable to convert example doc: %w", err)) } return out } // Example is handwritten translation of an official example SPDX document into a Go struct. // We expect that the result of parsing the official document should be this value. // We expect that the result of writing this struct should match the official example document. var example = spdx.Document{ DataLicense: spdx.DataLicense, SPDXVersion: spdx.Version, SPDXIdentifier: "DOCUMENT", DocumentName: "SPDX-Tools-v2.0", DocumentNamespace: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", CreationInfo: &spdx.CreationInfo{ LicenseListVersion: "3.9", Creators: []common.Creator{ {CreatorType: "Tool", Creator: "LicenseFind-1.0"}, {CreatorType: "Organization", Creator: "ExampleCodeInspect ()"}, {CreatorType: "Person", Creator: "Jane Doe ()"}, }, Created: "2010-01-29T18:30:22Z", CreatorComment: "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.", }, DocumentComment: "This document was created using SPDX 2.0 using licenses from the web site.", ExternalDocumentReferences: []spdx.ExternalDocumentRef{ { DocumentRefID: "DocumentRef-spdx-tool-1.2", URI: "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", Checksum: common.Checksum{ Algorithm: common.SHA1, Value: "d6a770ba38583ed4bb4525bd96e50461655d2759", }, }, }, OtherLicenses: []*spdx.OtherLicense{ { LicenseIdentifier: "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*/", }, { LicenseIdentifier: "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.", }, { LicenseIdentifier: "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*/", }, { LicenseIdentifier: "LicenseRef-Beerware-4.2", 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", LicenseComment: "The beerware license has a couple of other standard variants.", LicenseName: "Beer-Ware License (Version 42)", LicenseCrossReferences: []string{"http://people.freebsd.org/~phk/"}, }, { LicenseIdentifier: "LicenseRef-3", 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.", LicenseName: "CyberNeko License", LicenseCrossReferences: []string{ "http://people.apache.org/~andyc/neko/LICENSE", "http://justasample.url.com", }, LicenseComment: "This is tye CyperNeko License", }, }, Annotations: []*spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "Jane Doe ()", AnnotatorType: "Person", }, AnnotationDate: "2010-01-29T18:30:22Z", AnnotationType: "OTHER", AnnotationComment: "Document level annotation", }, { Annotator: common.Annotator{ Annotator: "Joe Reviewer", AnnotatorType: "Person", }, AnnotationDate: "2010-02-10T00:00:00Z", AnnotationType: "REVIEW", AnnotationComment: "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses", }, { Annotator: common.Annotator{ Annotator: "Suzanne Reviewer", AnnotatorType: "Person", }, AnnotationDate: "2011-03-13T00:00:00Z", AnnotationType: "REVIEW", AnnotationComment: "Another example reviewer.", }, }, Packages: []*spdx.Package{ { PackageName: "glibc", PackageSPDXIdentifier: "Package", PackageVersion: "2.11.1", PackageFileName: "glibc-2.11.1.tar.gz", PackageSupplier: &common.Supplier{ Supplier: "Jane Doe (jane.doe@example.com)", SupplierType: "Person", }, PackageOriginator: &common.Originator{ Originator: "ExampleCodeInspect (contact@example.com)", OriginatorType: "Organization", }, PackageDownloadLocation: "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: common.PackageVerificationCode{ Value: "d6a770ba38583ed4bb4525bd96e50461655d2758", ExcludedFiles: []string{"./package.spdx"}, }, PackageChecksums: []common.Checksum{ { Algorithm: "MD5", Value: "624c1abb3664f4b35547e7c73864ad24", }, { Algorithm: "SHA1", Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: "SHA256", Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, }, PackageHomePage: "http://ftp.gnu.org/gnu/glibc", PackageSourceInfo: "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.", PackageLicenseConcluded: "(LGPL-2.0-only OR LicenseRef-3)", PackageLicenseInfoFromFiles: []string{ "GPL-2.0-only", "LicenseRef-2", "LicenseRef-1", }, PackageLicenseDeclared: "(LGPL-2.0-only AND LicenseRef-3)", PackageLicenseComments: "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.", PackageCopyrightText: "Copyright 2008-2010 John Smith", PackageSummary: "GNU C library.", PackageDescription: "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.", PackageComment: "", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: "SECURITY", RefType: "cpe23Type", Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", }, { Category: "OTHER", RefType: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge", Locator: "acmecorp/acmenator/4.1.3-alpha", ExternalRefComment: "This is the external ref for Acme", }, }, PackageAttributionTexts: []string{ "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.", }, Files: nil, Annotations: []spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "Package Commenter", AnnotatorType: "Person", }, AnnotationDate: "2011-01-29T18:30:22Z", AnnotationType: "OTHER", AnnotationComment: "Package level annotation", }, }, }, { PackageSPDXIdentifier: "fromDoap-1", PackageCopyrightText: "NOASSERTION", PackageDownloadLocation: "NOASSERTION", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, PackageHomePage: "http://commons.apache.org/proper/commons-lang/", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", PackageName: "Apache Commons Lang", }, { PackageName: "Jena", PackageSPDXIdentifier: "fromDoap-0", PackageCopyrightText: "NOASSERTION", PackageDownloadLocation: "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: "PACKAGE-MANAGER", RefType: "purl", Locator: "pkg:maven/org.apache.jena/apache-jena@3.12.0", }, }, FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, PackageHomePage: "http://www.openjena.org/", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", PackageVersion: "3.12.0", }, { PackageSPDXIdentifier: "Saxon", PackageChecksums: []common.Checksum{ { Algorithm: "SHA1", Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, }, PackageCopyrightText: "Copyright Saxonica Ltd", PackageDescription: "The Saxon package is a collection of tools for processing XML documents.", PackageDownloadLocation: "https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, PackageHomePage: "http://saxon.sourceforge.net/", PackageLicenseComments: "Other versions available for a commercial license", PackageLicenseConcluded: "MPL-1.0", PackageLicenseDeclared: "MPL-1.0", PackageName: "Saxon", PackageFileName: "saxonB-8.8.zip", PackageVersion: "8.8", }, }, Files: []*spdx.File{ { FileName: "./src/org/spdx/parser/DOAPProject.java", FileSPDXIdentifier: "DoapSource", FileTypes: []string{ "SOURCE", }, Checksums: []common.Checksum{ { Algorithm: "SHA1", Value: "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", }, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright 2010, 2011 Source Auditor Inc.", FileContributors: []string{ "Protecode Inc.", "SPDX Technical Team Members", "Open Logic Inc.", "Source Auditor Inc.", "Black Duck Software In.c", }, }, { FileSPDXIdentifier: "CommonsLangSrc", Checksums: []common.Checksum{ { Algorithm: "SHA1", Value: "c2b4e1c67a2d28fced849ee1bb76e7391b93f125", }, }, FileComment: "This file is used by Jena", FileCopyrightText: "Copyright 2001-2011 The Apache Software Foundation", FileContributors: []string{"Apache Software Foundation"}, FileName: "./lib-source/commons-lang3-3.1-sources.jar", FileTypes: []string{"ARCHIVE"}, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{"Apache-2.0"}, FileNotice: "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())", }, { FileSPDXIdentifier: "JenaLib", Checksums: []common.Checksum{ { Algorithm: "SHA1", Value: "3ab4e1c67a2d28fced849ee1bb76e7391b93f125", }, }, FileComment: "This file belongs to Jena", FileCopyrightText: "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP", FileContributors: []string{"Apache Software Foundation", "Hewlett Packard Inc."}, FileName: "./lib-source/jena-2.6.3-sources.jar", FileTypes: []string{"ARCHIVE"}, LicenseComments: "This license is used by Jena", LicenseConcluded: "LicenseRef-1", LicenseInfoInFiles: []string{"LicenseRef-1"}, }, { FileSPDXIdentifier: "File", Annotations: []spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "File Commenter", AnnotatorType: "Person", }, AnnotationDate: "2011-01-29T18:30:22Z", AnnotationType: "OTHER", AnnotationComment: "File level annotation", }, }, Checksums: []common.Checksum{ { Algorithm: "SHA1", Value: "d6a770ba38583ed4bb4525bd96e50461655d2758", }, { Algorithm: "MD5", Value: "624c1abb3664f4b35547e7c73864ad24", }, }, FileComment: "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.", FileCopyrightText: "Copyright 2008-2010 John Smith", FileContributors: []string{"The Regents of the University of California", "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"}, FileName: "./package/foo.c", FileTypes: []string{"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: []string{"GPL-2.0-only", "LicenseRef-2"}, FileNotice: "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: []spdx.Snippet{ { SnippetSPDXIdentifier: "Snippet", SnippetFromFileSPDXIdentifier: "DoapSource", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 310, FileSPDXIdentifier: "DoapSource", }, EndPointer: common.SnippetRangePointer{ Offset: 420, FileSPDXIdentifier: "DoapSource", }, }, { StartPointer: common.SnippetRangePointer{ LineNumber: 5, FileSPDXIdentifier: "DoapSource", }, EndPointer: common.SnippetRangePointer{ LineNumber: 23, FileSPDXIdentifier: "DoapSource", }, }, }, SnippetLicenseConcluded: "GPL-2.0-only", LicenseInfoInSnippet: []string{"GPL-2.0-only"}, SnippetLicenseComments: "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.", SnippetCopyrightText: "Copyright 2008-2010 John Smith", SnippetComment: "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.", SnippetName: "from linux kernel", }, }, Relationships: []*spdx.Relationship{ { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "Package"), Relationship: "CONTAINS", }, { RefA: common.MakeDocElementID("", "Package"), RefB: common.MakeDocElementID("", "CommonsLangSrc"), Relationship: "CONTAINS", }, { RefA: common.MakeDocElementID("", "Package"), RefB: common.MakeDocElementID("", "DoapSource"), Relationship: "CONTAINS", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("spdx-tool-1.2", "ToolsElement"), Relationship: "COPY_OF", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "File"), Relationship: "DESCRIBES", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "Package"), Relationship: "DESCRIBES", }, { RefA: common.MakeDocElementID("", "Package"), RefB: common.MakeDocElementID("", "JenaLib"), Relationship: "CONTAINS", }, { RefA: common.MakeDocElementID("", "Package"), RefB: common.MakeDocElementID("", "Saxon"), Relationship: "DYNAMIC_LINK", }, { RefA: common.MakeDocElementID("", "CommonsLangSrc"), RefB: common.MakeDocElementSpecial("NOASSERTION"), Relationship: "GENERATED_FROM", }, { RefA: common.MakeDocElementID("", "JenaLib"), RefB: common.MakeDocElementID("", "Package"), Relationship: "CONTAINS", }, { RefA: common.MakeDocElementID("", "File"), RefB: common.MakeDocElementID("", "fromDoap-0"), Relationship: "GENERATED_FROM", }, }, } tools-golang-0.5.5/spdx/v2/v2_2/file.go000066400000000000000000000061201463371440000174520ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // File is a File section of an SPDX Document for version 2.2 of the spec. type File struct { // 8.1: File Name // Cardinality: mandatory, one FileName string `json:"fileName"` // 8.2: File SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one FileSPDXIdentifier common.ElementID `json:"SPDXID"` // 8.3: File Types // Cardinality: optional, multiple FileTypes []string `json:"fileTypes,omitempty"` // 8.4: File Checksum: may have keys for SHA1, SHA256 and/or MD5 // Cardinality: mandatory, one SHA1, others may be optionally provided Checksums []common.Checksum `json:"checksums"` // 8.5: Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one LicenseConcluded string `json:"licenseConcluded"` // 8.6: License Information in File: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one or many LicenseInfoInFiles []string `json:"licenseInfoInFiles"` // 8.7: Comments on License // Cardinality: optional, one LicenseComments string `json:"licenseComments,omitempty"` // 8.8: Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: mandatory, one FileCopyrightText string `json:"copyrightText"` // DEPRECATED in version 2.1 of spec // 8.9-8.11: Artifact of Project variables (defined below) // Cardinality: optional, one or many ArtifactOfProjects []*ArtifactOfProject `json:"-"` // 8.12: File Comment // Cardinality: optional, one FileComment string `json:"comment,omitempty"` // 8.13: File Notice // Cardinality: optional, one FileNotice string `json:"noticeText,omitempty"` // 8.14: File Contributor // Cardinality: optional, one or many FileContributors []string `json:"fileContributors,omitempty"` // 8.15: File Attribution Text // Cardinality: optional, one or many FileAttributionTexts []string `json:"attributionTexts,omitempty"` // DEPRECATED in version 2.0 of spec // 8.16: File Dependencies // Cardinality: optional, one or many FileDependencies []string `json:"-"` // Snippets contained in this File // Note that Snippets could be defined in a different Document! However, // the only ones that _THIS_ document can contain are this ones that are // defined here -- so this should just be an ElementID. Snippets map[common.ElementID]*Snippet `json:"-"` Annotations []Annotation `json:"annotations,omitempty"` } // ArtifactOfProject is a DEPRECATED collection of data regarding // a Package, as defined in sections 8.9-8.11 in version 2.2 of the spec. type ArtifactOfProject struct { // DEPRECATED in version 2.1 of spec // 8.9: Artifact of Project Name // Cardinality: conditional, required if present, one per AOP Name string // DEPRECATED in version 2.1 of spec // 8.10: Artifact of Project Homepage: URL or "UNKNOWN" // Cardinality: optional, one per AOP HomePage string // DEPRECATED in version 2.1 of spec // 8.11: Artifact of Project Uniform Resource Identifier // Cardinality: optional, one per AOP URI string } tools-golang-0.5.5/spdx/v2/v2_2/json/000077500000000000000000000000001463371440000171565ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/json/empty_values_test.go000066400000000000000000000030241463371440000232600ustar00rootroot00000000000000package json import ( "encoding/json" "testing" "github.com/stretchr/testify/require" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func Test_omitsAppropriateProperties(t *testing.T) { tests := []struct { name string pkg spdx.Package validate func(t *testing.T, got map[string]interface{}) }{ { name: "include packageVerificationCode exclusions", pkg: spdx.Package{ PackageVerificationCode: common.PackageVerificationCode{ ExcludedFiles: []string{}, }, }, validate: func(t *testing.T, got map[string]interface{}) { require.Contains(t, got, "packageVerificationCode") }, }, { name: "include packageVerificationCode value", pkg: spdx.Package{ PackageVerificationCode: common.PackageVerificationCode{ Value: "1234", }, }, validate: func(t *testing.T, got map[string]interface{}) { require.Contains(t, got, "packageVerificationCode") }, }, { name: "omit empty packageVerificationCode", pkg: spdx.Package{ PackageVerificationCode: common.PackageVerificationCode{}, }, validate: func(t *testing.T, got map[string]interface{}) { require.NotContains(t, got, "packageVerificationCode") }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, err := json.Marshal(test.pkg) require.NoError(t, err) var unmarshalled map[string]interface{} err = json.Unmarshal(got, &unmarshalled) require.NoError(t, err) test.validate(t, unmarshalled) }) } } tools-golang-0.5.5/spdx/v2/v2_2/json/json_test.go000066400000000000000000000272471463371440000215310ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package json import ( "bytes" jsonenc "encoding/json" "fmt" "os" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/require" "github.com/spdx/tools-golang/json" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" "github.com/spdx/tools-golang/spdx/v2/v2_2/example" ) func TestLoad(t *testing.T) { want := example.Copy() file, err := os.Open("../../../../examples/sample-docs/json/SPDXJSONExample-v2.2.spdx.json") if err != nil { panic(fmt.Errorf("error opening File: %s", err)) } var got spdx.Document err = json.ReadInto(file, &got) if err != nil { t.Errorf("json.parser.Load() error = %v", err) return } if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing JSON example: %s", diff) return } } func Test_nullRelationships(t *testing.T) { file, err := os.Open("testdata/spdx-null-relationships.json") if err != nil { panic(fmt.Errorf("error opening File: %s", err)) } var got spdx.Document err = json.ReadInto(file, &got) if err != nil { t.Errorf("json.parser.Load() error = %v", err) return } require.Len(t, got.Relationships, 2) for _, r := range got.Relationships { require.NotNil(t, r) } } func Test_Write(t *testing.T) { want := example.Copy() // we always output FilesAnalyzed, even though we handle reading files where it is omitted for _, p := range want.Packages { p.IsFilesAnalyzedTagPresent = true } w := &bytes.Buffer{} if err := json.Write(&want, w); err != nil { t.Errorf("Write() error = %v", err.Error()) return } // we should be able to parse what the writer wrote, and it should be identical to the original struct we wrote var got spdx.Document err := json.ReadInto(bytes.NewReader(w.Bytes()), &got) if err != nil { t.Errorf("failed to parse written document: %v", err.Error()) return } if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after writing and re-parsing JSON example: %s", diff) return } } func Test_ShorthandFields(t *testing.T) { contents := `{ "spdxVersion": "SPDX-2.2", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "SPDX-Tools-v2.0", "documentDescribes": [ "SPDXRef-Container" ], "packages": [ { "name": "Container", "SPDXID": "SPDXRef-Container" }, { "name": "Package-1", "SPDXID": "SPDXRef-Package-1", "versionInfo": "1.1.1", "hasFiles": [ "SPDXRef-File-1", "SPDXRef-File-2" ] }, { "name": "Package-2", "SPDXID": "SPDXRef-Package-2", "versionInfo": "2.2.2" } ], "files": [ { "fileName": "./f1", "SPDXID": "SPDXRef-File-1" }, { "fileName": "./f2", "SPDXID": "SPDXRef-File-2" } ] }` doc := spdx.Document{} err := json.ReadInto(strings.NewReader(contents), &doc) require.NoError(t, err) id := func(s string) common.DocElementID { return common.DocElementID{ ElementRefID: common.ElementID(s), } } want := spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: "DOCUMENT", DocumentName: "SPDX-Tools-v2.0", Packages: []*spdx.Package{ { PackageName: "Container", PackageSPDXIdentifier: "Container", FilesAnalyzed: true, }, { PackageName: "Package-1", PackageSPDXIdentifier: "Package-1", PackageVersion: "1.1.1", FilesAnalyzed: true, }, { PackageName: "Package-2", PackageSPDXIdentifier: "Package-2", PackageVersion: "2.2.2", FilesAnalyzed: true, }, }, Files: []*spdx.File{ { FileName: "./f1", FileSPDXIdentifier: "File-1", }, { FileName: "./f2", FileSPDXIdentifier: "File-2", }, }, Relationships: []*spdx.Relationship{ { RefA: id("DOCUMENT"), RefB: id("Container"), Relationship: common.TypeRelationshipDescribe, }, { RefA: id("Package-1"), RefB: id("File-1"), Relationship: common.TypeRelationshipContains, }, { RefA: id("Package-1"), RefB: id("File-2"), Relationship: common.TypeRelationshipContains, }, }, } if diff := cmp.Diff(want, doc, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing JSON example: %s", diff) return } } func Test_ShorthandFieldsNoDuplicates(t *testing.T) { contents := `{ "spdxVersion": "SPDX-2.2", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "SPDX-Tools-v2.0", "documentDescribes": [ "SPDXRef-Container" ], "packages": [ { "name": "Container", "SPDXID": "SPDXRef-Container" }, { "name": "Package-1", "SPDXID": "SPDXRef-Package-1", "versionInfo": "1.1.1", "hasFiles": [ "SPDXRef-File-1", "SPDXRef-File-2" ] }, { "name": "Package-2", "SPDXID": "SPDXRef-Package-2", "versionInfo": "2.2.2" } ], "files": [ { "fileName": "./f1", "SPDXID": "SPDXRef-File-1" }, { "fileName": "./f2", "SPDXID": "SPDXRef-File-2" } ], "relationships": [ { "spdxElementId": "SPDXRef-Package-1", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-File-1" }, { "spdxElementId": "SPDXRef-File-2", "relationshipType": "CONTAINED_BY", "relatedSpdxElement": "SPDXRef-Package-1" } ] }` doc := spdx.Document{} err := json.ReadInto(strings.NewReader(contents), &doc) require.NoError(t, err) id := func(s string) common.DocElementID { return common.DocElementID{ ElementRefID: common.ElementID(s), } } want := spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: "DOCUMENT", DocumentName: "SPDX-Tools-v2.0", Packages: []*spdx.Package{ { PackageName: "Container", PackageSPDXIdentifier: "Container", FilesAnalyzed: true, }, { PackageName: "Package-1", PackageSPDXIdentifier: "Package-1", PackageVersion: "1.1.1", FilesAnalyzed: true, }, { PackageName: "Package-2", PackageSPDXIdentifier: "Package-2", PackageVersion: "2.2.2", FilesAnalyzed: true, }, }, Files: []*spdx.File{ { FileName: "./f1", FileSPDXIdentifier: "File-1", }, { FileName: "./f2", FileSPDXIdentifier: "File-2", }, }, Relationships: []*spdx.Relationship{ { RefA: id("DOCUMENT"), RefB: id("Container"), Relationship: common.TypeRelationshipDescribe, }, { RefA: id("Package-1"), RefB: id("File-1"), Relationship: common.TypeRelationshipContains, }, { RefA: id("File-2"), RefB: id("Package-1"), Relationship: common.TypeRelationshipContainedBy, }, }, } if diff := cmp.Diff(want, doc, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing JSON example: %s", diff) return } } func Test_JsonEnums(t *testing.T) { contents := `{ "spdxVersion": "SPDX-2.2", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "SPDX-Tools-v2.0", "documentDescribes": [ "SPDXRef-Container" ], "packages": [ { "name": "Container", "SPDXID": "SPDXRef-Container" }, { "name": "Package-1", "SPDXID": "SPDXRef-Package-1", "versionInfo": "1.1.1", "externalRefs": [{ "referenceCategory": "PACKAGE_MANAGER", "referenceLocator": "pkg:somepkg/ns/name1", "referenceType": "purl" }] }, { "name": "Package-2", "SPDXID": "SPDXRef-Package-2", "versionInfo": "2.2.2", "externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceLocator": "pkg:somepkg/ns/name2", "referenceType": "purl" }] }, { "name": "Package-3", "SPDXID": "SPDXRef-Package-3", "versionInfo": "3.3.3", "externalRefs": [{ "referenceCategory": "PERSISTENT_ID", "referenceLocator": "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", "referenceType": "gitoid" }] }, { "name": "Package-4", "SPDXID": "SPDXRef-Package-4", "versionInfo": "4.4.4", "externalRefs": [{ "referenceCategory": "PERSISTENT-ID", "referenceLocator": "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", "referenceType": "gitoid" }] } ] }` doc := spdx.Document{} err := json.ReadInto(strings.NewReader(contents), &doc) require.NoError(t, err) id := func(s string) common.DocElementID { return common.DocElementID{ ElementRefID: common.ElementID(s), } } require.Equal(t, spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: "DOCUMENT", DocumentName: "SPDX-Tools-v2.0", Packages: []*spdx.Package{ { PackageName: "Container", PackageSPDXIdentifier: "Container", FilesAnalyzed: true, }, { PackageName: "Package-1", PackageSPDXIdentifier: "Package-1", PackageVersion: "1.1.1", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: common.CategoryPackageManager, RefType: common.TypePackageManagerPURL, Locator: "pkg:somepkg/ns/name1", }, }, FilesAnalyzed: true, }, { PackageName: "Package-2", PackageSPDXIdentifier: "Package-2", PackageVersion: "2.2.2", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: common.CategoryPackageManager, RefType: common.TypePackageManagerPURL, Locator: "pkg:somepkg/ns/name2", }, }, FilesAnalyzed: true, }, { PackageName: "Package-3", PackageSPDXIdentifier: "Package-3", PackageVersion: "3.3.3", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: common.CategoryPersistentId, RefType: common.TypePersistentIdGitoid, Locator: "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", }, }, FilesAnalyzed: true, }, { PackageName: "Package-4", PackageSPDXIdentifier: "Package-4", PackageVersion: "4.4.4", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: common.CategoryPersistentId, RefType: common.TypePersistentIdGitoid, Locator: "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", }, }, FilesAnalyzed: true, }, }, Relationships: []*spdx.Relationship{ { RefA: id("DOCUMENT"), RefB: id("Container"), Relationship: common.TypeRelationshipDescribe, }, }, }, doc) } func relationshipLess(a, b *spdx.Relationship) bool { aStr, _ := jsonenc.Marshal(a) bStr, _ := jsonenc.Marshal(b) return string(aStr) < string(bStr) } tools-golang-0.5.5/spdx/v2/v2_2/json/testdata/000077500000000000000000000000001463371440000207675ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/json/testdata/spdx-null-relationships.json000066400000000000000000000031411463371440000264710ustar00rootroot00000000000000{ "files": [ { "fileName": "./Microsoft.CSharp.dll", "SPDXID": "SPDXRef-File--Microsoft.CSharp.dll-E226415EEA8ABBBA041A635582440F75E873395C", "checksums": [ { "algorithm": "SHA256", "checksumValue": "696b0b0d6ac06e620efd58db6f5f2e15fa2c9b91ddf8774ab8768c958d593254" }, { "algorithm": "SHA1", "checksumValue": "e226415eea8abbba041a635582440f75e873395c" } ], "licenseConcluded": "NOASSERTION", "licenseInfoInFile": [ "NOASSERTION" ], "copyrightText": "NOASSERTION" }], "packages": [ { "name": "read-pkg", "SPDXID": "SPDXRef-Package-read-pkg-1.1.0-30839A4052AC42B4E1CAB4B52EBC7DE7B94BB36D", "versionInfo": "1.1.0" }, { "name": "read-pkg", "SPDXID": "SPDXRef-Package-read-pkg-1.1.0-30839A4052AC42B4E1CAB4B52EBC7DE7B94BB36D", "versionInfo": "1.1.0" } ], "relationships": [ null, { }, null, { }, null ], "spdxVersion": "SPDX-2.2", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "Coordinated Packages 229170", "documentNamespace": "https://sbom.microsoft/1:2QSF7qZlbE-F7QrUJlEo7g:pHp_nUFvDUijZ4LrJ4RhoQ/696:229170/F8kPc6dwY0WXD1Rkc2z6cg", "creationInfo": { "created": "2021-12-08T21:06:16Z", "creators": [ "Organization: Microsoft", "Tool: Microsoft.SBOMTool-2.0.88" ] } } tools-golang-0.5.5/spdx/v2/v2_2/other_license.go000066400000000000000000000021251463371440000213570ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 // OtherLicense is an Other License Information section of an // SPDX Document for version 2.2 of the spec. type OtherLicense struct { // 10.1: License Identifier: "LicenseRef-[idstring]" // Cardinality: conditional (mandatory, one) if license is not // on SPDX License List LicenseIdentifier string `json:"licenseId"` // 10.2: Extracted Text // Cardinality: conditional (mandatory, one) if there is a // License Identifier assigned ExtractedText string `json:"extractedText"` // 10.3: License Name: single line of text or "NOASSERTION" // Cardinality: conditional (mandatory, one) if license is not // on SPDX License List LicenseName string `json:"name,omitempty"` // 10.4: License Cross Reference // Cardinality: conditional (optional, one or many) if license // is not on SPDX License List LicenseCrossReferences []string `json:"seeAlsos,omitempty"` // 10.5: License Comment // Cardinality: optional, one LicenseComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_2/package.go000066400000000000000000000163241463371440000201350ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 import ( "encoding/json" "strings" "github.com/spdx/tools-golang/json/marshal" "github.com/spdx/tools-golang/spdx/v2/common" ) // Package is a Package section of an SPDX Document for version 2.2 of the spec. type Package struct { // NOT PART OF SPEC // flag: does this "package" contain files that were in fact "unpackaged", // e.g. included directly in the Document without being in a Package? IsUnpackaged bool `json:"-"` // 7.1: Package Name // Cardinality: mandatory, one PackageName string `json:"name"` // 7.2: Package SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one PackageSPDXIdentifier common.ElementID `json:"SPDXID"` // 7.3: Package Version // Cardinality: optional, one PackageVersion string `json:"versionInfo,omitempty"` // 7.4: Package File Name // Cardinality: optional, one PackageFileName string `json:"packageFileName,omitempty"` // 7.5: Package Supplier: may have single result for either Person or Organization, // or NOASSERTION // Cardinality: optional, one PackageSupplier *common.Supplier `json:"supplier,omitempty"` // 7.6: Package Originator: may have single result for either Person or Organization, // or NOASSERTION // Cardinality: optional, one PackageOriginator *common.Originator `json:"originator,omitempty"` // 7.7: Package Download Location // Cardinality: mandatory, one PackageDownloadLocation string `json:"downloadLocation"` // 7.8: FilesAnalyzed // Cardinality: optional, one; default value is "true" if omitted FilesAnalyzed bool `json:"filesAnalyzed"` // NOT PART OF SPEC: did FilesAnalyzed tag appear? IsFilesAnalyzedTagPresent bool `json:"-"` // 7.9: Package Verification Code PackageVerificationCode common.PackageVerificationCode `json:"packageVerificationCode,omitempty"` // 7.10: Package Checksum: may have keys for SHA1, SHA256, SHA512 and/or MD5 // Cardinality: optional, one or many PackageChecksums []common.Checksum `json:"checksums,omitempty"` // 7.11: Package Home Page // Cardinality: optional, one PackageHomePage string `json:"homepage,omitempty"` // 7.12: Source Information // Cardinality: optional, one PackageSourceInfo string `json:"sourceInfo,omitempty"` // 7.13: Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one PackageLicenseConcluded string `json:"licenseConcluded"` // 7.14: All Licenses Info from Files: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one or many if filesAnalyzed is true / omitted; // zero (must be omitted) if filesAnalyzed is false PackageLicenseInfoFromFiles []string `json:"licenseInfoFromFiles,omitempty"` // 7.15: Declared License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one PackageLicenseDeclared string `json:"licenseDeclared"` // 7.16: Comments on License // Cardinality: optional, one PackageLicenseComments string `json:"licenseComments,omitempty"` // 7.17: Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: mandatory, one PackageCopyrightText string `json:"copyrightText"` // 7.18: Package Summary Description // Cardinality: optional, one PackageSummary string `json:"summary,omitempty"` // 7.19: Package Detailed Description // Cardinality: optional, one PackageDescription string `json:"description,omitempty"` // 7.20: Package Comment // Cardinality: optional, one PackageComment string `json:"comment,omitempty"` // 7.21: Package External Reference // Cardinality: optional, one or many PackageExternalReferences []*PackageExternalReference `json:"externalRefs,omitempty"` // 7.22: Package External Reference Comment // Cardinality: conditional (optional, one) for each External Reference // contained within PackageExternalReference struct, if present // 7.23: Package Attribution Text // Cardinality: optional, one or many PackageAttributionTexts []string `json:"attributionTexts,omitempty"` // Files contained in this Package Files []*File `json:"files,omitempty"` Annotations []Annotation `json:"annotations,omitempty"` // this field is only used when decoding JSON to translate the hasFiles // property to relationships hasFiles []common.DocElementID } func (p Package) MarshalJSON() ([]byte, error) { type pkg Package p2 := pkg(p) data, err := marshal.JSON(p2) if err != nil { return nil, err } // remove empty packageVerificationCode entries -- required by SPDX 2.2 but // omitempty has no effect since it is a non-comparable struct and not a pointer, so we // manually check to determine if there is a valid value to output and omit the field if not // see: https://spdx.github.io/spdx-spec/v2.2.2/package-information/#79-package-verification-code-field if p.PackageVerificationCode.Value == "" && p.PackageVerificationCode.ExcludedFiles == nil { var values map[string]interface{} err = json.Unmarshal(data, &values) if err != nil { return nil, err } delete(values, "packageVerificationCode") return marshal.JSON(values) } return data, nil } func (p *Package) UnmarshalJSON(b []byte) error { type pkg Package type extras struct { HasFiles []common.DocElementID `json:"hasFiles"` FilesAnalyzed *bool `json:"filesAnalyzed"` } var p2 pkg if err := json.Unmarshal(b, &p2); err != nil { return err } var e extras if err := json.Unmarshal(b, &e); err != nil { return err } *p = Package(p2) p.hasFiles = e.HasFiles // FilesAnalyzed defaults to true if omitted if e.FilesAnalyzed == nil { p.FilesAnalyzed = true } else { p.IsFilesAnalyzedTagPresent = true } return nil } var _ json.Unmarshaler = (*Package)(nil) var _ json.Marshaler = (*Package)(nil) // PackageExternalReference is an External Reference to additional info // about a Package, as defined in section 7.21 in version 2.2 of the spec. type PackageExternalReference struct { // category is "SECURITY", "PACKAGE-MANAGER" or "OTHER" Category string `json:"referenceCategory"` // type is an [idstring] as defined in Appendix VI; // called RefType here due to "type" being a Golang keyword RefType string `json:"referenceType"` // locator is a unique string to access the package-specific // info, metadata or content within the target location Locator string `json:"referenceLocator"` // 7.22: Package External Reference Comment // Cardinality: conditional (optional, one) for each External Reference ExternalRefComment string `json:"comment,omitempty"` } var _ json.Unmarshaler = (*PackageExternalReference)(nil) func (r *PackageExternalReference) UnmarshalJSON(b []byte) error { type ref PackageExternalReference var rr ref if err := json.Unmarshal(b, &rr); err != nil { return err } *r = PackageExternalReference(rr) r.Category = strings.ReplaceAll(r.Category, "_", "-") return nil } var _ json.Marshaler = (*PackageExternalReference)(nil) // We output as the JSON type enums since in v2.2.0 the JSON schema // spec only had enums with _ (e.g. PACKAGE_MANAGER) func (r *PackageExternalReference) MarshalJSON() ([]byte, error) { type ref PackageExternalReference var rr ref rr = ref(*r) rr.Category = strings.ReplaceAll(rr.Category, "-", "_") return marshal.JSON(&rr) } tools-golang-0.5.5/spdx/v2/v2_2/rdf/000077500000000000000000000000001463371440000167605ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader.go000066400000000000000000000010201463371440000205420ustar00rootroot00000000000000package rdf import ( "io" "github.com/spdx/gordf/rdfloader" "github.com/spdx/tools-golang/spdx/v2/v2_2" "github.com/spdx/tools-golang/spdx/v2/v2_2/rdf/reader" ) // Takes in a file Reader and returns the pertaining spdx document // or the error if any is encountered while setting the doc. func Read(content io.Reader) (*v2_2.Document, error) { var rdfParserObj, err = rdfloader.LoadFromReaderObject(content) if err != nil { return nil, err } doc, err := reader.LoadFromGoRDFParser(rdfParserObj) return doc, err } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/000077500000000000000000000000001463371440000202225ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/constants.go000066400000000000000000000405751463371440000226000ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import "github.com/spdx/gordf/rdfloader/parser" var ( // NAMESPACES NS_SPDX = "http://spdx.org/rdf/terms#" NS_RDFS = "http://www.w3.org/2000/01/rdf-schema#" NS_RDF = parser.RDFNS NS_PTR = "http://www.w3.org/2009/pointers#" NS_DOAP = "http://usefulinc.com/ns/doap#" // SPDX properties SPDX_SPEC_VERSION = NS_SPDX + "specVersion" SPDX_DATA_LICENSE = NS_SPDX + "dataLicense" SPDX_NAME = NS_SPDX + "name" SPDX_EXTERNAL_DOCUMENT_REF = NS_SPDX + "externalDocumentRef" SPDX_LICENSE_LIST_VERSION = NS_SPDX + "licenseListVersion" SPDX_CREATOR = NS_SPDX + "creator" SPDX_CREATED = NS_SPDX + "created" SPDX_REVIEWED = NS_SPDX + "reviewed" SPDX_DESCRIBES_PACKAGE = NS_SPDX + "describesPackage" SPDX_HAS_EXTRACTED_LICENSING_INFO = NS_SPDX + "hasExtractedLicensingInfo" SPDX_RELATIONSHIP = NS_SPDX + "relationship" SPDX_ANNOTATION = NS_SPDX + "annotation" SPDX_COMMENT = NS_SPDX + "comment" SPDX_CREATION_INFO = NS_SPDX + "creationInfo" SPDX_CHECKSUM_ALGORITHM_SHA1 = NS_SPDX + "checksumAlgorithm_sha1" SPDX_CHECKSUM_ALGORITHM_SHA256 = NS_SPDX + "checksumAlgorithm_sha256" SPDX_CHECKSUM_ALGORITHM_MD5 = NS_SPDX + "checksumAlgorithm_md5" SPDX_EXTERNAL_DOCUMENT_ID = NS_SPDX + "externalDocumentId" SPDX_SPDX_DOCUMENT = NS_SPDX + "spdxDocument" SPDX_SPDX_DOCUMENT_CAPITALIZED = NS_SPDX + "SpdxDocument" SPDX_CHECKSUM = NS_SPDX + "checksum" SPDX_CHECKSUM_CAPITALIZED = NS_SPDX + "Checksum" SPDX_ANNOTATION_TYPE = NS_SPDX + "annotationType" SPDX_ANNOTATION_TYPE_OTHER = NS_SPDX + "annotationType_other" SPDX_ANNOTATION_TYPE_REVIEW = NS_SPDX + "annotationType_review" SPDX_LICENSE_INFO_IN_FILE = NS_SPDX + "licenseInfoInFile" SPDX_LICENSE_CONCLUDED = NS_SPDX + "licenseConcluded" SPDX_LICENSE_COMMENTS = NS_SPDX + "licenseComments" SPDX_COPYRIGHT_TEXT = NS_SPDX + "copyrightText" SPDX_ARTIFACT_OF = NS_SPDX + "artifactOf" SPDX_NOTICE_TEXT = NS_SPDX + "noticeText" SPDX_FILE_CONTRIBUTOR = NS_SPDX + "fileContributor" SPDX_FILE_DEPENDENCY = NS_SPDX + "fileDependency" SPDX_FILE_TYPE = NS_SPDX + "fileType" SPDX_FILE_NAME = NS_SPDX + "fileName" SPDX_EXTRACTED_TEXT = NS_SPDX + "extractedText" SPDX_LICENSE_ID = NS_SPDX + "licenseId" SPDX_FILE = NS_SPDX + "File" SPDX_PACKAGE = NS_SPDX + "Package" SPDX_SPDX_ELEMENT = NS_SPDX + "SpdxElement" SPDX_VERSION_INFO = NS_SPDX + "versionInfo" SPDX_PACKAGE_FILE_NAME = NS_SPDX + "packageFileName" SPDX_SUPPLIER = NS_SPDX + "supplier" SPDX_ORIGINATOR = NS_SPDX + "originator" SPDX_DOWNLOAD_LOCATION = NS_SPDX + "downloadLocation" SPDX_FILES_ANALYZED = NS_SPDX + "filesAnalyzed" SPDX_PACKAGE_VERIFICATION_CODE = NS_SPDX + "packageVerificationCode" SPDX_SOURCE_INFO = NS_SPDX + "sourceInfo" SPDX_LICENSE_INFO_FROM_FILES = NS_SPDX + "licenseInfoFromFiles" SPDX_LICENSE_DECLARED = NS_SPDX + "licenseDeclared" SPDX_SUMMARY = NS_SPDX + "summary" SPDX_DESCRIPTION = NS_SPDX + "description" SPDX_EXTERNAL_REF = NS_SPDX + "externalRef" SPDX_HAS_FILE = NS_SPDX + "hasFile" SPDX_ATTRIBUTION_TEXT = NS_SPDX + "attributionText" SPDX_PACKAGE_VERIFICATION_CODE_VALUE = NS_SPDX + "packageVerificationCodeValue" SPDX_PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE = NS_SPDX + "packageVerificationCodeExcludedFile" SPDX_RELATED_SPDX_ELEMENT = NS_SPDX + "relatedSpdxElement" SPDX_RELATIONSHIP_TYPE = NS_SPDX + "relationshipType" SPDX_SNIPPET_FROM_FILE = NS_SPDX + "snippetFromFile" SPDX_LICENSE_INFO_IN_SNIPPET = NS_SPDX + "licenseInfoInSnippet" SPDX_RANGE = NS_SPDX + "range" SPDX_REVIEWER = NS_SPDX + "reviewer" SPDX_REVIEW_DATE = NS_SPDX + "reviewDate" SPDX_SNIPPET = NS_SPDX + "Snippet" SPDX_ALGORITHM = NS_SPDX + "algorithm" SPDX_CHECKSUM_VALUE = NS_SPDX + "checksumValue" SPDX_REFERENCE_CATEGORY = NS_SPDX + "referenceCategory" SPDX_REFERENCE_CATEGORY_PACKAGE_MANAGER = NS_SPDX + "referenceCategory_packageManager" SPDX_REFERENCE_CATEGORY_SECURITY = NS_SPDX + "referenceCategory_security" SPDX_REFERENCE_CATEGORY_OTHER = NS_SPDX + "referenceCategory_other" SPDX_REFERENCE_TYPE = NS_SPDX + "referenceType" SPDX_REFERENCE_LOCATOR = NS_SPDX + "referenceLocator" SPDX_ANNOTATION_DATE = NS_SPDX + "annotationDate" SPDX_ANNOTATOR = NS_SPDX + "annotator" SPDX_MEMBER = NS_SPDX + "member" SPDX_DISJUNCTIVE_LICENSE_SET = NS_SPDX + "DisjunctiveLicenseSet" SPDX_CONJUNCTIVE_LICENSE_SET = NS_SPDX + "ConjunctiveLicenseSet" SPDX_EXTRACTED_LICENSING_INFO = NS_SPDX + "ExtractedLicensingInfo" SPDX_SIMPLE_LICENSING_INFO = NS_SPDX + "SimpleLicensingInfo" SPDX_NONE_CAPS = NS_SPDX + "NONE" SPDX_NOASSERTION_CAPS = NS_SPDX + "NOASSERTION" SPDX_NONE_SMALL = NS_SPDX + "none" SPDX_NOASSERTION_SMALL = NS_SPDX + "noassertion" SPDX_LICENSE = NS_SPDX + "License" SPDX_LISTED_LICENSE = NS_SPDX + "ListedLicense" SPDX_EXAMPLE = NS_SPDX + "example" SPDX_IS_OSI_APPROVED = NS_SPDX + "isOsiApproved" SPDX_STANDARD_LICENSE_TEMPLATE = NS_SPDX + "standardLicenseTemplate" SPDX_IS_DEPRECATED_LICENSE_ID = NS_SPDX + "isDeprecatedLicenseId" SPDX_IS_FSF_LIBRE = NS_SPDX + "isFsfLibre" SPDX_LICENSE_TEXT = NS_SPDX + "licenseText" SPDX_STANDARD_LICENSE_HEADER = NS_SPDX + "standardLicenseHeader" SPDX_LICENSE_EXCEPTION_ID = NS_SPDX + "licenseExceptionId" SPDX_LICENSE_EXCEPTION_TEXT = NS_SPDX + "licenseExceptionText" SPDX_LICENSE_EXCEPTION = NS_SPDX + "licenseException" SPDX_WITH_EXCEPTION_OPERATOR = NS_SPDX + "WithExceptionOperator" SPDX_OR_LATER_OPERATOR = NS_SPDX + "OrLaterOperator" SPDX_STANDARD_LICENSE_HEADER_TEMPLATE = NS_SPDX + "standardLicenseHeaderTemplate" // RDFS properties RDFS_COMMENT = NS_RDFS + "comment" RDFS_SEE_ALSO = NS_RDFS + "seeAlso" // RDF properties RDF_TYPE = NS_RDF + "type" // DOAP properties DOAP_HOMEPAGE = NS_DOAP + "homepage" DOAP_NAME = NS_DOAP + "name" // PTR properties PTR_START_END_POINTER = NS_PTR + "StartEndPointer" PTR_START_POINTER = NS_PTR + "startPointer" PTR_BYTE_OFFSET_POINTER = NS_PTR + "ByteOffsetPointer" PTR_LINE_CHAR_POINTER = NS_PTR + "LineCharPointer" PTR_REFERENCE = NS_PTR + "reference" PTR_OFFSET = NS_PTR + "offset" PTR_LINE_NUMBER = NS_PTR + "lineNumber" PTR_END_POINTER = NS_PTR + "endPointer" // prefixes PREFIX_RELATIONSHIP_TYPE = "relationshipType_" ) func AllRelationshipTypes() []string { return []string{ "amendment", "ancestorOf", "buildDependencyOf", "buildToolOf", "containedBy", "contains", "copyOf", "dataFile", "dataFileOf", "dependencyManifestOf", "dependencyOf", "dependsOn", "descendantOf", "describedBy", "describes", "devDependencyOf", "devToolOf", "distributionArtifact", "documentation", "dynamicLink", "exampleOf", "expandedFromArchive", "fileAdded", "fileDeleted", "fileModified", "generatedFrom", "generates", "hasPrerequisite", "metafileOf", "optionalComponentOf", "optionalDependencyOf", "other", "packageOf", "patchApplied", "patchFor", "prerequisiteFor", "providedDependencyOf", "runtimeDependencyOf", "staticLink", "testDependencyOf", "testOf", "testToolOf", "testcaseOf", "variantOf", } } func AllStandardLicenseIDS() []string { return []string{ "0BSD", "389-exception", "AAL", "Abstyles", "Adobe-2006", "Adobe-Glyph", "ADSL", "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0", "Afmparse", "AGPL-1.0-only", "AGPL-1.0-or-later", "AGPL-1.0", "AGPL-3.0-only", "AGPL-3.0-or-later", "AGPL-3.0", "Aladdin", "AMDPLPA", "AML", "AMPAS", "ANTLR-PD", "Apache-1.0", "Apache-1.1", "Apache-2.0", "APAFML", "APL-1.0", "APSL-1.0", "APSL-1.1", "APSL-1.2", "APSL-2.0", "Artistic-1.0-cl8", "Artistic-1.0-Perl", "Artistic-1.0", "Artistic-2.0", "", "Autoconf-exception-2.0", "Autoconf-exception-3.0", "Bahyph", "Barr", "Beerware", "Bison-exception-2.2", "BitTorrent-1.0", "BitTorrent-1.1", "blessing", "BlueOak-1.0.0", "Bootloader-exception", "Borceux", "BSD-1-Clause", "BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", "BSD-2-Clause-Patent", "BSD-2-Clause-Views", "BSD-2-Clause", "BSD-3-Clause-Attribution", "BSD-3-Clause-Clear", "BSD-3-Clause-LBNL", "BSD-3-Clause-No-Nuclear-License-2014", "BSD-3-Clause-No-Nuclear-License", "BSD-3-Clause-No-Nuclear-Warranty", "BSD-3-Clause-Open-MPI", "BSD-3-Clause", "BSD-4-Clause-UC", "BSD-4-Clause", "BSD-Protection", "BSD-Source-Code", "BSL-1.0", "bzip2-1.0.5", "bzip2-1.0.6", "CAL-1.0-Combined-Work-Exception", "CAL-1.0", "Caldera", "CATOSL-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5", "CC-BY-3.0-AT", "CC-BY-3.0", "CC-BY-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5", "CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0", "CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0-IGO", "CC-BY-NC-ND-3.0", "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0", "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0", "CC-BY-ND-4.0", "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0-AT", "CC-BY-SA-3.0", "CC-BY-SA-4.0", "CC-PDDC", "CC0-1.0", "CDDL-1.0", "CDDL-1.1", "CDLA-Permissive-1.0", "CDLA-Sharing-1.0", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-2.1", "CECILL-B", "CECILL-C", "CERN-OHL-1.1", "CERN-OHL-1.2", "CERN-OHL-P-2.0", "CERN-OHL-S-2.0", "CERN-OHL-W-2.0", "ClArtistic", "Classpath-exception-2.0", "CLISP-exception-2.0", "CNRI-Jython", "CNRI-Python-GPL-Compatible", "CNRI-Python", "Condor-1.1", "copyleft-next-0.3.0", "copyleft-next-0.3.1", "CPAL-1.0", "CPL-1.0", "CPOL-1.02", "Crossword", "CrystalStacker", "CUA-OPL-1.0", "Cube", "curl", "D-FSL-1.0", "diffmark", "DigiRule-FOSS-exception", "DOC", "Dotseqn", "DSDP", "dvipdfm", "ECL-1.0", "ECL-2.0", "eCos-2.0", "eCos-exception-2.0", "EFL-1.0", "EFL-2.0", "eGenix", "Entessa", "EPICS", "EPL-1.0", "EPL-2.0", "ErlPL-1.1", "etalab-2.0", "EUDatagrid", "EUPL-1.0", "EUPL-1.1", "EUPL-1.2", "Eurosym", "Fair", "Fawkes-Runtime-exception", "FLTK-exception", "Font-exception-2.0", "Frameworx-1.0", "FreeImage", "freertos-exception-2.0", "FSFAP", "FSFUL", "FSFULLR", "FTL", "GCC-exception-2.0", "GCC-exception-3.1", "GFDL-1.1-invariants-only", "GFDL-1.1-invariants-or-later", "GFDL-1.1-no-invariants-only", "GFDL-1.1-no-invariants-or-later", "GFDL-1.1-only", "GFDL-1.1-or-later", "GFDL-1.1", "GFDL-1.2-invariants-only", "GFDL-1.2-invariants-or-later", "GFDL-1.2-no-invariants-only", "GFDL-1.2-no-invariants-or-later", "GFDL-1.2-only", "GFDL-1.2-or-later", "GFDL-1.2", "GFDL-1.3-invariants-only", "GFDL-1.3-invariants-or-later", "GFDL-1.3-no-invariants-only", "GFDL-1.3-no-invariants-or-later", "GFDL-1.3-only", "GFDL-1.3-or-later", "GFDL-1.3", "Giftware", "GL2PS", "Glide", "Glulxe", "GLWTPL", "gnu-javamail-exception", "gnuplot", "GPL-1.0+", "GPL-1.0-only", "GPL-1.0-or-later", "GPL-1.0", "GPL-2.0+", "GPL-2.0-only", "GPL-2.0-or-later", "GPL-2.0-with-autoconf-exception", "GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception", "GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-2.0", "GPL-3.0+", "GPL-3.0-linking-exception", "GPL-3.0-linking-source-exception", "GPL-3.0-only", "GPL-3.0-or-later", "GPL-3.0-with-autoconf-exception", "GPL-3.0-with-GCC-exception", "GPL-3.0", "GPL-CC-1.0", "gSOAP-1.3b", "HaskellReport", "Hippocratic-2.1", "HPND-sell-variant", "HPND", "i2p-gpl-java-exception", "IBM-pibs", "ICU", "IJG", "ImageMagick", "iMatix", "Imlib2", "Info-ZIP", "Intel-ACPI", "Intel", "Interbase-1.0", "IPA", "IPL-1.0", "ISC", "JasPer-2.0", "JPNIC", "JSON", "LAL-1.2", "LAL-1.3", "Latex2e", "Leptonica", "LGPL-2.0+", "LGPL-2.0-only", "LGPL-2.0-or-later", "LGPL-2.0", "LGPL-2.1+", "LGPL-2.1-only", "LGPL-2.1-or-later", "LGPL-2.1", "LGPL-3.0+", "LGPL-3.0-linking-exception", "LGPL-3.0-only", "LGPL-3.0-or-later", "LGPL-3.0", "LGPLLR", "libpng-2.0", "Libpng", "libselinux-1.0", "libtiff", "Libtool-exception", "licenses", "LiLiQ-P-1.1", "LiLiQ-R-1.1", "LiLiQ-Rplus-1.1", "Linux-OpenIB", "Linux-syscall-note", "LLVM-exception", "LPL-1.0", "LPL-1.02", "LPPL-1.0", "LPPL-1.1", "LPPL-1.2", "LPPL-1.3a", "LPPL-1.3c", "LZMA-exception", "MakeIndex", "mif-exception", "MirOS", "MIT-0", "MIT-advertising", "MIT-CMU", "MIT-enna", "MIT-feh", "MIT", "MITNFA", "Motosoto", "mpich2", "MPL-1.0", "MPL-1.1", "MPL-2.0-no-copyleft-exception", "MPL-2.0", "MS-PL", "MS-RL", "MTLL", "MulanPSL-1.0", "MulanPSL-2.0", "Multics", "Mup", "NASA-1.3", "Naumen", "NBPL-1.0", "NCGL-UK-2.0", "NCSA", "Net-SNMP", "NetCDF", "Newsletr", "NGPL", "NIST-PD-fallback", "NIST-PD", "NLOD-1.0", "NLPL", "Nokia-Qt-exception-1.1", "Nokia", "NOSL", "Noweb", "NPL-1.0", "NPL-1.1", "NPOSL-3.0", "NRL", "NTP-0", "NTP", "Nunit", "O-UDA-1.0", "OCaml-LGPL-linking-exception", "OCCT-exception-1.0", "OCCT-PL", "OCLC-2.0", "ODbL-1.0", "ODC-By-1.0", "OFL-1.0-no-RFN", "OFL-1.0-RFN", "OFL-1.0", "OFL-1.1-no-RFN", "OFL-1.1-RFN", "OFL-1.1", "OGC-1.0", "OGL-Canada-2.0", "OGL-UK-1.0", "OGL-UK-2.0", "OGL-UK-3.0", "OGTSL", "OLDAP-1.1", "OLDAP-1.2", "OLDAP-1.3", "OLDAP-1.4", "OLDAP-2.0.1", "OLDAP-2.0", "OLDAP-2.1", "OLDAP-2.2.1", "OLDAP-2.2.2", "OLDAP-2.2", "OLDAP-2.3", "OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OLDAP-2.8", "OML", "", "OpenJDK-assembly-exception-1.0", "OpenSSL", "openvpn-openssl-exception", "OPL-1.0", "OSET-PL-2.1", "OSL-1.0", "OSL-1.1", "OSL-2.0", "OSL-2.1", "OSL-3.0", "Parity-6.0.0", "Parity-7.0.0", "PDDL-1.0", "PHP-3.0", "PHP-3.01", "Plexus", "PolyForm-Noncommercial-1.0.0", "PolyForm-Small-Business-1.0.0", "PostgreSQL", "PS-or-PDF-font-exception-20170817", "PSF-2.0", "psfrag", "psutils", "Python-2.0", "Qhull", "QPL-1.0", "Qt-GPL-exception-1.0", "Qt-LGPL-exception-1.1", "Qwt-exception-1.0", "Rdisc", "RHeCos-1.1", "RPL-1.1", "RPL-1.5", "RPSL-1.0", "RSA-MD", "RSCPL", "Ruby", "SAX-PD", "Saxpath", "SCEA", "Sendmail-8.23", "Sendmail", "SGI-B-1.0", "SGI-B-1.1", "SGI-B-2.0", "SHL-0.5", "SHL-0.51", "SHL-2.0", "SHL-2.1", "SimPL-2.0", "SISSL-1.2", "SISSL", "Sleepycat", "SMLNJ", "SMPPL", "SNIA", "Spencer-86", "Spencer-94", "Spencer-99", "SPL-1.0", "SSH-OpenSSH", "SSH-short", "SSPL-1.0", "StandardML-NJ", "SugarCRM-1.1.3", "Swift-exception", "SWL", "TAPR-OHL-1.0", "TCL", "TCP-wrappers", "TMate", "TORQUE-1.1", "TOSL", "TU-Berlin-1.0", "TU-Berlin-2.0", "u-boot-exception-2.0", "UCL-1.0", "Unicode-DFS-2015", "Unicode-DFS-2016", "Unicode-TOU", "Universal-FOSS-exception-1.0", "Unlicense", "UPL-1.0", "Vim", "VOSTROM", "VSL-1.0", "W3C-19980720", "W3C-20150513", "W3C", "Watcom-1.0", "Wsuipa", "WTFPL", "WxWindows-exception-3.1", "wxWindows", "X11", "Xerox", "XFree86-1.1", "xinetd", "Xnet", "xpp", "XSkat", "YPL-1.0", "YPL-1.1", "Zed", "Zend-2.0", "Zimbra-1.3", "Zimbra-1.4", "zlib-acknowledgement", "Zlib", "ZPL-1.1", "ZPL-2.0", "ZPL-2.1", } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/license_utils.go000066400000000000000000000071401463371440000234150ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" ) /* util methods for licenses and checksums below:*/ // Given the license URI, returns the name of the license defined // in the last part of the uri. // This function is susceptible to false-positives. func getLicenseStringFromURI(uri string) string { licenseEnd := strings.TrimSpace(getLastPartOfURI(uri)) lower := strings.ToLower(licenseEnd) if lower == "none" || lower == "noassertion" { return strings.ToUpper(licenseEnd) } return licenseEnd } // returns the checksum algorithm and it's value // In the newer versions, these two strings will be bound to a single checksum struct // whose pointer will be returned. func (parser *rdfParser2_2) getChecksumFromNode(checksumNode *gordfParser.Node) (algorithm common.ChecksumAlgorithm, value string, err error) { var checksumValue, checksumAlgorithm string for _, checksumTriple := range parser.nodeToTriples(checksumNode) { switch checksumTriple.Predicate.ID { case RDF_TYPE: continue case SPDX_CHECKSUM_VALUE: // cardinality: exactly 1 checksumValue = strings.TrimSpace(checksumTriple.Object.ID) case SPDX_ALGORITHM: // cardinality: exactly 1 checksumAlgorithm, err = getAlgorithmFromURI(checksumTriple.Object.ID) if err != nil { return } default: err = fmt.Errorf("unknown predicate '%s' while parsing checksum node", checksumTriple.Predicate.ID) return } } return common.ChecksumAlgorithm(checksumAlgorithm), checksumValue, nil } func getAlgorithmFromURI(algorithmURI string) (checksumAlgorithm string, err error) { fragment := getLastPartOfURI(algorithmURI) if !strings.HasPrefix(fragment, "checksumAlgorithm_") { return "", fmt.Errorf("checksum algorithm uri must begin with checksumAlgorithm_. found %s", fragment) } algorithm := strings.TrimPrefix(fragment, "checksumAlgorithm_") algorithm = strings.ToLower(strings.TrimSpace(algorithm)) switch algorithm { case "md2", "md4", "md5", "md6": checksumAlgorithm = strings.ToUpper(algorithm) case "sha1", "sha224", "sha256", "sha384", "sha512": checksumAlgorithm = strings.ToUpper(algorithm) default: return "", fmt.Errorf("unknown checksum algorithm %s", algorithm) } return } // from a list of licenses, it returns a // list of string representation of those licenses. func mapLicensesToStrings(licences []AnyLicenseInfo) []string { res := make([]string, len(licences)) for i, lic := range licences { res[i] = lic.ToLicenseString() } return res } /****** Type Functions ******/ // TODO: should probably add brackets while linearizing a nested license. func (lic ConjunctiveLicenseSet) ToLicenseString() string { return strings.Join(mapLicensesToStrings(lic.members), " AND ") } // TODO: should probably add brackets while linearizing a nested license. func (lic DisjunctiveLicenseSet) ToLicenseString() string { return strings.Join(mapLicensesToStrings(lic.members), " OR ") } func (lic ExtractedLicensingInfo) ToLicenseString() string { return lic.licenseID } func (operator OrLaterOperator) ToLicenseString() string { return operator.member.ToLicenseString() } func (lic License) ToLicenseString() string { return lic.licenseID } func (lic ListedLicense) ToLicenseString() string { return lic.licenseID } func (lic WithExceptionOperator) ToLicenseString() string { return lic.member.ToLicenseString() } func (lic SpecialLicense) ToLicenseString() string { return string(lic.value) } func (lic SimpleLicensingInfo) ToLicenseString() string { return lic.licenseID } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/license_utils_test.go000066400000000000000000000226241463371440000244600ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" ) func Test_getLicenseStringFromURI(t *testing.T) { // TestCase 1: NONE license input := SPDX_NONE_CAPS output := getLicenseStringFromURI(input) expectedOutput := "NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", expectedOutput, output) } // TestCase 2: NOASSERTION license input = SPDX_NOASSERTION_SMALL output = getLicenseStringFromURI(input) expectedOutput = "NOASSERTION" if output != expectedOutput { t.Errorf("expected: %s, found %s", expectedOutput, output) } // TestCase 3: Other license input = NS_SPDX + "LicenseRef-1" output = getLicenseStringFromURI(input) expectedOutput = "LicenseRef-1" if output != expectedOutput { t.Errorf("expected: %s, found %s", expectedOutput, output) } } func Test_rdfParser2_2_getChecksumFromNode(t *testing.T) { var parser *rdfParser2_2 var err error // TestCase 1: invalid checksum algorithm parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) checksumNode := parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getChecksumFromNode(checksumNode) if err == nil { t.Errorf("expected an error saying invalid checksum algorithm") } // TestCase 2: invalid predicate parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) checksumNode = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getChecksumFromNode(checksumNode) if err == nil { t.Errorf("expected an error saying invalid predicate") } // TestCase 3: valid input parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) checksumNode = parser.gordfParserObj.Triples[0].Subject algorithm, value, err := parser.getChecksumFromNode(checksumNode) if err != nil { t.Errorf("unexpected error: %v", err) } if algorithm != "SHA1" { t.Errorf("expected checksum algorithm to be sha1, found %s", algorithm) } expectedValue := "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" if value != expectedValue { t.Errorf("expected checksumValue to be %s, found %s", expectedValue, value) } } func Test_rdfParser2_2_getAlgorithmFromURI(t *testing.T) { var algorithmURI string var err error // TestCase 1: checksumAlgorithm uri doesn't start with checksumAlgorithm_ algorithmURI = NS_SPDX + "sha1" _, err = getAlgorithmFromURI(algorithmURI) if err == nil { t.Errorf("should've raised an error for algorithmURI that doesn't start with checksumAlgorithm_") } // TestCase 2: unknown checksum algorithm algorithmURI = NS_SPDX + "checksumAlgorithm_sha999" _, err = getAlgorithmFromURI(algorithmURI) if err == nil { t.Errorf("should've raised an error for invalid algorithm") } // TestCase 3: valid input algorithmURI = NS_SPDX + "checksumAlgorithm_sha256" algorithm, err := getAlgorithmFromURI(algorithmURI) if err != nil { t.Errorf("unexpected error: %v", err) } if algorithm != "SHA256" { t.Errorf("expected: SHA256, found: %s", algorithm) } } func Test_mapLicensesToStrings(t *testing.T) { // nothing much to test here. // just a dummy dry run. licenses := []AnyLicenseInfo{ SpecialLicense{ value: NONE, }, SpecialLicense{ value: NOASSERTION, }, } licenseStrings := mapLicensesToStrings(licenses) expectedLicenseStrings := []string{"NONE", "NOASSERTION"} if !reflect.DeepEqual(licenseStrings, expectedLicenseStrings) { t.Errorf("expected: %+v\nfound %+v", expectedLicenseStrings, licenseStrings) } } func TestConjunctiveLicenseSet_ToLicenseString(t *testing.T) { var lic ConjunctiveLicenseSet var output, expectedOutput string // TestCase 1: no license in the set lic = ConjunctiveLicenseSet{ members: nil, } output = lic.ToLicenseString() expectedOutput = "" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 2: single license in the set lic = ConjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 3: more than one license in the set. lic = ConjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, SpecialLicense{value: NONE}, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION AND NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 4: nested conjunctive license. lic = ConjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, ConjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: "LicenseRef-1"}, SpecialLicense{value: NONE}, }, }, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION AND LicenseRef-1 AND NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } } func TestDisjunctiveLicenseSet_ToLicenseString(t *testing.T) { var lic DisjunctiveLicenseSet var output, expectedOutput string // TestCase 1: no license in the set lic = DisjunctiveLicenseSet{ members: nil, } output = lic.ToLicenseString() expectedOutput = "" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 2: single license in the set lic = DisjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 3: more than one license in the set. lic = DisjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, SpecialLicense{value: NONE}, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION OR NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 4: nested conjunctive license. lic = DisjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, DisjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: "LicenseRef-1"}, SpecialLicense{value: NONE}, }, }, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION OR LicenseRef-1 OR NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } } func TestExtractedLicensingInfo_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) extractedLicense := ExtractedLicensingInfo{ SimpleLicensingInfo: SimpleLicensingInfo{ licenseID: "license", }, extractedText: "extracted Text", } expectedOutput := "license" output := extractedLicense.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestOrLaterOperator_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) orLater := OrLaterOperator{ member: SimpleLicensingInfo{ licenseID: "license", }, } expectedOutput := "license" output := orLater.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestLicense_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) license := License{ SimpleLicensingInfo: SimpleLicensingInfo{ licenseID: "license", }, } expectedOutput := "license" output := license.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestListedLicense_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) ll := ListedLicense{License{ SimpleLicensingInfo: SimpleLicensingInfo{ licenseID: "license", }, }, } expectedOutput := "license" output := ll.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestWithExceptionOperator_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) withException := WithExceptionOperator{ member: SimpleLicensingInfo{ licenseID: "license", }, licenseException: LicenseException{}, } expectedOutput := "license" output := withException.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestSpecialLicense_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) specialLicense := SpecialLicense{ value: "license", } expectedOutput := "license" output := specialLicense.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestSimpleLicensingInfo_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) sli := SimpleLicensingInfo{ licenseID: "license", } expectedOutput := "license" output := sli.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_annotation.go000066400000000000000000000045731463371440000241260ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "errors" "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // creates a new instance of annotation and sets the annotation attributes // associated with the given node. // The newly created annotation is appended to the doc. func (parser *rdfParser2_2) parseAnnotationFromNode(node *gordfParser.Node) (err error) { ann := &v2_2.Annotation{} for _, subTriple := range parser.nodeToTriples(node) { switch subTriple.Predicate.ID { case SPDX_ANNOTATOR: // cardinality: exactly 1 err = setAnnotatorFromString(subTriple.Object.ID, ann) case SPDX_ANNOTATION_DATE: // cardinality: exactly 1 ann.AnnotationDate = subTriple.Object.ID case RDFS_COMMENT: // cardinality: exactly 1 ann.AnnotationComment = subTriple.Object.ID case SPDX_ANNOTATION_TYPE: // cardinality: exactly 1 err = setAnnotationType(subTriple.Object.ID, ann) case RDF_TYPE: // cardinality: exactly 1 continue default: err = fmt.Errorf("unknown predicate %s while parsing annotation", subTriple.Predicate.ID) } if err != nil { return err } } return setAnnotationToParser(parser, ann) } func setAnnotationToParser(parser *rdfParser2_2, annotation *v2_2.Annotation) error { if parser.doc == nil { return errors.New("uninitialized spdx document") } if parser.doc.Annotations == nil { parser.doc.Annotations = []*v2_2.Annotation{} } parser.doc.Annotations = append(parser.doc.Annotations, annotation) return nil } // annotator is of type [Person|Organization|Tool]:String func setAnnotatorFromString(annotatorString string, ann *v2_2.Annotation) error { subkey, subvalue, err := ExtractSubs(annotatorString, ":") if err != nil { return err } if subkey == "Person" || subkey == "Organization" || subkey == "Tool" { ann.Annotator.AnnotatorType = subkey ann.Annotator.Annotator = subvalue return nil } return fmt.Errorf("unrecognized Annotator type %v while parsing annotation", subkey) } // it can be NS_SPDX+annotationType_[review|other] func setAnnotationType(annType string, ann *v2_2.Annotation) error { switch annType { case SPDX_ANNOTATION_TYPE_OTHER: ann.AnnotationType = "OTHER" case SPDX_ANNOTATION_TYPE_REVIEW: ann.AnnotationType = "REVIEW" default: return fmt.Errorf("unknown annotation type %s", annType) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_annotation_test.go000066400000000000000000000140551463371440000251610ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func Test_setAnnotatorFromString(t *testing.T) { // TestCase 1: Empty String must raise an error ann := &v2_2.Annotation{} input := "" err := setAnnotatorFromString(input, ann) if err == nil { t.Error("should've raised an error for an empty string") } // TestCase 2: Invalid annotator type ann = &v2_2.Annotation{} input = "Company: some_company" err = setAnnotatorFromString(input, ann) if err == nil { t.Errorf("should've raised an error for an unknown annotator type") } // TestCase 3: Valid annotator ann = &v2_2.Annotation{} input = "Person: Rishabh" err = setAnnotatorFromString(input, ann) if err != nil { t.Errorf("unexpected error for a valid annotator") } if ann.Annotator.AnnotatorType != "Person" { t.Errorf("wrnog annotator type: expected: %s, found: %s", "Person", ann.Annotator) } if ann.Annotator.Annotator != "Rishabh" { t.Errorf("wrong annotator: expected: %s, found: %s", "Rishabh", ann.Annotator) } } func Test_setAnnotationType(t *testing.T) { ann := &v2_2.Annotation{} // TestCase 1: invalid input (empty annotationType) err := setAnnotationType("", ann) if err == nil { t.Errorf("expected an error for empty input") } // TestCase 2: invalid input (unknown annotation type) err = setAnnotationType(NS_SPDX+"annotationType_unknown", ann) if err == nil { t.Errorf("expected an error for invalid annotationType") } // TestCase 3: valid input (annotationType_other) err = setAnnotationType(SPDX_ANNOTATION_TYPE_OTHER, ann) if err != nil { t.Errorf("unexpected error: %v", err) } if ann.AnnotationType != "OTHER" { t.Errorf("expected: OTHER, found: %s", ann.AnnotationType) } // TestCase 4: valid input (annotationType_review) err = setAnnotationType(SPDX_ANNOTATION_TYPE_REVIEW, ann) if err != nil { t.Errorf("unexpected error: %v", err) } if ann.AnnotationType != "REVIEW" { t.Errorf("expected: REVIEW, found: %s", ann.AnnotationType) } } func Test_setAnnotationToParser(t *testing.T) { // TestCase 1: doc is nil (must raise an error) parser, _ := parserFromBodyContent(``) parser.doc = nil err := setAnnotationToParser(parser, &v2_2.Annotation{}) if err == nil { t.Errorf("empty doc should've raised an error") } // TestCase 2: empty annotations should create a new annotations // list and append the input to it. parser, _ = parserFromBodyContent(``) parser.doc.Annotations = nil err = setAnnotationToParser(parser, &v2_2.Annotation{}) if err != nil { t.Errorf("unexpected error: %v", err) } if len(parser.doc.Annotations) != 1 { t.Errorf("expected doc to have 1 annotation, found %d", len(parser.doc.Annotations)) } } func Test_rdfParser2_2_parseAnnotationFromNode(t *testing.T) { // TestCase 1: invalid annotator must raise an error parser, _ := parserFromBodyContent(` 2010-01-29T18:30:22Z Document level annotation Company: some company `) node := parser.gordfParserObj.Triples[0].Subject err := parser.parseAnnotationFromNode(node) if err == nil { t.Errorf("wrong annotator type should've raised an error") } // TestCase 2: wrong annotation type should raise an error parser, _ = parserFromBodyContent(` 2010-01-29T18:30:22Z Document level annotation Person: Jane Doe `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseAnnotationFromNode(node) if err == nil { t.Errorf("wrong annotation type should've raised an error") } // TestCase 3: unknown predicate should also raise an error parser, _ = parserFromBodyContent(` 2010-01-29T18:30:22Z Document level annotation Person: Jane Doe `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseAnnotationFromNode(node) if err == nil { t.Errorf("unknown predicate must raise an error") } // TestCase 4: completely valid annotation parser, _ = parserFromBodyContent(` 2010-01-29T18:30:22Z Document level annotation Person: Jane Doe `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseAnnotationFromNode(node) if err != nil { t.Errorf("error parsing valid a annotation") } if n := len(parser.doc.Annotations); n != 1 { t.Errorf("document should've had only one annotation, found %d", n) } ann := parser.doc.Annotations[0] // validating all the attributes of the annotations expectedComment := "Document level annotation" if ann.AnnotationComment != expectedComment { t.Errorf(`expected: "%s", found "%s"`, expectedComment, ann.AnnotationComment) } expectedDate := "2010-01-29T18:30:22Z" if expectedDate != ann.AnnotationDate { t.Errorf(`expected: "%s", found "%s"`, expectedDate, ann.AnnotationDate) } expectedAnnotator := "Jane Doe" if expectedAnnotator != ann.Annotator.Annotator { t.Errorf(`expected: "%s", found "%s"`, expectedAnnotator, ann.Annotator) } if ann.Annotator.AnnotatorType != "Person" { t.Errorf(`expected: "%s", found "%s"`, "Person", ann.Annotator.AnnotatorType) } expectedAnnotationType := "OTHER" if expectedAnnotationType != ann.AnnotationType { t.Errorf(`expected: "%s", found "%s"`, expectedAnnotationType, ann.AnnotationType) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_creation_info.go000066400000000000000000000027761463371440000245760ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // Cardinality: Mandatory, one. func (parser *rdfParser2_2) parseCreationInfoFromNode(ci *v2_2.CreationInfo, node *gordfParser.Node) error { for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case SPDX_LICENSE_LIST_VERSION: // 2.7 // cardinality: max 1 ci.LicenseListVersion = triple.Object.ID case SPDX_CREATOR: // 2.8 // cardinality: min 1 err := setCreator(triple.Object.ID, ci) if err != nil { return err } case SPDX_CREATED: // 2.9 // cardinality: exactly 1 ci.Created = triple.Object.ID case RDFS_COMMENT: // 2.10 ci.CreatorComment = triple.Object.ID case RDF_TYPE: continue default: return fmt.Errorf("unknown predicate %v while parsing a creation info", triple.Predicate) } } return nil } func setCreator(creatorStr string, ci *v2_2.CreationInfo) error { entityType, entity, err := ExtractSubs(creatorStr, ":") if err != nil { return fmt.Errorf("error setting creator of a creation info: %s", err) } creator := common.Creator{Creator: entity} switch entityType { case "Person", "Organization", "Tool": creator.CreatorType = entityType default: return fmt.Errorf("unknown creatorType %v in a creation info", entityType) } ci.Creators = append(ci.Creators, creator) return nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_creation_info_test.go000066400000000000000000000064141463371440000256260ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func Test_setCreator(t *testing.T) { // TestCase 1: invalid creator (empty) input := "" err := setCreator(input, &spdx.CreationInfo{}) if err == nil { t.Errorf("shoud've raised an error due to invalid input") } // TestCase 2: invalid entity type input = "Company: some company" err = setCreator(input, &spdx.CreationInfo{}) if err == nil { t.Errorf("shoud've raised an error due to unknown entity type") } // TestCase 3: valid input input = "Person: Jane Doe" ci := &spdx.CreationInfo{} err = setCreator(input, ci) if err != nil { t.Errorf("error parsing a valid input: %v", err) } if len(ci.Creators) != 1 { t.Errorf("creationInfo should've had 1 creatorPersons, found %d", len(ci.Creators)) } expectedPerson := "Jane Doe" if ci.Creators[0].Creator != expectedPerson { t.Errorf("expected %s, found %s", expectedPerson, ci.Creators[0]) } } func Test_rdfParser2_2_parseCreationInfoFromNode(t *testing.T) { // TestCase 1: invalid creator must raise an error parser, _ := parserFromBodyContent(` 2.6 Person Unknown 2018-08-24T19:55:34Z `) ciNode := parser.gordfParserObj.Triples[0].Subject err := parser.parseCreationInfoFromNode(&spdx.CreationInfo{}, ciNode) if err == nil { t.Errorf("invalid creator must raise an error") } // TestCase 2: unknown predicate must also raise an error parser, _ = parserFromBodyContent(` 2.6 Person: fossy (y) Organization: Tool: spdx2 2018-08-24T19:55:34Z `) ciNode = parser.gordfParserObj.Triples[0].Subject err = parser.parseCreationInfoFromNode(&spdx.CreationInfo{}, ciNode) if err == nil { t.Errorf("unknown predicate must raise an error") } // TestCase 2: unknown predicate must also raise an error parser, _ = parserFromBodyContent(` 2.6 Person: fossy 2018-08-24T19:55:34Z comment `) ciNode = parser.gordfParserObj.Triples[0].Subject ci := &spdx.CreationInfo{} err = parser.parseCreationInfoFromNode(ci, ciNode) if err != nil { t.Errorf("unexpected error: %v", err) } if ci.LicenseListVersion != "2.6" { t.Errorf(`expected %s, found %s`, "2.6", ci.LicenseListVersion) } n := len(ci.Creators) if n != 1 { t.Errorf("expected 1 creatorPersons, found %d", n) } if ci.Creators[0].Creator != "fossy" { t.Errorf("expected %s, found %s", "fossy", ci.Creators[0].Creator) } expectedCreated := "2018-08-24T19:55:34Z" if ci.Created != expectedCreated { t.Errorf("expected %s, found %s", expectedCreated, ci.Created) } expectedComment := "comment" if ci.CreatorComment != expectedComment { t.Errorf("expected %s, found %s", expectedComment, ci.CreatorComment) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_file.go000066400000000000000000000162071463371440000226700ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // returns a file instance and the error if any encountered. func (parser *rdfParser2_2) getFileFromNode(fileNode *gordfParser.Node) (file *v2_2.File, err error) { file = &v2_2.File{} currState := parser.cache[fileNode.ID] if currState == nil { // this is the first time we are seeing this node. parser.cache[fileNode.ID] = &nodeState{ object: file, Color: WHITE, } } else if currState.Color == GREY { // we have already started parsing this file node and we needn't parse it again. return currState.object.(*v2_2.File), nil } // setting color to grey to indicate that we've started parsing this node. parser.cache[fileNode.ID].Color = GREY // setting color to black just before function returns to the caller to // indicate that parsing current node is complete. defer func() { parser.cache[fileNode.ID].Color = BLACK }() err = setFileIdentifier(fileNode.ID, file) // 4.2 if err != nil { return nil, err } if existingFile := parser.files[file.FileSPDXIdentifier]; existingFile != nil { file = existingFile } for _, subTriple := range parser.nodeToTriples(fileNode) { switch subTriple.Predicate.ID { case SPDX_FILE_NAME: // 4.1 // cardinality: exactly 1 file.FileName = subTriple.Object.ID case SPDX_NAME: // cardinality: exactly 1 // TODO: check where it will be set in the golang-tools spdx-data-model case RDF_TYPE: // cardinality: exactly 1 case SPDX_FILE_TYPE: // 4.3 // cardinality: min 0 fileType := "" fileType, err = parser.getFileTypeFromUri(subTriple.Object.ID) file.FileTypes = append(file.FileTypes, fileType) case SPDX_CHECKSUM: // 4.4 // cardinality: min 1 err = parser.setFileChecksumFromNode(file, subTriple.Object) case SPDX_LICENSE_CONCLUDED: // 4.5 // cardinality: (exactly 1 anyLicenseInfo) or (None) or (Noassertion) anyLicense, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing licenseConcluded: %v", err) } file.LicenseConcluded = anyLicense.ToLicenseString() case SPDX_LICENSE_INFO_IN_FILE: // 4.6 // cardinality: min 1 lic, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing licenseInfoInFile: %v", err) } file.LicenseInfoInFiles = append(file.LicenseInfoInFiles, lic.ToLicenseString()) case SPDX_LICENSE_COMMENTS: // 4.7 // cardinality: max 1 file.LicenseComments = subTriple.Object.ID // TODO: allow copyright text to be of type NOASSERTION case SPDX_COPYRIGHT_TEXT: // 4.8 // cardinality: exactly 1 file.FileCopyrightText = subTriple.Object.ID case SPDX_LICENSE_INFO_FROM_FILES: // TODO: implement it. It is not defined in the tools-golang model. // deprecated artifactOf (see sections 4.9, 4.10, 4.11) case SPDX_ARTIFACT_OF: // cardinality: min 0 var artifactOf *v2_2.ArtifactOfProject artifactOf, err = parser.getArtifactFromNode(subTriple.Object) file.ArtifactOfProjects = append(file.ArtifactOfProjects, artifactOf) case RDFS_COMMENT: // 4.12 // cardinality: max 1 file.FileComment = subTriple.Object.ID case SPDX_NOTICE_TEXT: // 4.13 // cardinality: max 1 file.FileNotice = getNoticeTextFromNode(subTriple.Object) case SPDX_FILE_CONTRIBUTOR: // 4.14 // cardinality: min 0 file.FileContributors = append(file.FileContributors, subTriple.Object.ID) case SPDX_FILE_DEPENDENCY: // cardinality: min 0 newFile, err := parser.getFileFromNode(subTriple.Object) if err != nil { return nil, fmt.Errorf("error setting a file dependency in a file: %v", err) } file.FileDependencies = append(file.FileDependencies, string(newFile.FileSPDXIdentifier)) case SPDX_ATTRIBUTION_TEXT: // cardinality: min 0 file.FileAttributionTexts = append(file.FileAttributionTexts, subTriple.Object.ID) case SPDX_ANNOTATION: // cardinality: min 0 err = parser.parseAnnotationFromNode(subTriple.Object) case SPDX_RELATIONSHIP: // cardinality: min 0 err = parser.parseRelationship(subTriple) default: return nil, fmt.Errorf("unknown triple predicate id %s", subTriple.Predicate.ID) } if err != nil { return nil, err } } parser.files[file.FileSPDXIdentifier] = file return file, nil } func (parser *rdfParser2_2) setFileChecksumFromNode(file *v2_2.File, checksumNode *gordfParser.Node) error { checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(checksumNode) if err != nil { return fmt.Errorf("error parsing checksumNode of a file: %v", err) } if file.Checksums == nil { file.Checksums = []common.Checksum{} } switch checksumAlgorithm { case common.SHA1, common.SHA224, common.SHA256, common.SHA384, common.SHA512, common.MD2, common.MD4, common.MD5, common.MD6: file.Checksums = append(file.Checksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue}) case "": return fmt.Errorf("empty checksum algorithm and value") default: return fmt.Errorf("unknown checksumAlgorithm %s for a file", checksumAlgorithm) } return nil } func (parser *rdfParser2_2) getArtifactFromNode(node *gordfParser.Node) (*v2_2.ArtifactOfProject, error) { artifactOf := &v2_2.ArtifactOfProject{} // setting artifactOfProjectURI attribute (which is optional) if node.NodeType == gordfParser.IRI { artifactOf.URI = node.ID } // parsing rest triples and attributes of the artifact. for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case RDF_TYPE: case DOAP_HOMEPAGE: artifactOf.HomePage = triple.Object.ID case DOAP_NAME: artifactOf.Name = triple.Object.ID default: return nil, fmt.Errorf("error parsing artifactOf predicate %s", triple.Predicate.ID) } } return artifactOf, nil } // TODO: check if the filetype is valid. func (parser *rdfParser2_2) getFileTypeFromUri(uri string) (string, error) { // fileType is given as a uri. for example: http://spdx.org/rdf/terms#fileType_text lastPart := getLastPartOfURI(uri) if !strings.HasPrefix(lastPart, "fileType_") { return "", fmt.Errorf("fileType Uri must begin with fileTYpe_. found: %s", lastPart) } return strings.TrimPrefix(lastPart, "fileType_"), nil } // populates parser.doc.Files by a list of files which are not // associated with a package by the hasFile attribute // assumes: all the packages are already parsed. func (parser *rdfParser2_2) setUnpackagedFiles() { for fileID := range parser.files { if !parser.assocWithPackage[fileID] { parser.doc.Files = append(parser.doc.Files, parser.files[fileID]) } } } func setFileIdentifier(idURI string, file *v2_2.File) (err error) { idURI = strings.TrimSpace(idURI) uriFragment := getLastPartOfURI(idURI) file.FileSPDXIdentifier, err = ExtractElementID(uriFragment) if err != nil { return fmt.Errorf("error setting file identifier: %s", err) } return nil } func getNoticeTextFromNode(node *gordfParser.Node) string { switch node.ID { case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL: return "NOASSERTION" default: return node.ID } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_file_test.go000066400000000000000000000712741463371440000237340ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "bufio" "strings" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" rdfloader2 "github.com/spdx/gordf/rdfloader/xmlreader" gordfWriter "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // content is the tags within the rdf:RDF tag // pads the content with the enclosing rdf:RDF tag func wrapIntoTemplate(content string) string { header := `` footer := `` return header + content + footer } func parserFromBodyContent(content string) (*rdfParser2_2, error) { rdfContent := wrapIntoTemplate(content) xmlreader := rdfloader2.XMLReaderFromFileObject(bufio.NewReader(strings.NewReader(rdfContent))) rootBlock, err := xmlreader.Read() if err != nil { return nil, err } parser := gordfParser.New() err = parser.Parse(rootBlock) if err != nil { return nil, err } nodeToTriples := gordfWriter.GetNodeToTriples(parser.Triples) rdfParser := NewParser2_2(parser, nodeToTriples) return rdfParser, err } func Test_rdfParser2_2_getArtifactFromNode(t *testing.T) { // TestCase 1: artifactOf without project URI rdfParser, err := parserFromBodyContent( ` http://www.openjena.org/ Jena `) if err != nil { t.Errorf("unexpected error while parsing a valid example: %v", err) } artifactOfNode := gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object artifact, err := rdfParser.getArtifactFromNode(artifactOfNode) if err != nil { t.Errorf("error parsing a valid artifactOf node: %v", err) } if artifact.Name != "Jena" { t.Errorf("expected name of artifact: %s, found: %s", "Jena", artifact.Name) } expectedHomePage := "http://www.openjena.org/" if artifact.HomePage != expectedHomePage { t.Errorf("wrong artifact homepage. Expected: %s, found: %s", expectedHomePage, artifact.HomePage) } if artifact.URI != "" { t.Errorf("wrong artifact URI. Expected: %s, found: %s", "", artifact.URI) } // TestCase 2: artifactOf with a Project URI rdfParser, err = parserFromBodyContent( ` http://www.openjena.org/ Jena `) if err != nil { t.Errorf("unexpected error while parsing a valid example: %v", err) } artifactOfNode = gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object artifact, err = rdfParser.getArtifactFromNode(artifactOfNode) if err != nil { t.Errorf("error parsing a valid artifactOf node: %v", err) } expectedURI := "http://subversion.apache.org/doap.rdf" if artifact.URI != expectedURI { t.Errorf("wrong artifact URI. Expected: %s, found: %s", expectedURI, artifact.URI) } // TestCase 3: artifactOf with unknown predicate rdfParser, err = parserFromBodyContent( ` http://www.openjena.org/ Jena `) if err != nil { t.Errorf("unexpected error while parsing a valid example: %v", err) } artifactOfNode = gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object _, err = rdfParser.getArtifactFromNode(artifactOfNode) if err == nil { t.Errorf("must've raised an error for an invalid predicate") } } func Test_rdfParser2_2_getFileTypeFromUri(t *testing.T) { rdfParser, _ := parserFromBodyContent(``) // TestCase 1: Valid fileType URI: fileTypeURI := "http://spdx.org/rdf/terms#fileType_source" fileType, err := rdfParser.getFileTypeFromUri(fileTypeURI) if err != nil { t.Errorf("error in a valid example: %v", err) } if fileType != "source" { t.Errorf("wrong fileType. expected: %s, found: %s", "source", fileType) } // TestCase 2: Invalid fileType URI format. fileTypeURI = "http://spdx.org/rdf/terms#source" fileType, err = rdfParser.getFileTypeFromUri(fileTypeURI) if err == nil { t.Error("should've raised an error for invalid fileType") } } func Test_rdfParser2_2_setUnpackagedFiles(t *testing.T) { // unpackaged files are the files which are not associated with any package // file associated with a package sets parser.assocWithPackage[fileID] to true. rdfParser, _ := parserFromBodyContent(``) file1 := &v2_2.File{FileSPDXIdentifier: common.ElementID("file1")} file2 := &v2_2.File{FileSPDXIdentifier: common.ElementID("file2")} file3 := &v2_2.File{FileSPDXIdentifier: common.ElementID("file3")} // setting files to the document as if it were to be set when it was parsed using triples. rdfParser.files[file1.FileSPDXIdentifier] = file1 rdfParser.files[file2.FileSPDXIdentifier] = file2 rdfParser.files[file3.FileSPDXIdentifier] = file3 // assuming file1 is associated with a package rdfParser.assocWithPackage[file1.FileSPDXIdentifier] = true rdfParser.setUnpackagedFiles() // after setting unpackaged files, parser.doc.Files must've file2 and file3 if n := len(rdfParser.doc.Files); n != 2 { t.Errorf("unpackage files should've had 2 files, found %d files", n) } // checking if the unpackagedFiles contain only file2 & file3. for _, file := range rdfParser.doc.Files { switch string(file.FileSPDXIdentifier) { case "file2", "file3": continue default: t.Errorf("unexpected file with id %s found in unpackaged files", file.FileSPDXIdentifier) } } } func Test_setFileIdentifier(t *testing.T) { file := &v2_2.File{} // TestCase 1: valid example err := setFileIdentifier("http://spdx.org/documents/spdx-toolsv2.1.7-SNAPSHOT#SPDXRef-129", file) if err != nil { t.Errorf("unexpected error: %v", err) } if file.FileSPDXIdentifier != "129" { t.Errorf("expected %s, found: %s", "129", file.FileSPDXIdentifier) } // TestCase 2: invalid example err = setFileIdentifier("http://spdx.org/documents/spdx-toolsv2.1.7-SNAPSHOT#129", file) if err == nil { t.Errorf("should've raised an error for an invalid example") } } func Test_rdfParser2_2_setFileChecksumFromNode(t *testing.T) { // TestCase 1: md5 checksum parser, _ := parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file := &v2_2.File{} err := parser.setFileChecksumFromNode(file, checksumNode) if err != nil { t.Errorf("error parsing a valid checksum node") } checksumValue := "d2356e0fe1c0b85285d83c6b2ad51b5f" for _, checksum := range file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "" { t.Errorf("incorrectly set sha1, should've been empty") } case common.SHA256: if checksum.Value != "" { t.Errorf("incorrectly set sha256, should've been empty") } case common.MD5: if checksum.Value != checksumValue { t.Errorf("wrong checksum value for md5. Expected: %s, found: %s", checksumValue, checksum.Value) } } } // TestCase 2: valid sha1 checksum parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &v2_2.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err != nil { t.Errorf("error parsing a valid checksum node") } for _, checksum := range file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != checksumValue { t.Errorf("wrong checksum value for sha1. Expected: %s, found: %s", checksumValue, checksum.Value) } case common.SHA256: if checksum.Value != "" { t.Errorf("incorrectly set sha256, should've been empty") } case common.MD5: if checksum.Value != checksumValue { t.Errorf("incorrectly set md5, should've been empty") } } } // TestCase 3: valid sha256 checksum parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &v2_2.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err != nil { t.Errorf("error parsing a valid checksum node") } for _, checksum := range file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != checksumValue { t.Errorf("incorrectly set sha1, should've been empty") } case common.SHA256: if checksum.Value != checksumValue { t.Errorf("wrong checksum value for sha256. Expected: %s, found: %s", checksumValue, checksum.Value) } case common.MD5: if checksum.Value != checksumValue { t.Errorf("incorrectly set md5, should've been empty") } } } // TestCase 4: checksum node without one of the mandatory attributes parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &v2_2.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err == nil { t.Errorf("should've raised an error parsing an invalid checksum node") } // TestCase 5: invalid checksum algorithm parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &v2_2.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err == nil { t.Errorf("should've raised an error parsing an invalid checksum node") } // TestCase 6: valid checksum algorithm which is invalid for file (like md4, md6, sha384, etc.) parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &v2_2.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err == nil { t.Errorf("should've raised an error parsing an invalid checksum algorithm for a file") } } func Test_rdfParser2_2_getFileFromNode(t *testing.T) { // TestCase 1: file with invalid id parser, _ := parserFromBodyContent(` `) fileNode := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err := parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid file ID") } // TestCase 2: invalid fileType parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid fileType") } // TestCase 3: invalid file checksum parser, _ = parserFromBodyContent(` 0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid checksum") } // TestCase 4: invalid license concluded parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid license Concluded") } // TestCase 5: invalid artifactOf attribute parser, _ = parserFromBodyContent(` Jena `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid artifactOf predicate") } // TestCase 6: invalid file dependency parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid fileDependency") } // TestCase 7: invalid annotation with unknown predicate parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid annotation predicate") } // TestCase 8: invalid relationship parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid relationship Type") } // TestCase 8: unknown predicate parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Error("should've raised an error stating invalid predicate for a file") } // TestCase 9: invalid licenseInfoInFile. parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Error("should've raised an error stating invalid licenseInfoInFile for a file") } // TestCase 10: Splitting of File definition into parents of different tags mustn't create new file objects. fileDefinitions := []string{ ` time-1.9/ChangeLog `, ` `, } parser, _ = parserFromBodyContent(strings.Join(fileDefinitions, "")) var file *v2_2.File packageTypeTriples := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE) for _, typeTriple := range packageTypeTriples { pkg, err := parser.getPackageFromNode(typeTriple.Subject) if err != nil { t.Errorf("unexpected error parsing a valid package: %v", err) } if n := len(pkg.Files); n != 1 { t.Errorf("expected package to contain exactly 1 file. Found %d files", n) } for _, file = range pkg.Files { } } // checking if all the attributes that spanned over a several tags are set in the same variable. expectedFileName := "time-1.9/ChangeLog" if file.FileName != expectedFileName { t.Errorf("expected %s, found %s", expectedFileName, file.FileName) } expectedLicenseConcluded := "NOASSERTION" if file.LicenseConcluded != expectedLicenseConcluded { t.Errorf("expected %s, found %s", expectedLicenseConcluded, file.LicenseConcluded) } expectedFileType := "source" if file.FileTypes[0] != expectedFileType { t.Errorf("expected %s, found %s", expectedFileType, file.FileTypes) } expectedLicenseInfoInFile := "NOASSERTION" if file.LicenseInfoInFiles[0] != expectedLicenseInfoInFile { t.Errorf("expected %s, found %s", expectedLicenseInfoInFile, file.LicenseInfoInFiles[0]) } // TestCase 12: checking if recursive dependencies are resolved. parser, _ = parserFromBodyContent(` ParentFile `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject file, err = parser.getFileFromNode(fileNode) // TestCase 11: all valid attribute and it's values. parser, _ = parserFromBodyContent(` time-1.9/ChangeLog 0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd no comments from spdx file http://www.openjena.org/ Jena no comments Some Organization attribution text 2011-01-29T18:30:22Z File level annotation copied from a spdx document Person: File Commenter `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject file, err = parser.getFileFromNode(fileNode) if err != nil { t.Errorf("unexpected error parsing a valid file: %v", err) } // checking each and every attribute of the obtained file. expectedFileName = "time-1.9/ChangeLog" if file.FileName != expectedFileName { t.Errorf("expected %s, found %s", expectedFileName, file.FileName) } if len(file.FileTypes) != 1 { t.Errorf("given file should have 1 fileType attribute. found %d", len(file.FileTypes)) } expectedFileType = "source" if file.FileTypes[0] != expectedFileType { t.Errorf("expected %s, found %s", expectedFileType, file.FileTypes) } expectedChecksum := "0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd" for _, checksum := range file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != expectedChecksum { t.Errorf("expected %s, found %s", expectedChecksum, checksum.Value) } } } expectedLicenseConcluded = "NOASSERTION" if file.LicenseConcluded != expectedLicenseConcluded { t.Errorf("expected %s, found %s", expectedLicenseConcluded, file.LicenseConcluded) } if len(file.LicenseInfoInFiles) != 1 { t.Errorf("given file should have 1 licenseInfoInFile attribute. found %d", len(file.LicenseInfoInFiles)) } expectedLicenseInfoInFile = "NOASSERTION" if file.LicenseInfoInFiles[0] != expectedLicenseInfoInFile { t.Errorf("expected %s, found %s", expectedLicenseInfoInFile, file.LicenseInfoInFiles[0]) } expectedLicenseComments := "no comments" if file.LicenseComments != expectedLicenseComments { t.Errorf("expected %s, found %s", expectedLicenseComments, file.LicenseComments) } expectedCopyrightText := "from spdx file" if file.FileCopyrightText != expectedCopyrightText { t.Errorf("expected %s, found %s", expectedCopyrightText, file.FileCopyrightText) } if n := len(file.ArtifactOfProjects); n != 1 { t.Errorf("given file should have 1 artifactOfProjects attribute. found %d", n) } artifactOf := file.ArtifactOfProjects[0] expectedHomePage := "http://www.openjena.org/" if artifactOf.HomePage != expectedHomePage { t.Errorf("expected %s, found %s", expectedHomePage, artifactOf.HomePage) } if artifactOf.Name != "Jena" { t.Errorf("expected %s, found %s", "Jena", artifactOf.Name) } if artifactOf.URI != "" { t.Errorf("expected artifactOf uri to be empty, found %s", artifactOf.URI) } expectedFileComment := "no comments" if file.FileComment != expectedFileComment { t.Errorf("expected %s, found %s", expectedFileName, file.FileComment) } expectedNoticeText := "NOASSERTION" if file.FileNotice != expectedNoticeText { t.Errorf("expected %s, found %s", expectedNoticeText, file.FileNotice) } if n := len(file.FileContributors); n != 1 { t.Errorf("given file should have 1 fileContributor. found %d", n) } expectedFileContributor := "Some Organization" if file.FileContributors[0] != expectedFileContributor { t.Errorf("expected %s, found %s", expectedFileContributor, file.FileContributors) } if n := len(file.FileDependencies); n != 1 { t.Errorf("given file should have 1 fileDependencies. found %d", n) } expectedFileDependency := "CommonsLangSrc" if file.FileDependencies[0] != expectedFileDependency { t.Errorf("expected %s, found %s", expectedFileDependency, file.FileDependencies[0]) } if n := len(file.FileAttributionTexts); n != 1 { t.Errorf("given file should have 1 attributionText. found %d", n) } expectedAttributionText := "attribution text" if file.FileAttributionTexts[0] != expectedAttributionText { t.Errorf("expected %s, found %s", expectedAttributionText, file.FileAttributionTexts[0]) } if n := len(parser.doc.Annotations); n != 1 { t.Errorf("doc should've had 1 annotation. found %d", n) } ann := parser.doc.Annotations[0] expectedAnnDate := "2011-01-29T18:30:22Z" if ann.AnnotationDate != expectedAnnDate { t.Errorf("expected %s, found %s", expectedAnnDate, ann.AnnotationDate) } expectedAnnComment := "File level annotation copied from a spdx document" if ann.AnnotationComment != expectedAnnComment { t.Errorf("expected %s, found %s", expectedAnnComment, ann.AnnotationComment) } expectedAnnotationType := "OTHER" if ann.AnnotationType != expectedAnnotationType { t.Errorf("expected %s, found %s", expectedAnnotationType, ann.AnnotationType) } expectedAnnotator := "File Commenter" if ann.Annotator.Annotator != expectedAnnotator { t.Errorf("expected %s, found %s", expectedAnnotator, ann.Annotator) } expectedAnnotatorType := "Person" if ann.AnnotationType != expectedAnnotationType { t.Errorf("expected %s, found %s", expectedAnnotatorType, ann.Annotator.AnnotatorType) } if n := len(parser.doc.Relationships); n != 1 { t.Errorf("doc should've had 1 relation. found %d", n) } reln := parser.doc.Relationships[0] expectedRefAEID := "item177" if reln.RefA.DocumentRefID != "" { t.Errorf("expected refA.DocumentRefID to be empty, found %s", reln.RefA.DocumentRefID) } if string(reln.RefA.ElementRefID) != expectedRefAEID { t.Errorf("expected %s, found %s", expectedRefAEID, reln.RefA.ElementRefID) } expectedRefBEID := "Package" if reln.RefB.DocumentRefID != "" { t.Errorf("expected refB.DocumentRefID to be empty, found %s", reln.RefB.DocumentRefID) } if string(reln.RefB.ElementRefID) != expectedRefBEID { t.Errorf("expected %s, found %s", expectedRefBEID, reln.RefB.ElementRefID) } expectedRelationType := "contains" if reln.Relationship != expectedRelationType { t.Errorf("expected %s, found %s", expectedRefBEID, reln.RefB.ElementRefID) } if reln.RelationshipComment != "" { t.Errorf("expected relationship comment to be empty, found %s", reln.RelationshipComment) } } func Test_getNoticeTextFromNode(t *testing.T) { // TestCase 1: SPDX_NOASSERTION_SMALL must return NOASSERTION output := getNoticeTextFromNode(&gordfParser.Node{ NodeType: gordfParser.IRI, ID: SPDX_NOASSERTION_SMALL, }) if strings.ToUpper(output) != "NOASSERTION" { t.Errorf("expected NOASSERTION, found %s", strings.ToUpper(output)) } // TestCase 2: SPDX_NOASSERTION_CAPS must return NOASSERTION output = getNoticeTextFromNode(&gordfParser.Node{ NodeType: gordfParser.IRI, ID: SPDX_NOASSERTION_CAPS, }) if strings.ToUpper(output) != "NOASSERTION" { t.Errorf("expected NOASSERTION, found %s", strings.ToUpper(output)) } // TestCase 3: not a NOASSERTION must return the field verbatim // TestCase 1: SPDX_NOASSERTION_SMALL must return NOASSERTION output = getNoticeTextFromNode(&gordfParser.Node{ NodeType: gordfParser.IRI, ID: "text", }) if output != "text" { t.Errorf("expected text, found %s", output) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_license.go000066400000000000000000000242571463371440000233770ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "errors" "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" ) // AnyLicense is a baseClass for all the licenses // All the types of licenses is a sub-type of AnyLicense, // either directly or indirectly. // This function acts as a mux for all the licenses. Based on the input, it // decides which type of license it is and passes control to that type of // license parser to parse the given input. func (parser *rdfParser2_2) getAnyLicenseFromNode(node *gordfParser.Node) (AnyLicenseInfo, error) { currState := parser.cache[node.ID] if currState == nil { // there is no entry about the state of current package node. // this is the first time we're seeing this node. parser.cache[node.ID] = &nodeState{ object: nil, // not storing the object as we won't retrieve it later. Color: WHITE, } } else if currState.Color == GREY { // we have already started parsing this license node. // We have a cyclic dependency! return nil, errors.New("Couldn't parse license: found a cyclic dependency on " + node.ID) } // setting color of the state to grey to indicate that we've started to // parse this node once. parser.cache[node.ID].Color = GREY // setting state color to black when we're done parsing this node. defer func() { parser.cache[node.ID].Color = BLACK }() associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) if len(associatedTriples) == 0 { // just a license uri string was found. return parser.getSpecialLicenseFromNode(node) } // we have some attributes associated with the license node. nodeType, err := getNodeTypeFromTriples(associatedTriples, node) if err != nil { return nil, fmt.Errorf("error parsing license triple: %v", err) } switch nodeType { case SPDX_DISJUNCTIVE_LICENSE_SET: return parser.getDisjunctiveLicenseSetFromNode(node) case SPDX_CONJUNCTIVE_LICENSE_SET: return parser.getConjunctiveLicenseSetFromNode(node) case SPDX_EXTRACTED_LICENSING_INFO: return parser.getExtractedLicensingInfoFromNode(node) case SPDX_LISTED_LICENSE, SPDX_LICENSE: return parser.getLicenseFromNode(node) case SPDX_WITH_EXCEPTION_OPERATOR: return parser.getWithExceptionOperatorFromNode(node) case SPDX_OR_LATER_OPERATOR: return parser.getOrLaterOperatorFromNode(node) case SPDX_SIMPLE_LICENSING_INFO: return parser.getSimpleLicensingInfoFromNode(node) } return nil, fmt.Errorf("Unknown subTag (%s) found while parsing AnyLicense", nodeType) } func (parser *rdfParser2_2) getLicenseExceptionFromNode(node *gordfParser.Node) (exception LicenseException, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) for _, triple := range associatedTriples { value := triple.Object.ID switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_LICENSE_EXCEPTION_ID: exception.licenseExceptionId = value case SPDX_LICENSE_EXCEPTION_TEXT: exception.licenseExceptionText = value case RDFS_SEE_ALSO: if !isUriValid(value) { return exception, fmt.Errorf("invalid uri (%s) for seeAlso attribute of LicenseException", value) } exception.seeAlso = value case SPDX_NAME: exception.name = value case SPDX_EXAMPLE: exception.example = value case RDFS_COMMENT: exception.comment = value default: return exception, fmt.Errorf("invalid predicate(%s) for LicenseException", triple.Predicate) } } return exception, nil } func (parser *rdfParser2_2) getSimpleLicensingInfoFromNode(node *gordfParser.Node) (SimpleLicensingInfo, error) { simpleLicensingTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) return parser.getSimpleLicensingInfoFromTriples(simpleLicensingTriples) } func (parser *rdfParser2_2) getWithExceptionOperatorFromNode(node *gordfParser.Node) (operator WithExceptionOperator, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) var memberFound bool for _, triple := range associatedTriples { switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_MEMBER: if memberFound { return operator, fmt.Errorf("more than one member found in the WithExceptionOperator (expected only 1)") } memberFound = true member, err := parser.getSimpleLicensingInfoFromNode(triple.Object) if err != nil { return operator, fmt.Errorf("error parsing member of a WithExceptionOperator: %v", err) } operator.member = member case SPDX_LICENSE_EXCEPTION: operator.licenseException, err = parser.getLicenseExceptionFromNode(triple.Object) if err != nil { return operator, fmt.Errorf("error parsing licenseException of WithExceptionOperator: %v", err) } default: return operator, fmt.Errorf("unknown predicate (%s) for a WithExceptionOperator", triple.Predicate.ID) } } return operator, nil } func (parser *rdfParser2_2) getOrLaterOperatorFromNode(node *gordfParser.Node) (operator OrLaterOperator, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) n := len(associatedTriples) if n != 2 { return operator, fmt.Errorf("orLaterOperator must be associated with exactly one tag. found %v triples", n-1) } for _, triple := range associatedTriples { switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_MEMBER: operator.member, err = parser.getSimpleLicensingInfoFromNode(triple.Object) if err != nil { return operator, fmt.Errorf("error parsing simpleLicensingInfo of OrLaterOperator: %v", err) } default: return operator, fmt.Errorf("unknown predicate %s", triple.Predicate.ID) } } return operator, nil } // SpecialLicense is a type of license which is not defined in any of the // spdx documents, it is a type of license defined for the sake of brevity. // It can be [NONE|NOASSERTION|LicenseRef-] func (parser *rdfParser2_2) getSpecialLicenseFromNode(node *gordfParser.Node) (lic SpecialLicense, err error) { uri := strings.TrimSpace(node.ID) switch uri { case SPDX_NONE_CAPS, SPDX_NONE_SMALL: return SpecialLicense{ value: NONE, }, nil case SPDX_NOASSERTION_SMALL, SPDX_NOASSERTION_CAPS: return SpecialLicense{ value: NOASSERTION, }, nil } // the license is neither NONE nor NOASSERTION // checking if the license is among the standardLicenses licenseAbbreviation := getLastPartOfURI(uri) for _, stdLicense := range AllStandardLicenseIDS() { if licenseAbbreviation == stdLicense { return SpecialLicense{ value: SpecialLicenseValue(stdLicense), }, nil } } return lic, fmt.Errorf("found a custom license uri (%s) without any associated fields", uri) } func (parser *rdfParser2_2) getDisjunctiveLicenseSetFromNode(node *gordfParser.Node) (DisjunctiveLicenseSet, error) { licenseSet := DisjunctiveLicenseSet{ members: []AnyLicenseInfo{}, } for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_MEMBER: member, err := parser.getAnyLicenseFromNode(triple.Object) if err != nil { return licenseSet, fmt.Errorf("error parsing disjunctive license set: %v", err) } licenseSet.members = append(licenseSet.members, member) } } return licenseSet, nil } func (parser *rdfParser2_2) getConjunctiveLicenseSetFromNode(node *gordfParser.Node) (ConjunctiveLicenseSet, error) { licenseSet := ConjunctiveLicenseSet{ members: []AnyLicenseInfo{}, } for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_MEMBER: member, err := parser.getAnyLicenseFromNode(triple.Object) if err != nil { return licenseSet, fmt.Errorf("error parsing conjunctive license set: %v", err) } licenseSet.members = append(licenseSet.members, member) default: return licenseSet, fmt.Errorf("unknown subTag for ConjunctiveLicenseSet: %s", triple.Predicate.ID) } } return licenseSet, nil } func (parser *rdfParser2_2) getSimpleLicensingInfoFromTriples(triples []*gordfParser.Triple) (lic SimpleLicensingInfo, err error) { for _, triple := range triples { switch triple.Predicate.ID { case RDFS_COMMENT: lic.comment = triple.Object.ID case SPDX_LICENSE_ID: lic.licenseID = triple.Object.ID case SPDX_NAME: lic.name = triple.Object.ID case RDFS_SEE_ALSO: if !isUriValid(triple.Object.ID) { return lic, fmt.Errorf("%s is not a valid uri for seeAlso attribute of a License", triple.Object.ID) } lic.seeAlso = append(lic.seeAlso, triple.Object.ID) case SPDX_EXAMPLE: lic.example = triple.Object.ID case RDF_TYPE: continue default: return lic, fmt.Errorf("unknown predicate(%s) for simple licensing info", triple.Predicate) } } return lic, nil } func (parser *rdfParser2_2) getLicenseFromNode(node *gordfParser.Node) (lic License, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) var restTriples []*gordfParser.Triple for _, triple := range associatedTriples { value := triple.Object.ID switch triple.Predicate.ID { case SPDX_IS_OSI_APPROVED: lic.isOsiApproved, err = boolFromString(value) if err != nil { return lic, fmt.Errorf("error parsing isOsiApproved attribute of a License: %v", err) } case SPDX_LICENSE_TEXT: lic.licenseText = value case SPDX_STANDARD_LICENSE_HEADER: lic.standardLicenseHeader = value case SPDX_STANDARD_LICENSE_TEMPLATE: lic.standardLicenseTemplate = value case SPDX_STANDARD_LICENSE_HEADER_TEMPLATE: lic.standardLicenseHeaderTemplate = value case SPDX_IS_DEPRECATED_LICENSE_ID: lic.isDeprecatedLicenseID, err = boolFromString(value) if err != nil { return lic, fmt.Errorf("error parsing isDeprecatedLicenseId attribute of a License: %v", err) } case SPDX_IS_FSF_LIBRE: lic.isFsfLibre, err = boolFromString(value) if err != nil { return lic, fmt.Errorf("error parsing isFsfLibre attribute of a License: %v", err) } default: restTriples = append(restTriples, triple) } } lic.SimpleLicensingInfo, err = parser.getSimpleLicensingInfoFromTriples(restTriples) if err != nil { return lic, fmt.Errorf("error setting simple licensing information of a License: %s", err) } return lic, nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_license_test.go000066400000000000000000001160521463371440000244310ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "sort" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" ) func Test_rdfParser2_2_getAnyLicenseFromNode(t *testing.T) { // since this function is a mux, we just have to make sure that with each // type of input, it is able to redirect the request to an appropriate // license getter. // TestCase 1: input node is just a node string without any associated // triple (either a NONE|NOASSERTION) because for other case, // the license should've been associated with other triples parser, _ := parserFromBodyContent(``) inputNode := &gordfParser.Node{ NodeType: gordfParser.IRI, ID: NS_SPDX + "NONE", } lic, err := parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a SpecialLicense switch lic.(type) { case SpecialLicense: default: t.Errorf("expected license to be of type SpecialLicense, found %v", reflect.TypeOf(lic)) } // TestCase 2: DisjunctiveLicenseSet: parser, _ = parserFromBodyContent(` `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a DisjunctiveLicenseSet switch lic.(type) { case DisjunctiveLicenseSet: default: t.Errorf("expected license to be of type DisjunctiveLicenseSet, found %v", reflect.TypeOf(lic)) } // TestCase 3: ConjunctiveLicenseSet: parser, _ = parserFromBodyContent(` `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a ConjunctiveLicenseSet switch lic.(type) { case ConjunctiveLicenseSet: default: t.Errorf("expected license to be of type ConjunctiveLicenseSet, found %v", reflect.TypeOf(lic)) } // TestCase 4: ExtractedLicensingInfo parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a ExtractedLicensingInfo switch lic.(type) { case ExtractedLicensingInfo: default: t.Errorf("expected license to be of type ExtractedLicensingInfo, found %v", reflect.TypeOf(lic)) } // TestCase 4: ExtractedLicensingInfo parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a ExtractedLicensingInfo switch lic.(type) { case ExtractedLicensingInfo: default: t.Errorf("expected license to be of type ExtractedLicensingInfo, found %v", reflect.TypeOf(lic)) } // TestCase 5: License parser, _ = parserFromBodyContent(` <> Apache License Version 2.0, January 2004 http://www.apache.org/licenses/<><> TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION<> <> Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. <> Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. <> Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. <> Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: <> You must give any other recipients of the Work or Derivative Works a copy of this License; and <> You must cause any modified files to carry prominent notices stating that You changed the files; and <> You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and <> If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. <> Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. <> Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. <> Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. <> Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. <> Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.<> END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright <> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.<> http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 Apache-2.0 true http://www.opensource.org/licenses/Apache-2.0 ... ... `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a License switch lic.(type) { case License: default: t.Errorf("expected license to be of type License, found %v", reflect.TypeOf(lic)) } // TestCase 5: WithExceptionOperator parser, _ = parserFromBodyContent(` Libtool-exception `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a WithExceptionOperator switch lic.(type) { case WithExceptionOperator: default: t.Errorf("expected license to be of type WithExceptionOperator, found %v", reflect.TypeOf(lic)) } // TestCase 6: OrLaterOperator parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a OrLaterOperator switch lic.(type) { case OrLaterOperator: default: t.Errorf("expected license to be of type OrLaterOperator, found %v", reflect.TypeOf(lic)) } // TestCase 7: checking if an unknown license raises an error. parser, _ = parserFromBodyContent(` `) node := parser.gordfParserObj.Triples[0].Subject _, err = parser.getAnyLicenseFromNode(node) t.Log(err) if err == nil { t.Errorf("should've raised an error for invalid input") } // TestCase 8: cyclic dependent license must raise an error. parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getAnyLicenseFromNode(node) if err == nil { t.Errorf("expected an error due to cyclic dependent license. found %v", err) } } func Test_rdfParser2_2_getConjunctiveLicenseSetFromNode(t *testing.T) { var parser *rdfParser2_2 var err error var licenseNode *gordfParser.Node var license ConjunctiveLicenseSet // TestCase 1: invalid license member parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject _, err = parser.getConjunctiveLicenseSetFromNode(licenseNode) if err == nil { t.Errorf("expected an error saying invalid license member, found ") } // TestCase 2: invalid predicate in the licenseSet. parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject _, err = parser.getConjunctiveLicenseSetFromNode(licenseNode) if err == nil { t.Errorf("expected an error saying invalid predicate found") } // TestCase 3: valid example. parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject license, err = parser.getConjunctiveLicenseSetFromNode(licenseNode) if err != nil { t.Errorf("unexpected error parsing licenseSet: %v", err) } nMembers := len(license.members) if nMembers != 2 { t.Errorf("expected licenseSet to have 2 members, found %d", nMembers) } licenseMembers := mapLicensesToStrings(license.members) expectedLicenseMembers := []string{"LGPL-2.0", "Nokia"} sort.Strings(licenseMembers) if !reflect.DeepEqual(licenseMembers, expectedLicenseMembers) { t.Errorf("expected %v, found %v", expectedLicenseMembers, licenseMembers) } } func Test_rdfParser2_2_getDisjunctiveLicenseSetFromNode(t *testing.T) { var parser *rdfParser2_2 var err error var licenseNode *gordfParser.Node var license DisjunctiveLicenseSet // TestCase 1: invalid license member parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject _, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode) if err == nil { t.Errorf("expected an error saying invalid license member, found ") } // TestCase 2: invalid predicate in the licenseSet. parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject _, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode) if err == nil { t.Errorf("expected an error saying invalid predicate found") } // TestCase 3: valid example. parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject license, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode) if err != nil { t.Errorf("unexpected error parsing licenseSet: %v", err) } nMembers := len(license.members) if nMembers != 2 { t.Errorf("expected licenseSet to have 2 members, found %d", nMembers) } licenseMembers := mapLicensesToStrings(license.members) expectedLicenseMembers := []string{"LGPL-2.0", "Nokia"} sort.Strings(licenseMembers) if !reflect.DeepEqual(licenseMembers, expectedLicenseMembers) { t.Errorf("expected %v, found %v", expectedLicenseMembers, licenseMembers) } } func Test_rdfParser2_2_getLicenseExceptionFromNode(t *testing.T) { var licenseException LicenseException var err error var node *gordfParser.Node var parser *rdfParser2_2 // TestCase 1: invalid value for rdf:seeAlso parser, _ = parserFromBodyContent(` see-also Libtool-exception `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getLicenseExceptionFromNode(node) if err == nil { t.Errorf("should've raised an error due to invalid uri for rdfs:seeAlso") } // TestCase 2: invalid predicate for licenseException // TestCase 1: invalid value for rdf:seeAlso parser, _ = parserFromBodyContent(` Libtool-exception `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getLicenseExceptionFromNode(node) if err == nil { t.Errorf("should've raised an error due to invalid predicate") } // TestCase 3: everything valid // TestCase 1: invalid value for rdf:seeAlso parser, _ = parserFromBodyContent(` no example Libtool-exception no comments text name `) node = parser.gordfParserObj.Triples[0].Subject licenseException, err = parser.getLicenseExceptionFromNode(node) if err != nil { t.Fatalf("unexpected error while parsing a valid licenseException") } expectedCrossReference := "http://www.opensource.org/licenses/GPL-3.0" if licenseException.seeAlso != expectedCrossReference { t.Errorf("expected: %s, found: %s", expectedCrossReference, licenseException.seeAlso) } expectedExample := "no example" if licenseException.example != expectedExample { t.Errorf("expected: %s, got: %s", expectedExample, licenseException.example) } if licenseException.licenseExceptionId != "Libtool-exception" { t.Errorf("expected: %s, got: %s", "Libtool-exception", licenseException.licenseExceptionId) } if licenseException.comment != "no comments" { t.Errorf("expected: %s, got: %s", "no comments", licenseException.comment) } if licenseException.licenseExceptionText != "text" { t.Errorf("expected: '%s', got: '%s'", "text", licenseException.licenseExceptionText) } if licenseException.name != "name" { t.Errorf("expected: '%s', got: '%s'", "name", licenseException.name) } } func Test_rdfParser2_2_getLicenseFromNode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var license License var err error // TestCase 1: isOsiApproved is not a valid boolean parser, _ = parserFromBodyContent(` no `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("expected function to raise an error stating isOsiApproved should be a valid boolean type") } // TestCase 2: rdf:seeAlso not a valid uri must raise an error parser, _ = parserFromBodyContent(` uri `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("expected function to raise an error stating invalid uri for rdfs:seeAlso") } // TestCase 3: isDeprecatedLicenseId is not a valid boolean parser, _ = parserFromBodyContent(` yes `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("expected function to raise an error stating isDeprecatedLicenseId should be a valid boolean type") } // TestCase 4: isFsfLibre is not a valid boolean parser, _ = parserFromBodyContent(` no `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("expected function to raise an error stating isFsfLibre should be a valid boolean type") } // TestCase 5: invalid triple for License: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("invalid predicate should've raised an error, got ") } // TestCase 5: everything valid: parser, _ = parserFromBodyContent(` http://www.opensource.org/licenses/GPL-3.0 true GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 GNU General Public License v3.0 or later ... GPL-3.0-or-later This license was released: 29 June 2007 true ... .... `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err != nil { t.Errorf("error parsing a valid input: %v", err) } expectedSeeAlso := "http://www.opensource.org/licenses/GPL-3.0" if len(license.seeAlso) != 1 { t.Fatalf("expected seeAlso to have 1 element, got %d", len(license.seeAlso)) } if license.seeAlso[len(license.seeAlso)-1] != expectedSeeAlso { t.Errorf("expected %s, got %s", expectedSeeAlso, license.seeAlso) } if license.isOsiApproved != true { t.Errorf("expected %t, got %t", true, license.isOsiApproved) } expectedLicenseText := "GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007" if license.licenseText != expectedLicenseText { t.Errorf("expected %s, got %s", expectedSeeAlso, license.licenseText) } expectedName := "GNU General Public License v3.0 or later" if license.name != expectedName { t.Errorf("expected %s, got %s", expectedName, license.name) } expectedstdLicHeader := "..." if license.standardLicenseHeader != expectedstdLicHeader { t.Errorf("expected %s, got %s", expectedstdLicHeader, license.standardLicenseHeader) } expectedLicenseId := "GPL-3.0-or-later" if expectedLicenseId != license.licenseID { t.Errorf("expected %s, got %s", expectedLicenseId, license.licenseID) } expectedLicenseComment := "This license was released: 29 June 2007" if expectedLicenseComment != license.comment { t.Errorf("expected %s, got %s", expectedLicenseComment, license.comment) } expectedstdLicTemplate := "..." if license.standardLicenseHeader != expectedstdLicTemplate { t.Errorf("expected %s, got %s", expectedstdLicTemplate, license.standardLicenseTemplate) } expectedstdLicHeaderTemplate := "..." if license.standardLicenseHeaderTemplate != expectedstdLicHeaderTemplate { t.Errorf("expected %s, got %s", expectedstdLicHeaderTemplate, license.standardLicenseHeaderTemplate) } if license.isFsfLibre != true { t.Errorf("expected %t, got %t", true, license.isFsfLibre) } } func Test_rdfParser2_2_getOrLaterOperatorFromNode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var err error // TestCase 1: more than one member in the OrLaterOperator tag must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getOrLaterOperatorFromNode(node) if err == nil { t.Error("expected an error due to more than one members, got ") } // TestCase 2: Invalid predicate must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getOrLaterOperatorFromNode(node) if err == nil { t.Error("expected an error due to invalid predicate, got ") } // TestCase 5: invalid member parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getOrLaterOperatorFromNode(node) if err == nil { t.Errorf("expected an error parsing invalid license member, got %v", err) } // TestCase 4: valid input parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getOrLaterOperatorFromNode(node) if err != nil { t.Errorf("unexpected error parsing a valid input: %v", err) } } func Test_rdfParser2_2_getSimpleLicensingInfoFromNode(t *testing.T) { // nothing to test. The just provides an interface to call function that // uses triples to render a SimpleLicensingInfo. parser, _ := parserFromBodyContent(` LicenseRef-Freeware freeware `) node := parser.gordfParserObj.Triples[0].Subject _, err := parser.getSimpleLicensingInfoFromNode(node) if err != nil { t.Errorf("error parsing a valid input: %v", err) } } func Test_rdfParser2_2_getSimpleLicensingInfoFromTriples(t *testing.T) { var parser *rdfParser2_2 var err error var license SimpleLicensingInfo // TestCase 1: invalid rdf:seeAlso attribute parser, _ = parserFromBodyContent(` an invalid uri `) _, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples) if err == nil { t.Error("expected an error reporting invalid uri for rdf:seeAlso, got ") } // TestCase 2: invalid predicate must raise an error parser, _ = parserFromBodyContent(` `) _, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples) if err == nil { t.Error("expected an error reporting invalid predicate, got ") } // TestCase 3: valid example parser, _ = parserFromBodyContent(` comment lid name https://opensource.org/licenses/MPL-1.0 example `) license, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedComment := "comment" expectedLicenseId := "lid" expectedName := "name" expectedSeeAlso := "https://opensource.org/licenses/MPL-1.0" expectedExample := "example" if expectedComment != license.comment { t.Errorf("expected %v, got %v", expectedComment, license.comment) } if expectedLicenseId != license.licenseID { t.Errorf("expected %v, got %v", expectedLicenseId, license.licenseID) } if expectedName != license.name { t.Errorf("expected %v, got %v", expectedName, license.name) } if len(license.seeAlso) != 1 { t.Fatalf("expected seeAlso to have 1 element, found %d", len(license.seeAlso)) } if license.seeAlso[0] != expectedSeeAlso { t.Errorf("expected %v, got %v", expectedSeeAlso, license.seeAlso[0]) } if license.example != expectedExample { t.Errorf("expected %v, got %v", expectedExample, license.example) } } func Test_rdfParser2_2_getSpecialLicenseFromNode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var license SpecialLicense // TestCase 1: NONE parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: NS_SPDX + "NONE", } license, err := parser.getSpecialLicenseFromNode(node) if err != nil { t.Errorf("error parsing a valid node: %v", err) } if license.value != "NONE" { t.Errorf("expected %s, got %s", "NONE", license.value) } // TestCase 2: NOASSERTION parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: NS_SPDX + "NOASSERTION", } license, err = parser.getSpecialLicenseFromNode(node) if err != nil { t.Errorf("error parsing a valid node: %v", err) } if license.value != "NOASSERTION" { t.Errorf("expected %s, got %s", "NOASSERTION", license.value) } // TestCase 4: undefined standard license parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "https://opensource.org/licenses/unknown", } _, err = parser.getSpecialLicenseFromNode(node) if err == nil { t.Errorf("expected an error saying invalid license") } // TestCase 4: valid standard license parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "https://opensource.org/licenses/MPL-1.0", } license, err = parser.getSpecialLicenseFromNode(node) if err != nil { t.Errorf("error parsing a valid node: %v", err) } if license.value != "MPL-1.0" { t.Errorf("expected %s, got %s", "MPL-1.0", license.value) } } func Test_rdfParser2_2_getWithExceptionOperatorFromNode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var err error // TestCase 1: more than one member in the OrLaterOperator tag must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err == nil { t.Error("expected an error due to more than one members, got ") } // TestCase 2: Invalid predicate must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err == nil { t.Error("expected an error due to invalid predicate, got ") } // TestCase 3: Invalid member parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err == nil { t.Error("expected an error due to error parsing a member, got ") } // TestCase 4: Invalid licenseException parser, _ = parserFromBodyContent(` example Libtool-exception comment `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err == nil { t.Error("expected an error due to invalid licenseException, got ") } // TestCase 5: valid input parser, _ = parserFromBodyContent(` example Libtool-exception comment `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err != nil { t.Errorf("error parsing a valid input: %v", err) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_other_license_info.go000066400000000000000000000024321463371440000256020ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *rdfParser2_2) getExtractedLicensingInfoFromNode(node *gordfParser.Node) (lic ExtractedLicensingInfo, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) var restTriples []*gordfParser.Triple for _, triple := range associatedTriples { switch triple.Predicate.ID { case SPDX_EXTRACTED_TEXT: lic.extractedText = triple.Object.ID default: restTriples = append(restTriples, triple) } } lic.SimpleLicensingInfo, err = parser.getSimpleLicensingInfoFromTriples(restTriples) if err != nil { return lic, fmt.Errorf("error setting simple licensing information of extracted licensing info: %s", err) } return lic, nil } func (parser *rdfParser2_2) extractedLicenseToOtherLicense(extLicense ExtractedLicensingInfo) (othLic v2_2.OtherLicense) { othLic.LicenseIdentifier = extLicense.licenseID othLic.ExtractedText = extLicense.extractedText othLic.LicenseComment = extLicense.comment othLic.LicenseCrossReferences = extLicense.seeAlso othLic.LicenseName = extLicense.name return othLic } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_other_license_info_test.go000066400000000000000000000147631463371440000266530ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" ) func Test_rdfParser2_2_getExtractedLicensingInfoFromNode(t *testing.T) { var parser *rdfParser2_2 var err error var node *gordfParser.Node // TestCase 1: invalid predicate must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExtractedLicensingInfoFromNode(node) if err == nil { t.Errorf("expected an error saying invalid predicate, got ") } // TestCase 2: valid input parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExtractedLicensingInfoFromNode(node) if err != nil { t.Errorf("unexpected error: %v", err) } } func Test_rdfParser2_2_extractedLicenseToOtherLicense(t *testing.T) { // nothing to test for this function. parser, _ := parserFromBodyContent(` LicenseRef-Freeware freeware `) node := parser.gordfParserObj.Triples[0].Subject extLicense, _ := parser.getExtractedLicensingInfoFromNode(node) othLic := parser.extractedLicenseToOtherLicense(extLicense) if othLic.LicenseIdentifier != extLicense.licenseID { t.Errorf("expected %v, got %v", othLic.LicenseIdentifier, extLicense.licenseID) } if othLic.ExtractedText != extLicense.extractedText { t.Errorf("expected %v, got %v", othLic.ExtractedText, extLicense.extractedText) } if othLic.LicenseComment != extLicense.comment { t.Errorf("expected %v, got %v", othLic.LicenseComment, extLicense.comment) } if !reflect.DeepEqual(othLic.LicenseCrossReferences, extLicense.seeAlso) { t.Errorf("expected %v, got %v", othLic.LicenseCrossReferences, extLicense.seeAlso) } if othLic.LicenseName != extLicense.name { t.Errorf("expected %v, got %v", othLic.LicenseName, extLicense.name) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_package.go000066400000000000000000000265141463371440000233460ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *rdfParser2_2) getPackageFromNode(packageNode *gordfParser.Node) (pkg *v2_2.Package, err error) { pkg = &v2_2.Package{} // new package which will be returned currState := parser.cache[packageNode.ID] if currState == nil { // there is no entry about the state of current package node. // this is the first time we're seeing this node. parser.cache[packageNode.ID] = &nodeState{ object: pkg, Color: WHITE, } } else if currState.Color == GREY { // we have already started parsing this package node and we needn't parse it again. return currState.object.(*v2_2.Package), nil } // setting color of the state to grey to indicate that we've started to // parse this node once. parser.cache[packageNode.ID].Color = GREY // setting state color to black to indicate when we're done parsing this node. defer func() { parser.cache[packageNode.ID].Color = BLACK }() // setting the SPDXIdentifier for the package. eId, err := ExtractElementID(getLastPartOfURI(packageNode.ID)) if err != nil { return nil, fmt.Errorf("error extracting elementID of a package identifier: %v", err) } pkg.PackageSPDXIdentifier = eId // 3.2 // check if we already have a package initialized for this ID existingPackageIndex := -1 for ii, existingPkg := range parser.doc.Packages { if existingPkg != nil && existingPkg.PackageSPDXIdentifier == eId { existingPackageIndex = ii pkg = existingPkg break } } // iterate over all the triples associated with the provided package packageNode. for _, subTriple := range parser.nodeToTriples(packageNode) { switch subTriple.Predicate.ID { case RDF_TYPE: // cardinality: exactly 1 continue case SPDX_NAME: // 3.1 // cardinality: exactly 1 pkg.PackageName = subTriple.Object.ID case SPDX_VERSION_INFO: // 3.3 // cardinality: max 1 pkg.PackageVersion = subTriple.Object.ID case SPDX_PACKAGE_FILE_NAME: // 3.4 // cardinality: max 1 pkg.PackageFileName = subTriple.Object.ID case SPDX_SUPPLIER: // 3.5 // cardinality: max 1 err = setPackageSupplier(pkg, subTriple.Object.ID) case SPDX_ORIGINATOR: // 3.6 // cardinality: max 1 err = setPackageOriginator(pkg, subTriple.Object.ID) case SPDX_DOWNLOAD_LOCATION: // 3.7 // cardinality: exactly 1 err = setDocumentLocationFromURI(pkg, subTriple.Object.ID) case SPDX_FILES_ANALYZED: // 3.8 // cardinality: max 1 err = setFilesAnalyzed(pkg, subTriple.Object.ID) case SPDX_PACKAGE_VERIFICATION_CODE: // 3.9 // cardinality: max 1 err = parser.setPackageVerificationCode(pkg, subTriple.Object) case SPDX_CHECKSUM: // 3.10 // cardinality: min 0 err = parser.setPackageChecksum(pkg, subTriple.Object) case DOAP_HOMEPAGE: // 3.11 // cardinality: max 1 // homepage must be a valid Uri if !isUriValid(subTriple.Object.ID) { return nil, fmt.Errorf("invalid uri %s while parsing doap_homepage in a package", subTriple.Object.ID) } pkg.PackageHomePage = subTriple.Object.ID case SPDX_SOURCE_INFO: // 3.12 // cardinality: max 1 pkg.PackageSourceInfo = subTriple.Object.ID case SPDX_LICENSE_CONCLUDED: // 3.13 // cardinality: exactly 1 anyLicenseInfo, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return nil, err } pkg.PackageLicenseConcluded = anyLicenseInfo.ToLicenseString() case SPDX_LICENSE_INFO_FROM_FILES: // 3.14 // cardinality: min 0 pkg.PackageLicenseInfoFromFiles = append(pkg.PackageLicenseInfoFromFiles, getLicenseStringFromURI(subTriple.Object.ID)) case SPDX_LICENSE_DECLARED: // 3.15 // cardinality: exactly 1 anyLicenseInfo, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return nil, err } pkg.PackageLicenseDeclared = anyLicenseInfo.ToLicenseString() case SPDX_LICENSE_COMMENTS: // 3.16 // cardinality: max 1 pkg.PackageLicenseComments = subTriple.Object.ID case SPDX_COPYRIGHT_TEXT: // 3.17 // cardinality: exactly 1 pkg.PackageCopyrightText = subTriple.Object.ID case SPDX_SUMMARY: // 3.18 // cardinality: max 1 pkg.PackageSummary = subTriple.Object.ID case SPDX_DESCRIPTION: // 3.19 // cardinality: max 1 pkg.PackageDescription = subTriple.Object.ID case RDFS_COMMENT: // 3.20 // cardinality: max 1 pkg.PackageComment = subTriple.Object.ID case SPDX_EXTERNAL_REF: // 3.21 // cardinality: min 0 externalDocRef, err := parser.getPackageExternalRef(subTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing externalRef of a package: %v", err) } pkg.PackageExternalReferences = append(pkg.PackageExternalReferences, externalDocRef) case SPDX_HAS_FILE: // 3.22 // cardinality: min 0 file, err := parser.getFileFromNode(subTriple.Object) if err != nil { return nil, fmt.Errorf("error setting file inside a package: %v", err) } parser.setFileToPackage(pkg, file) case SPDX_RELATIONSHIP: // cardinality: min 0 err = parser.parseRelationship(subTriple) case SPDX_ATTRIBUTION_TEXT: // cardinality: min 0 pkg.PackageAttributionTexts = append(pkg.PackageAttributionTexts, subTriple.Object.ID) case SPDX_ANNOTATION: // cardinality: min 0 err = parser.parseAnnotationFromNode(subTriple.Object) default: return nil, fmt.Errorf("unknown predicate id %s while parsing a package", subTriple.Predicate.ID) } if err != nil { return nil, err } } if existingPackageIndex != -1 { parser.doc.Packages[existingPackageIndex] = pkg } else { parser.doc.Packages = append(parser.doc.Packages, pkg) } return pkg, nil } // parses externalReference found in the package by the associated triple. func (parser *rdfParser2_2) getPackageExternalRef(node *gordfParser.Node) (externalDocRef *v2_2.PackageExternalReference, err error) { externalDocRef = &v2_2.PackageExternalReference{} for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case SPDX_REFERENCE_CATEGORY: // cardinality: exactly 1 switch triple.Object.ID { case SPDX_REFERENCE_CATEGORY_SECURITY: externalDocRef.Category = "SECURITY" case SPDX_REFERENCE_CATEGORY_PACKAGE_MANAGER: externalDocRef.Category = "PACKAGE-MANAGER" case SPDX_REFERENCE_CATEGORY_OTHER: externalDocRef.Category = "OTHER" default: return nil, fmt.Errorf("unknown packageManager uri %s", triple.Predicate.ID) } case RDF_TYPE: continue case SPDX_REFERENCE_TYPE: // assumes: the reference type is associated with just the uri and // other associated fields are ignored. // other fields include: // 1. contextualExample, // 2. documentation and, // 3. externalReferenceSite externalDocRef.RefType = triple.Object.ID case SPDX_REFERENCE_LOCATOR: // cardinality: exactly 1 externalDocRef.Locator = triple.Object.ID case RDFS_COMMENT: // cardinality: max 1 externalDocRef.ExternalRefComment = triple.Object.ID default: return nil, fmt.Errorf("unknown package external reference predicate id %s", triple.Predicate.ID) } } return } func (parser *rdfParser2_2) setPackageVerificationCode(pkg *v2_2.Package, node *gordfParser.Node) error { for _, subTriple := range parser.nodeToTriples(node) { switch subTriple.Predicate.ID { case SPDX_PACKAGE_VERIFICATION_CODE_VALUE: // cardinality: exactly 1 pkg.PackageVerificationCode.Value = subTriple.Object.ID case SPDX_PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE: // cardinality: min 0 pkg.PackageVerificationCode.ExcludedFiles = append(pkg.PackageVerificationCode.ExcludedFiles, subTriple.Object.ID) case RDF_TYPE: // cardinality: exactly 1 continue default: return fmt.Errorf("unparsed predicate %s", subTriple.Predicate.ID) } } return nil } // appends the file to the package and also sets the assocWithPackage for the // file to indicate the file is associated with a package func (parser *rdfParser2_2) setFileToPackage(pkg *v2_2.Package, file *v2_2.File) { if pkg.Files == nil { pkg.Files = []*v2_2.File{} } pkg.Files = append(pkg.Files, file) parser.assocWithPackage[file.FileSPDXIdentifier] = true } // given a supplierObject, sets the PackageSupplier attribute of the pkg. // Args: // // value: [NOASSERTION | [Person | Organization]: string] func setPackageSupplier(pkg *v2_2.Package, value string) error { value = strings.TrimSpace(value) supplier := &common.Supplier{} if strings.ToUpper(value) == "NOASSERTION" { supplier.Supplier = "NOASSERTION" pkg.PackageSupplier = supplier return nil } subKey, subValue, err := ExtractSubs(value, ":") if err != nil { return fmt.Errorf("package supplier must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value) } switch subKey { case "Person", "Organization": supplier.Supplier = subValue supplier.SupplierType = subKey default: return fmt.Errorf("unknown supplier %s", subKey) } pkg.PackageSupplier = supplier return nil } // given a OriginatorObject, sets the PackageOriginator attribute of the pkg. // Args: // // value: [NOASSERTION | [Person | Organization]: string] func setPackageOriginator(pkg *v2_2.Package, value string) error { value = strings.TrimSpace(value) originator := &common.Originator{} if strings.ToUpper(value) == "NOASSERTION" { originator.Originator = "NOASSERTION" pkg.PackageOriginator = originator return nil } subKey, subValue, err := ExtractSubs(value, ":") if err != nil { return fmt.Errorf("package Originator must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value) } switch subKey { case "Person", "Organization": originator.Originator = subValue originator.OriginatorType = subKey default: return fmt.Errorf("unknown Originator %s", subKey) } pkg.PackageOriginator = originator return nil } // validates the uri and sets the location if it is valid func setDocumentLocationFromURI(pkg *v2_2.Package, locationURI string) error { switch locationURI { case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL: pkg.PackageDownloadLocation = "NOASSERTION" case SPDX_NONE_CAPS, SPDX_NONE_SMALL: pkg.PackageDownloadLocation = "NONE" default: if !isUriValid(locationURI) { return fmt.Errorf("%s is not a valid uri", locationURI) } pkg.PackageDownloadLocation = locationURI } return nil } // sets the FilesAnalyzed attribute to the given package // boolValue is a string of type "true" or "false" func setFilesAnalyzed(pkg *v2_2.Package, boolValue string) (err error) { pkg.IsFilesAnalyzedTagPresent = true pkg.FilesAnalyzed, err = boolFromString(boolValue) return err } func (parser *rdfParser2_2) setPackageChecksum(pkg *v2_2.Package, node *gordfParser.Node) error { checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(node) if err != nil { return fmt.Errorf("error getting checksum algorithm and value from %v", node) } if pkg.PackageChecksums == nil { pkg.PackageChecksums = make([]common.Checksum, 0, 1) } switch checksumAlgorithm { case common.SHA1, common.SHA224, common.SHA256, common.SHA384, common.SHA512, common.MD2, common.MD4, common.MD5, common.MD6: pkg.PackageChecksums = append(pkg.PackageChecksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue}) default: return fmt.Errorf("unknown checksumAlgorithm %s while parsing a package", checksumAlgorithm) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_package_test.go000066400000000000000000000666761463371440000244220ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func Test_setPackageSupplier(t *testing.T) { var err error // TestCase 1: no assertion must set PackageSupplierNOASSERTION field to true pkg := &v2_2.Package{} err = setPackageSupplier(pkg, "NOASSERTION") if err != nil { t.Fatalf("unexpected error: %v", err) } if pkg.PackageSupplier.Supplier != "NOASSERTION" { t.Errorf("PackageSupplier must've been set to NOASSERTION") } // TestCase 2: lower-case noassertion must also set the // PackageSupplierNOASSERTION to true. pkg = &v2_2.Package{} err = setPackageSupplier(pkg, "noassertion") if err != nil { t.Fatalf("unexpected error: %v", err) } if pkg.PackageSupplier.Supplier != "NOASSERTION" { t.Errorf("PackageSupplier must've been set to NOASSERTION") } // TestCase 3: invalid input without colon separator. must raise an error pkg = &v2_2.Package{} input := "string without colon separator" err = setPackageSupplier(pkg, input) if err == nil { t.Errorf("invalid input \"%s\" didn't raise an error", input) } // TestCase 4: Valid Person pkg = &v2_2.Package{} personName := "Rishabh Bhatnagar" input = "Person: " + personName err = setPackageSupplier(pkg, input) if err != nil { t.Errorf("unexpected error: %v", err) } if pkg.PackageSupplier.Supplier != personName { t.Errorf("PackageSupplierPerson should be %s. found %s", personName, pkg.PackageSupplier.Supplier) } // TestCase 5: Valid Organization pkg = &v2_2.Package{} orgName := "SPDX" input = "Organization: " + orgName err = setPackageSupplier(pkg, input) if err != nil { t.Errorf("unexpected error: %v", err) } if pkg.PackageSupplier.Supplier != orgName { t.Errorf("PackageSupplierPerson should be %s. found %s", orgName, pkg.PackageSupplier.Supplier) } // TestCase 6: Invalid EntityType pkg = &v2_2.Package{} input = "InvalidEntity: entity" err = setPackageSupplier(pkg, input) if err == nil { t.Errorf("invalid entity should've raised an error") } } func Test_setPackageOriginator(t *testing.T) { var err error // TestCase 1: no assertion must set PackageSupplierNOASSERTION field to true pkg := &v2_2.Package{} err = setPackageOriginator(pkg, "NOASSERTION") if err != nil { t.Fatalf("unexpected error: %v", err) } if pkg.PackageOriginator.Originator != "NOASSERTION" { t.Errorf("PackageOriginator must've been set to NOASSERTION") } // TestCase 2: lower-case noassertion must also set the // PackageOriginatorNOASSERTION to true. pkg = &v2_2.Package{} err = setPackageOriginator(pkg, "noassertion") if err != nil { t.Fatalf("unexpected error: %v", err) } if pkg.PackageOriginator.Originator != "NOASSERTION" { t.Errorf("PackageOriginator must've been set to NOASSERTION") } // TestCase 3: invalid input without colon separator. must raise an error pkg = &v2_2.Package{} input := "string without colon separator" err = setPackageOriginator(pkg, input) if err == nil { t.Errorf("invalid input \"%s\" didn't raise an error", input) } // TestCase 4: Valid Person pkg = &v2_2.Package{} personName := "Rishabh Bhatnagar" input = "Person: " + personName err = setPackageOriginator(pkg, input) if err != nil { t.Errorf("unexpected error: %v", err) } if pkg.PackageOriginator.Originator != personName { t.Errorf("PackageOriginatorPerson should be %s. found %s", personName, pkg.PackageOriginator.Originator) } // TestCase 5: Valid Organization pkg = &v2_2.Package{} orgName := "SPDX" input = "Organization: " + orgName err = setPackageOriginator(pkg, input) if err != nil { t.Errorf("unexpected error: %v", err) } if pkg.PackageOriginator.Originator != orgName { t.Errorf("PackageOriginatorOrganization should be %s. found %s", orgName, pkg.PackageOriginator.Originator) } // TestCase 6: Invalid EntityType pkg = &v2_2.Package{} input = "InvalidEntity: entity" err = setPackageOriginator(pkg, input) if err == nil { t.Errorf("invalid entity should've raised an error") } } func Test_rdfParser2_2_setPackageVerificationCode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var pkg *v2_2.Package var err error // TestCase 1: invalid predicate must raise an error parser, _ = parserFromBodyContent(` cbceb8b5689b75a584efe35587b5d41bd48820ce ./package.spdx `) node = parser.gordfParserObj.Triples[0].Subject pkg = &v2_2.Package{} err = parser.setPackageVerificationCode(pkg, node) if err == nil { t.Errorf("expected an error due to invalid predicate, got ") } // TestCase 2: valid input parser, _ = parserFromBodyContent(` cbceb8b5689b75a584efe35587b5d41bd48820ce ./package.spdx `) node = parser.gordfParserObj.Triples[0].Subject pkg = &v2_2.Package{} err = parser.setPackageVerificationCode(pkg, node) if err != nil { t.Errorf("unexpected error: %v", err) } expectedValue := "cbceb8b5689b75a584efe35587b5d41bd48820ce" if pkg.PackageVerificationCode.Value != expectedValue { t.Errorf("expected %v, got %v", expectedValue, pkg.PackageVerificationCode) } expectedExcludedFile := "./package.spdx" if pkg.PackageVerificationCode.ExcludedFiles[0] != expectedExcludedFile { t.Errorf("expected %v, got %v", expectedExcludedFile, pkg.PackageVerificationCode.ExcludedFiles) } } func Test_rdfParser2_2_getPackageExternalRef(t *testing.T) { var extRef *v2_2.PackageExternalReference var err error var parser *rdfParser2_2 var node *gordfParser.Node // TestCase 1: invalid reference category parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err == nil { t.Errorf("expected an error due to invalid referenceCategory, got ") } // TestCase 2: invalid predicate parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err == nil { t.Errorf("expected an error due to invalid referenceCategory, got ") } // TestCase 3: valid example (referenceCategory_security) parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* comment `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err != nil { t.Fatalf("unexpected error parsing a valid example: %v", err) } expectedExtRef := &v2_2.PackageExternalReference{ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", RefType: "http://spdx.org/rdf/references/cpe23Type", Category: "SECURITY", ExternalRefComment: "comment", } if !reflect.DeepEqual(extRef, expectedExtRef) { t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef) } // TestCase 4: valid example (referenceCategory_packageManager) parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* comment `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err != nil { t.Fatalf("unexpected error parsing a valid example: %v", err) } expectedExtRef = &v2_2.PackageExternalReference{ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", RefType: "http://spdx.org/rdf/references/cpe23Type", Category: "PACKAGE-MANAGER", ExternalRefComment: "comment", } if !reflect.DeepEqual(extRef, expectedExtRef) { t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef) } // TestCase 5: valid example (referenceCategory_other) parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* comment `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err != nil { t.Fatalf("unexpected error parsing a valid example: %v", err) } expectedExtRef = &v2_2.PackageExternalReference{ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", RefType: "http://spdx.org/rdf/references/cpe23Type", Category: "OTHER", ExternalRefComment: "comment", } if !reflect.DeepEqual(extRef, expectedExtRef) { t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef) } } func Test_rdfParser2_2_getPackageFromNode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var err error // TestCase 1: invalid elementId parser, _ = parserFromBodyContent(` time-1.9.tar.gz `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(missing SPDXRef- prefix), found %v", err) } // TestCase 2: Invalid License Concluded must raise an error: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid license), found %v", err) } // TestCase 2: Invalid License Declared must raise an error: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid license), found %v", err) } // TestCase 3: Invalid ExternalRef parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid externalRef), found %v", err) } // TestCase 4: invalid file must raise an error parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid file), found %v", err) } // TestCase 5: invalid predicate must raise an error parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid predicate), found %v", err) } // TestCase 6: invalid annotation must raise an error parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid annotation), found %v", err) } // TestCase 6: invalid homepage must raise an error parser, _ = parserFromBodyContent(` u r i `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid homepage uri), found %v", err) } // TestCase 7: Package tag declared more than once should be parsed into a single object's definition parser, _ = parserFromBodyContent(` Test Package `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err != nil { t.Errorf("error parsing a valid package: %v", err) } yetAnotherPkgTriple := gordfParser.Triple{ Subject: node, Predicate: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: SPDX_PACKAGE_FILE_NAME, }, Object: &gordfParser.Node{ NodeType: gordfParser.LITERAL, ID: "packageFileName", }, } parser.nodeStringToTriples[node.String()] = append(parser.nodeStringToTriples[node.String()], &yetAnotherPkgTriple) pkg, err := parser.getPackageFromNode(node) if err != nil { t.Errorf("error parsing a valid package: %v", err) } // validating if all the attributes that spanned over two tags are included in the parsed package. expectedID := "upload2" if string(pkg.PackageSPDXIdentifier) != expectedID { t.Errorf("expected package id: %s, got %s", expectedID, pkg.PackageSPDXIdentifier) } expectedPkgFileName := "packageFileName" if expectedPkgFileName != pkg.PackageFileName { t.Errorf("expected package file name: %s, got %s", expectedPkgFileName, pkg.PackageFileName) } expectedName := "Test Package" if pkg.PackageName != expectedName { t.Errorf("expected package name: %s, got %s", expectedPkgFileName, pkg.PackageName) } // TestCase 8: Checking if packages can handle cyclic dependencies: // Simulating a smallest possible cycle: package related to itself. parser, _ = parserFromBodyContent(` Test Package 1.1.1 `) node = parser.gordfParserObj.Triples[0].Subject pkg, err = parser.getPackageFromNode(node) if err != nil { t.Errorf("error parsing a valid package: %v", err) } // checking if both the attributes of the packages are set. expectedVersionInfo := "1.1.1" expectedPackageName := "Test Package" if pkg.PackageVersion != expectedVersionInfo { t.Errorf("Expected %s, found %s", expectedVersionInfo, pkg.PackageVersion) } if pkg.PackageName != expectedPackageName { t.Errorf("Expected %s, found %s", expectedPackageName, pkg.PackageName) } // TestCase 9: everything valid parser, _ = parserFromBodyContent(` Test Package 1.1.1 time-1.9.tar.gz Person: Jane Doe (jane.doe@example.com) Organization: SPDX true cbceb8b5689b75a584efe35587b5d41bd48820ce ./package.spdx 75068c26abbed3ad3980685bae21d7202d288317 http://www.openjena.org/ uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. Other versions available for a commercial license Package for Testing Some tags are taken from other spdx autogenerated files no comments cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* attribution text 2011-01-29T18:30:22Z Package level annotation Person: Package Commenter `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err != nil { t.Errorf("error parsing a valid package: %v", err) } } func Test_rdfParser2_2_setFileToPackage(t *testing.T) { var pkg *v2_2.Package var file *v2_2.File var parser *rdfParser2_2 // TestCase 1: setting to a nil files attribute shouldn't panic. parser, _ = parserFromBodyContent(``) pkg = &v2_2.Package{} file = &v2_2.File{} parser.setFileToPackage(pkg, file) if len(pkg.Files) != 1 { t.Errorf("expected given package to have one file after setting, got %d", len(pkg.Files)) } if parser.assocWithPackage[file.FileSPDXIdentifier] != true { t.Errorf("given file should've been associated with a package, assocWithPackage is false") } } func Test_rdfParser2_2_setPackageChecksum(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var pkg *v2_2.Package var expectedChecksumValue string var err error // TestCase 1: invalid checksum algorithm parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &v2_2.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err == nil { t.Error("expected an error due to invalid checksum node, got ") } // TestCase 1: valid checksum algorithm which is invalid for package parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &v2_2.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err == nil { t.Error("expected an error due to invalid checksum for package, got ") } // TestCase 2: valid checksum (sha1) parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &v2_2.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err != nil { t.Errorf("unexpected error: %v", err) } expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" for _, checksum := range pkg.PackageChecksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != expectedChecksumValue { t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value) } } } // TestCase 3: valid checksum (sha256) parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &v2_2.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err != nil { t.Errorf("unexpected error: %v", err) } expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" for _, checksum := range pkg.PackageChecksums { switch checksum.Algorithm { case common.SHA256: if checksum.Value != expectedChecksumValue { t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value) } } } // TestCase 4: valid checksum (md5) parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &v2_2.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err != nil { t.Errorf("unexpected error: %v", err) } expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" for _, checksum := range pkg.PackageChecksums { switch checksum.Algorithm { case common.MD5: if checksum.Value != expectedChecksumValue { t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value) } } } } func Test_setDocumentLocationFromURI(t *testing.T) { var pkg *v2_2.Package var expectedDocumentLocation, gotDocumentLocation string var inputURI string var err error // TestCase 1: NOASSERTION inputURI = SPDX_NOASSERTION_SMALL pkg = &v2_2.Package{} err = setDocumentLocationFromURI(pkg, inputURI) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedDocumentLocation = "NOASSERTION" gotDocumentLocation = pkg.PackageDownloadLocation if expectedDocumentLocation != gotDocumentLocation { t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation) } // TestCase 2: NONE inputURI = SPDX_NONE_CAPS pkg = &v2_2.Package{} err = setDocumentLocationFromURI(pkg, inputURI) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedDocumentLocation = "NONE" gotDocumentLocation = pkg.PackageDownloadLocation if expectedDocumentLocation != gotDocumentLocation { t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation) } // TestCase 3: valid uri inputURI = "https://www.gnu.org/software/texinfo/" pkg = &v2_2.Package{} err = setDocumentLocationFromURI(pkg, inputURI) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedDocumentLocation = "https://www.gnu.org/software/texinfo/" gotDocumentLocation = pkg.PackageDownloadLocation if expectedDocumentLocation != gotDocumentLocation { t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation) } // TestCase 3: invalid uri inputURI = " " pkg = &v2_2.Package{} err = setDocumentLocationFromURI(pkg, inputURI) if err == nil { t.Fatalf("expected an error due to invalid uri, got %v", err) } } func Test_setFilesAnalyzed(t *testing.T) { var pkg *v2_2.Package var err error // TestCase 1: not a valid bool value: pkg = &v2_2.Package{} err = setFilesAnalyzed(pkg, "no") if err == nil { t.Errorf("expected an error due to invalid bool input, got %v", err) } // TestCase 2: valid input pkg = &v2_2.Package{} err = setFilesAnalyzed(pkg, "true") if err != nil { t.Fatalf("unexpected error: %v", err) } if !pkg.IsFilesAnalyzedTagPresent { t.Errorf("should've set IsFilesAnalyzedTagPresent, got: %t", pkg.IsFilesAnalyzedTagPresent) } if !pkg.FilesAnalyzed { t.Errorf("expected: %t, got: %t", true, pkg.FilesAnalyzed) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_relationship.go000066400000000000000000000115321463371440000244460ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // parsing the relationship that exists in the rdf document. // Relationship is of type RefA relationType RefB. // parsing the relationship appends the relationship to the current document's // Relationships Slice. func (parser *rdfParser2_2) parseRelationship(triple *gordfParser.Triple) (err error) { reln := v2_2.Relationship{} reln.RefA, err = getReferenceFromURI(triple.Subject.ID) if err != nil { return err } currState := parser.cache[triple.Object.ID] if currState == nil { // there is no entry about the state of current package node. // this is the first time we're seeing this node. parser.cache[triple.Object.ID] = &nodeState{ object: reln, Color: WHITE, } } else if currState.Color == GREY { // we have already started parsing this relationship node and we needn't parse it again. return nil } // setting color of the state to grey to indicate that we've started to // parse this node once. parser.cache[triple.Object.ID].Color = GREY // setting state color to black to indicate when we're done parsing this node. defer func() { parser.cache[triple.Object.ID].Color = BLACK }() for _, subTriple := range parser.nodeToTriples(triple.Object) { switch subTriple.Predicate.ID { case SPDX_RELATIONSHIP_TYPE: // cardinality: exactly 1 reln.Relationship, err = getRelationshipTypeFromURI(subTriple.Object.ID) case RDF_TYPE: // cardinality: exactly 1 continue case SPDX_RELATED_SPDX_ELEMENT: // cardinality: exactly 1 // assumes: spdx-element is a uri reln.RefB, err = getReferenceFromURI(subTriple.Object.ID) if err != nil { return err } relatedSpdxElementTriples := parser.nodeToTriples(subTriple.Object) if len(relatedSpdxElementTriples) == 0 { continue } typeTriples := rdfwriter.FilterTriples(relatedSpdxElementTriples, &subTriple.Object.ID, &RDF_TYPE, nil) if len(typeTriples) != 1 { return fmt.Errorf("expected %s to have exactly one rdf:type triple. found %d triples", subTriple.Object, len(typeTriples)) } err = parser.parseRelatedElementFromTriple(&reln, typeTriples[0]) if err != nil { return err } case RDFS_COMMENT: // cardinality: max 1 reln.RelationshipComment = subTriple.Object.ID default: return fmt.Errorf("unexpected predicate id: %s", subTriple.Predicate.ID) } if err != nil { return err } } parser.doc.Relationships = append(parser.doc.Relationships, &reln) return nil } func (parser *rdfParser2_2) parseRelatedElementFromTriple(reln *v2_2.Relationship, triple *gordfParser.Triple) error { // iterate over relatedElement Type and check which SpdxElement it is. var err error switch triple.Object.ID { case SPDX_FILE: file, err := parser.getFileFromNode(triple.Subject) if err != nil { return fmt.Errorf("error setting a file: %v", err) } reln.RefB = common.DocElementID{ DocumentRefID: "", ElementRefID: file.FileSPDXIdentifier, } case SPDX_PACKAGE: pkg, err := parser.getPackageFromNode(triple.Subject) if err != nil { return fmt.Errorf("error setting a package inside a relationship: %v", err) } reln.RefB = common.DocElementID{ DocumentRefID: "", ElementRefID: pkg.PackageSPDXIdentifier, } case SPDX_SPDX_ELEMENT: // it shouldn't be associated with any other triple. // it must be a uri reference. reln.RefB, err = ExtractDocElementID(getLastPartOfURI(triple.Subject.ID)) if err != nil { return err } default: return fmt.Errorf("undefined relatedElement %s found while parsing relationship", triple.Object.ID) } return nil } // references like RefA and RefB of any relationship func getReferenceFromURI(uri string) (common.DocElementID, error) { fragment := getLastPartOfURI(uri) switch strings.ToLower(strings.TrimSpace(fragment)) { case "noassertion", "none": return common.DocElementID{ DocumentRefID: "", ElementRefID: common.ElementID(strings.ToUpper(fragment)), }, nil } return ExtractDocElementID(fragment) } // note: relationshipType is case sensitive. func getRelationshipTypeFromURI(relnTypeURI string) (string, error) { relnTypeURI = strings.TrimSpace(relnTypeURI) lastPart := getLastPartOfURI(relnTypeURI) if !strings.HasPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE) { return "", fmt.Errorf("relationshipType must start with %s. found %s", PREFIX_RELATIONSHIP_TYPE, lastPart) } lastPart = strings.TrimPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE) lastPart = strings.TrimSpace(lastPart) for _, validRelationshipType := range AllRelationshipTypes() { if lastPart == validRelationshipType { return lastPart, nil } } return "", fmt.Errorf("unknown relationshipType: '%s'", lastPart) } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_relationship_test.go000066400000000000000000000345051463371440000255120ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func Test_getReferenceFromURI(t *testing.T) { // TestCase 1: noassertion uri ref, err := getReferenceFromURI(SPDX_NOASSERTION_CAPS) if err != nil { t.Errorf("unexpected error: %v", err) } if ref.DocumentRefID != "" { t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID) } if ref.ElementRefID != "NOASSERTION" { t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "NOASSERTION") } // TestCase 2: NONE uri ref, err = getReferenceFromURI(SPDX_NONE_CAPS) if err != nil { t.Errorf("unexpected error: %v", err) } if ref.DocumentRefID != "" { t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID) } if ref.ElementRefID != "NONE" { t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "NONE") } // TestCase 3: Valid URI ref, err = getReferenceFromURI(NS_SPDX + "SPDXRef-item1") if err != nil { t.Errorf("unexpected error: %v", err) } if ref.DocumentRefID != "" { t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID) } if ref.ElementRefID != "item1" { t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "item1") } // TestCase 3: Invalid URI _, err = getReferenceFromURI(NS_SPDX + "item1") if err == nil { t.Errorf("should've raised an error for invalid input") } } func Test_getRelationshipTypeFromURI(t *testing.T) { // TestCase 1: valid relationshipType relnType := "expandedFromArchive" op, err := getRelationshipTypeFromURI(NS_SPDX + "relationshipType_" + relnType) if err != nil { t.Errorf("error getting relationship type from a valid input") } if op != relnType { t.Errorf("expected %s, found %s", relnType, op) } // TestCase2: invalid relationshipType relnType = "invalidRelationship" _, err = getRelationshipTypeFromURI(NS_SPDX + "relationshipType_" + relnType) if err == nil { t.Errorf("should've raised an error for an invalid input(%s)", relnType) } } func Test_rdfParser2_2_parseRelatedElementFromTriple(t *testing.T) { // TestCase 1: Package as a related element parser, _ := parserFromBodyContent(` `) reln := &v2_2.Relationship{} triple := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE)[0] err := parser.parseRelatedElementFromTriple(reln, triple) if err != nil { t.Errorf("error parsing a valid example") } expectedRefA := common.DocElementID{ DocumentRefID: "", ElementRefID: "", } if !reflect.DeepEqual(expectedRefA, reln.RefA) { t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA) } expectedRefB := common.DocElementID{ DocumentRefID: "", ElementRefID: "Saxon", } if !reflect.DeepEqual(expectedRefB, reln.RefB) { t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB) } // TestCase 3: invalid package as a relatedElement parser, _ = parserFromBodyContent(` `) reln = &v2_2.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err == nil { t.Errorf("expected an error due to invalid Package id, got %v", err) } // TestCase 4: valid File as a related element parser, _ = parserFromBodyContent(` `) reln = &v2_2.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err != nil { t.Errorf("error parsing a valid example") } expectedRefA = common.DocElementID{ DocumentRefID: "", ElementRefID: "", } if !reflect.DeepEqual(expectedRefA, reln.RefA) { t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA) } expectedRefB = common.DocElementID{ DocumentRefID: "", ElementRefID: "Saxon", } if !reflect.DeepEqual(expectedRefB, reln.RefB) { t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB) } // TestCase 5: invalid File as a relatedElement parser, _ = parserFromBodyContent(` `) reln = &v2_2.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err == nil { t.Errorf("expected an error while parsing an invalid File, got %v", err) } // TestCase 6: valid SpdxElement as a related element parser, _ = parserFromBodyContent(` `) reln = &v2_2.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_SPDX_ELEMENT)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err != nil { t.Errorf("error parsing a valid example") } expectedRefA = common.DocElementID{ DocumentRefID: "", ElementRefID: "", } if !reflect.DeepEqual(expectedRefA, reln.RefA) { t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA) } expectedRefB = common.DocElementID{ DocumentRefID: "", ElementRefID: "File", } if !reflect.DeepEqual(expectedRefB, reln.RefB) { t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB) } // TestCase 7: invalid SpdxElement as a related element parser, _ = parserFromBodyContent(` `) reln = &v2_2.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_SPDX_ELEMENT)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err == nil { t.Errorf("expected an error due to invalid documentId for SpdxElement, got %v", err) } } func Test_rdfParser2_2_parseRelationship(t *testing.T) { // TestCase 1: invalid RefA parser, _ := parserFromBodyContent(` `) triple := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err := parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to invalid RefA") } // TestCase 3: invalid RefB parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to invalid RefB") } // TestCase 3: more than one typeTriple for relatedElement parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to more than one type triples") } // TestCase 4: undefined relatedSpdxElement parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to unknown relatedElement, got %v", err) } // TestCase 6: relatedElement associated with more than one type parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("expected an error due to invalid relatedElement, got %v", err) } // TestCase 5: unknown predicate inside a relationship parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to unknown predicate in a relationship") } // TestCase 8: Recursive relationships mustn't raise any error: parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err != nil { t.Errorf("error parsing a valid example") } // TestCase 7: completely valid example: parser, _ = parserFromBodyContent(` comment `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err != nil { t.Errorf("unexpected error parsing a valid relationship: %v", err) } // validating parsed attributes if len(parser.doc.Relationships) != 1 { t.Errorf("after parsing a valid relationship, doc should've had 1 relationship, found %d", len(parser.doc.Relationships)) } reln := parser.doc.Relationships[0] expectedRelnType := "describes" if reln.Relationship != expectedRelnType { t.Errorf("expected %s, found %s", expectedRelnType, reln.Relationship) } expectedRefA := common.DocElementID{ DocumentRefID: "", ElementRefID: "File", } if !reflect.DeepEqual(expectedRefA, reln.RefA) { t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA) } expectedRefB := common.DocElementID{ DocumentRefID: "", ElementRefID: "Saxon", } if !reflect.DeepEqual(expectedRefB, reln.RefB) { t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB) } expectedComment := "comment" if reln.RelationshipComment != expectedComment { t.Errorf("expected %v, found %v", expectedComment, reln.RelationshipComment) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_review.go000066400000000000000000000020161463371440000232430ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *rdfParser2_2) setReviewFromNode(reviewedNode *gordfParser.Node) error { review := v2_2.Review{} for _, triple := range parser.nodeToTriples(reviewedNode) { switch triple.Predicate.ID { case RDF_TYPE: // cardinality: exactly 1 continue case RDFS_COMMENT: // cardinality: max 1 review.ReviewComment = triple.Object.ID case SPDX_REVIEW_DATE: // cardinality: exactly 1 review.ReviewDate = triple.Object.ID case SPDX_REVIEWER: // cardinality: max 1 var err error review.ReviewerType, review.Reviewer, err = ExtractSubs(triple.Object.ID, ":") if err != nil { return fmt.Errorf("error parsing reviewer: %v", err) } default: return fmt.Errorf("unknown predicate %v for review triples", triple.Predicate) } } parser.doc.Reviews = append(parser.doc.Reviews, &review) return nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_review_test.go000066400000000000000000000044411463371440000243060ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" ) func Test_rdfParser2_2_setReviewFromNode(t *testing.T) { // TestCase 1: unknown predicate must raise an error parser, _ := parserFromBodyContent(` Another example reviewer. 2011-03-13T00:00:00Z Person: Suzanne Reviewer `) reviewNode := parser.gordfParserObj.Triples[0].Subject err := parser.setReviewFromNode(reviewNode) if err == nil { t.Errorf("unknown predicate should've elicit an error") } // TestCase 2: wrong reviewer format must raise an error parser, _ = parserFromBodyContent(` Another example reviewer. 2011-03-13T00:00:00Z Suzanne Reviewer `) reviewNode = parser.gordfParserObj.Triples[0].Subject err = parser.setReviewFromNode(reviewNode) if err == nil { t.Errorf("incorrect should've elicit an error") } // TestCase 3: valid input parser, _ = parserFromBodyContent(` Another example reviewer. 2011-03-13T00:00:00Z Person: Suzanne `) reviewNode = parser.gordfParserObj.Triples[0].Subject err = parser.setReviewFromNode(reviewNode) if err != nil { t.Errorf("error parsing a valid node") } n := len(parser.doc.Reviews) if n != 1 { t.Errorf("expected doc to have 1 review, found %d", n) } review := parser.doc.Reviews[0] expectedComment := "Another example reviewer." if review.ReviewComment != expectedComment { t.Errorf("expected: %v, found: %s", expectedComment, review.ReviewComment) } expectedDate := "2011-03-13T00:00:00Z" if review.ReviewDate != expectedDate { t.Errorf("expected %s, found %s", expectedDate, review.ReviewDate) } expectedReviewer := "Suzanne" if review.Reviewer != expectedReviewer { t.Errorf("expected %s, found %s", expectedReviewer, review.Reviewer) } expectedReviewerType := "Person" if review.ReviewerType != expectedReviewerType { t.Errorf("expected %s, found %s", expectedReviewerType, review.ReviewerType) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_snippet_info.go000066400000000000000000000156361463371440000244530ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strconv" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // Snippet Information // Cardinality: Optional, Many func (parser *rdfParser2_2) getSnippetInformationFromNode2_2(node *gordfParser.Node) (si *v2_2.Snippet, err error) { si = &v2_2.Snippet{} err = setSnippetID(node.ID, si) if err != nil { return nil, err } for _, siTriple := range parser.nodeToTriples(node) { switch siTriple.Predicate.ID { case RDF_TYPE: // cardinality: exactly 1 case SPDX_SNIPPET_FROM_FILE: // cardinality: exactly 1 // file which is associated with the snippet _, err := parser.getFileFromNode(siTriple.Object) if err != nil { return nil, err } docElemID, err := ExtractDocElementID(getLastPartOfURI(siTriple.Object.ID)) if err != nil { return nil, err } si.SnippetFromFileSPDXIdentifier = docElemID.ElementRefID case SPDX_RANGE: // cardinality: min 1 err = parser.setSnippetRangeFromNode(siTriple.Object, si) if err != nil { return nil, err } case SPDX_LICENSE_INFO_IN_SNIPPET: // license info in snippet can be NONE, NOASSERTION or SimpleLicensingInfo // using AnyLicenseInfo because it can redirect the request and // can handle NONE & NOASSERTION var anyLicense AnyLicenseInfo anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing license info in snippet: %v", err) } si.LicenseInfoInSnippet = append(si.LicenseInfoInSnippet, anyLicense.ToLicenseString()) case SPDX_NAME: si.SnippetName = siTriple.Object.ID case SPDX_COPYRIGHT_TEXT: si.SnippetCopyrightText = siTriple.Object.ID case SPDX_LICENSE_COMMENTS: si.SnippetLicenseComments = siTriple.Object.ID case RDFS_COMMENT: si.SnippetComment = siTriple.Object.ID case SPDX_LICENSE_CONCLUDED: var anyLicense AnyLicenseInfo anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing license info in snippet: %v", err) } si.SnippetLicenseConcluded = anyLicense.ToLicenseString() default: return nil, fmt.Errorf("unknown predicate %v", siTriple.Predicate.ID) } } return si, nil } // given is the id of the file, sets the snippet to the file in parser. func (parser *rdfParser2_2) setSnippetToFileWithID(snippet *v2_2.Snippet, fileID common.ElementID) error { if parser.files[fileID] == nil { return fmt.Errorf("snippet refers to an undefined file with ID: %s", fileID) } // initializing snippet of the files if it is not defined already if parser.files[fileID].Snippets == nil { parser.files[fileID].Snippets = map[common.ElementID]*v2_2.Snippet{} } // setting the snippet to the file. parser.files[fileID].Snippets[snippet.SnippetSPDXIdentifier] = snippet return nil } func (parser *rdfParser2_2) setSnippetRangeFromNode(node *gordfParser.Node, si *v2_2.Snippet) error { // for a range object, we can have only 3 associated triples: // node -> RDF_TYPE -> Object // node -> startPointer -> Object // node -> endPointer -> Object associatedTriples := parser.nodeToTriples(node) if len(associatedTriples) != 3 { return fmt.Errorf("range should be associated with exactly 3 triples, got %d", len(associatedTriples)) } // Triple 1: Predicate=RDF_TYPE typeTriple := rdfwriter.FilterTriples(associatedTriples, &node.ID, &RDF_TYPE, nil) if len(typeTriple) != 1 { // we had 3 associated triples. out of which 2 is start and end pointer, // if we do not have the rdf:type triple as the third one, // we have either extra or undefined predicate. return fmt.Errorf("every object node must be associated with exactly one rdf:type triple, found: %d", len(typeTriple)) } // getting start pointer startPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_START_POINTER, nil) if len(startPointerTriples) != 1 { return fmt.Errorf("range object must be associated with exactly 1 startPointer, got %d", len(startPointerTriples)) } startRangeType, start, err := parser.getPointerFromNode(startPointerTriples[0].Object, si) if err != nil { return fmt.Errorf("error parsing startPointer: %v", err) } // getting end pointer endPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_END_POINTER, nil) if len(startPointerTriples) != 1 { return fmt.Errorf("range object must be associated with exactly 1 endPointer, got %d", len(endPointerTriples)) } endRangeType, end, err := parser.getPointerFromNode(endPointerTriples[0].Object, si) if err != nil { return fmt.Errorf("error parsing endPointer: %v", err) } // return error when start and end pointer type is not same. if startRangeType != endRangeType { return fmt.Errorf("start and end range type doesn't match") } si.Ranges = []common.SnippetRange{{ StartPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier}, EndPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier}, }} if startRangeType == LINE_RANGE { si.Ranges[0].StartPointer.LineNumber = start si.Ranges[0].EndPointer.LineNumber = end } else { si.Ranges[0].StartPointer.Offset = start si.Ranges[0].EndPointer.Offset = end } return nil } func (parser *rdfParser2_2) getPointerFromNode(node *gordfParser.Node, si *v2_2.Snippet) (rt RangeType, number int, err error) { for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case RDF_TYPE: case PTR_REFERENCE: err = parser.parseRangeReference(triple.Object, si) case PTR_OFFSET: number, err = strconv.Atoi(triple.Object.ID) rt = BYTE_RANGE case PTR_LINE_NUMBER: number, err = strconv.Atoi(triple.Object.ID) rt = LINE_RANGE default: err = fmt.Errorf("undefined predicate (%s) for a pointer", triple.Predicate) } if err != nil { return } } if rt == "" { err = fmt.Errorf("range type not defined for a pointer") } return } func (parser *rdfParser2_2) parseRangeReference(node *gordfParser.Node, snippet *v2_2.Snippet) error { // reference is supposed to be either a resource reference to an already // defined or a new file. Unfortunately, I didn't find field where this can be set in the tools-golang data model. // todo: set this reference to the snippet associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) if len(associatedTriples) == 0 { return nil } _, err := parser.getFileFromNode(node) if err != nil { return fmt.Errorf("error parsing a new file in a reference: %v", err) } return nil } func setSnippetID(uri string, si *v2_2.Snippet) (err error) { fragment := getLastPartOfURI(uri) si.SnippetSPDXIdentifier, err = ExtractElementID(fragment) if err != nil { return fmt.Errorf("error setting snippet identifier: %v", uri) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_snippet_info_test.go000066400000000000000000000441001463371440000254760ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func Test_rdfParser2_2_getSnippetInformationFromTriple2_2(t *testing.T) { var err error var parser *rdfParser2_2 var node *gordfParser.Node // TestCase 1: invalid snippet id: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_2(node) if err == nil { t.Errorf("expected an error due to invalid, got %v", err) } // TestCase 2: Invalid LicenseInfoInSnippet parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_2(node) if err == nil { t.Errorf("expected an error due to invalid licenseInfoInSnippet, got %v", err) } // TestCase 3: Invalid range. parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_2(node) if err == nil { t.Errorf("expected an error due to invalid range, got %v", err) } // TestCase 3: invalid file in snippetFromFile parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_2(node) if err == nil { t.Errorf("expected an error due to invalid snippetFromFile, got %v", err) } // TestCase 4: unknown predicate parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_2(node) if err == nil { t.Errorf("expected an error due to invalid predicate, got %v", err) } // TestCase 5: invalid license concluded: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_2(node) if err == nil { t.Errorf("expected an error due to invalid licenseConcluded, got %v", err) } // TestCase 6: everything valid: parser, _ = parserFromBodyContent(` 420 310 snippet test test comments comments `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_2(node) if err != nil { t.Fatalf("error parsing a valid example: %v", err) } } func Test_setSnippetID(t *testing.T) { // TestCase 1: invalid input (empty) err := setSnippetID("", &v2_2.Snippet{}) if err == nil { t.Errorf("should've raised an error for empty input") } // TestCase 2: valid input si := &v2_2.Snippet{} err = setSnippetID("http://spdx.org/spdxdocs/spdx-example#SPDXRef-Snippet", si) if err != nil { t.Errorf("unexpected error: %v", err) } if si.SnippetSPDXIdentifier != "Snippet" { t.Errorf("expected: %s, found: %s", "Snippet", si.SnippetSPDXIdentifier) } } func Test_rdfParser2_2_parseRangeReference(t *testing.T) { var err error var node *gordfParser.Node var parser *rdfParser2_2 var si *v2_2.Snippet // TestCase 1: ResourceLiteral node without a new file shouldn't raise any error. si = &v2_2.Snippet{} parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.RESOURCELITERAL, ID: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource", } err = parser.parseRangeReference(node, si) if err != nil { t.Errorf("error parsing a valid node: %v", err) } // TestCase 2: invalid file in the reference should raise an error si = &v2_2.Snippet{} parser, _ = parserFromBodyContent(` test file `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseRangeReference(node, si) if err == nil { t.Errorf("expected an error due to invalid file in the range reference, got %v", err) } // TestCase 3: A valid reference must set the file to the files map of the parser. si = &v2_2.Snippet{} parser, _ = parserFromBodyContent(` test file `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseRangeReference(node, si) if err != nil { t.Errorf("error parsing a valid input: %v", err) } if len(parser.files) != 1 { t.Errorf("expected parser.files to have 1 file, found %d", len(parser.files)) } } func Test_rdfParser2_2_getPointerFromNode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var si *v2_2.Snippet var err error var rt RangeType var number int // TestCase 1: invalid number in the offset field must raise an error. parser, _ = parserFromBodyContent(` 3-10 `) node = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getPointerFromNode(node, si) if err == nil { t.Errorf("should've raised an error parsing invalid offset, got %v", err) } // TestCase 2: invalid number in the lineNumber field must raise an error. parser, _ = parserFromBodyContent(` 3-10 `) node = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getPointerFromNode(node, si) if err == nil { t.Errorf("should've raised an error parsing invalid offset, got %v", err) } // TestCase 3: invalid predicate in the pointer field parser, _ = parserFromBodyContent(` 3-10 `) node = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getPointerFromNode(node, si) if err == nil { t.Errorf("should've raised an error parsing invalid predicate, got %v", err) } // TestCase 4: No range type defined must also raise an error parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getPointerFromNode(node, si) if err == nil { t.Errorf("should've raised an error parsing invalid rangeType, got %v", err) } // TestCase 5: valid example parser, _ = parserFromBodyContent(` 310 `) node = parser.gordfParserObj.Triples[0].Subject rt, number, err = parser.getPointerFromNode(node, si) if err != nil { t.Fatalf("unexpected error parsing a valid node: %v", err) } if rt != BYTE_RANGE { t.Errorf("expected: %s, got: %s", BYTE_RANGE, rt) } if number != 310 { t.Errorf("expected: %d, got: %d", 310, number) } } func Test_rdfParser2_2_setSnippetRangeFromNode(t *testing.T) { var parser *rdfParser2_2 var err error var si *v2_2.Snippet var node *gordfParser.Node // TestCase 1: range with less one pointer less must raise an error // (end-pointer missing in the range) parser, _ = parserFromBodyContent(` 310 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to missing end pointer, got %v", err) } // TestCase 2: triples with 0 or more than one type-triple parser, _ = parserFromBodyContent(` 420 310 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject dummyTriple := parser.gordfParserObj.Triples[0] // resetting the node to be associated with 3 triples which will have // rdf:type triple either thrice or 0 times. parser.nodeStringToTriples[node.String()] = []*gordfParser.Triple{ dummyTriple, dummyTriple, dummyTriple, } err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to invalid rdf:type triples, got %v", err) } // TestCase 3: triples with 0 startPointer parser, _ = parserFromBodyContent(` 420 310 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to missing start pointer, got %v", err) } // TestCase 4: triples with 0 endPointer parser, _ = parserFromBodyContent(` 420 310 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to missing end pointer, got %v", err) } // TestCase 5: error parsing start pointer must be propagated to the range parser, _ = parserFromBodyContent(` 42.0 310 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to invalid start pointer, got %v", err) } // TestCase 6: error parsing end pointer must be propagated to the range parser, _ = parserFromBodyContent(` 420 31+0 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to invalid end pointer, got %v", err) } // TestCase 7: mismatching start and end pointer must also raise an error. parser, _ = parserFromBodyContent(` 420 310 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to mismatching start and end pointers, got %v", err) } // TestCase 8: everything valid(byte_range): parser, _ = parserFromBodyContent(` 420 310 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err != nil { t.Errorf("unexpected error: %v", err) } // TestCase 9: everything valid(line_range): parser, _ = parserFromBodyContent(` 420 310 `) si = &v2_2.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err != nil { t.Errorf("unexpected error: %v", err) } } func Test_rdfParser2_2_setSnippetToFileWithID(t *testing.T) { var parser *rdfParser2_2 var fileId common.ElementID var si *v2_2.Snippet var file *v2_2.File var err error // TestCase 1: file id which is not associated with any file must raise an error. parser, _ = parserFromBodyContent("") si = &v2_2.Snippet{} err = parser.setSnippetToFileWithID(si, fileId) if err == nil { t.Errorf("expected an error saying undefined file") } // TestCase 2: file exists, but snippet of the file doesn't ( it mustn't raise any error ) fileId = common.ElementID("File1") file = &v2_2.File{ FileSPDXIdentifier: fileId, } parser.files[fileId] = file file.Snippets = nil // nil snippets err = parser.setSnippetToFileWithID(si, fileId) if err != nil { t.Errorf("unexpected error: %v", err) } if len(file.Snippets) != 1 { t.Errorf("expected file to have 1 snippet, got %d", len(file.Snippets)) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_spdx_document.go000066400000000000000000000101221463371440000246130ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *rdfParser2_2) parseSpdxDocumentNode(spdxDocNode *gordfParser.Node) (err error) { // shorthand for document's creation info. ci := parser.doc.CreationInfo // parse the document header information (SPDXID and document namespace) // the Subject.ID is of type baseURI#spdxID baseUri, offset, err := ExtractSubs(spdxDocNode.ID, "#") if err != nil { return err } parser.doc.DocumentNamespace = baseUri // 2.5 parser.doc.SPDXIdentifier = common.ElementID(offset) // 2.3 // parse other associated triples. for _, subTriple := range parser.nodeToTriples(spdxDocNode) { objectValue := subTriple.Object.ID switch subTriple.Predicate.ID { case RDF_TYPE: continue case SPDX_SPEC_VERSION: // 2.1: specVersion // cardinality: exactly 1 parser.doc.SPDXVersion = objectValue case SPDX_DATA_LICENSE: // 2.2: dataLicense // cardinality: exactly 1 dataLicense, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return err } parser.doc.DataLicense = dataLicense.ToLicenseString() case SPDX_NAME: // 2.4: DocumentName // cardinality: exactly 1 parser.doc.DocumentName = objectValue case SPDX_EXTERNAL_DOCUMENT_REF: // 2.6: externalDocumentReferences // cardinality: min 0 var extRef v2_2.ExternalDocumentRef extRef, err = parser.getExternalDocumentRefFromNode(subTriple.Object) if err != nil { return err } parser.doc.ExternalDocumentReferences = append(parser.doc.ExternalDocumentReferences, extRef) case SPDX_CREATION_INFO: // 2.7 - 2.10: // cardinality: exactly 1 err = parser.parseCreationInfoFromNode(ci, subTriple.Object) case RDFS_COMMENT: // 2.11: Document Comment // cardinality: max 1 parser.doc.DocumentComment = objectValue case SPDX_REVIEWED: // reviewed: // cardinality: min 0 err = parser.setReviewFromNode(subTriple.Object) case SPDX_DESCRIBES_PACKAGE: // describes Package // cardinality: min 0 var pkg *v2_2.Package pkg, err = parser.getPackageFromNode(subTriple.Object) if err != nil { return err } parser.doc.Packages = append(parser.doc.Packages, pkg) case SPDX_HAS_EXTRACTED_LICENSING_INFO: // hasExtractedLicensingInfo // cardinality: min 0 extractedLicensingInfo, err := parser.getExtractedLicensingInfoFromNode(subTriple.Object) if err != nil { return fmt.Errorf("error setting extractedLicensingInfo in spdxDocument: %v", err) } othLicense := parser.extractedLicenseToOtherLicense(extractedLicensingInfo) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, &othLicense) case SPDX_RELATIONSHIP: // relationship // cardinality: min 0 err = parser.parseRelationship(subTriple) case SPDX_ANNOTATION: // annotations // cardinality: min 0 err = parser.parseAnnotationFromNode(subTriple.Object) default: return fmt.Errorf("invalid predicate while parsing SpdxDocument: %v", subTriple.Predicate) } if err != nil { return err } } return nil } func (parser *rdfParser2_2) getExternalDocumentRefFromNode(node *gordfParser.Node) (edr v2_2.ExternalDocumentRef, err error) { for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case SPDX_EXTERNAL_DOCUMENT_ID: // cardinality: exactly 1 edr.DocumentRefID = triple.Object.ID case SPDX_SPDX_DOCUMENT: // cardinality: exactly 1 // assumption: "spdxDocument" property of an external document // reference is just a uri which doesn't follow a spdxDocument definition edr.URI = triple.Object.ID case SPDX_CHECKSUM: // cardinality: exactly 1 alg, checksum, err := parser.getChecksumFromNode(triple.Object) if err != nil { return edr, err } edr.Checksum.Value = checksum edr.Checksum.Algorithm = alg case RDF_TYPE: continue default: return edr, fmt.Errorf("unknown predicate ID (%s) while parsing externalDocumentReference", triple.Predicate.ID) } } return edr, nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parse_spdx_document_test.go000066400000000000000000000234071463371440000256640ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" ) func Test_rdfParser2_2_getExternalDocumentRefFromNode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var err error // TestCase 1: invalid checksum parser, _ = parserFromBodyContent(` DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExternalDocumentRefFromNode(node) if err == nil { t.Errorf("expected an error due to invalid checksum, found %v", err) } // TestCase 2: unknown predicate parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExternalDocumentRefFromNode(node) if err == nil { t.Errorf("expected an error due to invalid predicate, found %v", err) } // TestCase 3: valid example parser, _ = parserFromBodyContent(` DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExternalDocumentRefFromNode(node) if err != nil { t.Errorf("unexpected error: %v", err) } } func Test_rdfParser2_2_parseSpdxDocumentNode(t *testing.T) { var parser *rdfParser2_2 var node *gordfParser.Node var err error // TestCase 1: invalid spdx id of the document parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid document id, got %v", err) } // TestCase 2: erroneous dataLicense parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid dataLicense, got %v", err) } // TestCase 3: invalid external document ref parser, _ = parserFromBodyContent(` DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid externalDocumentRef, got %v", err) } // TestCase 4: invalid package parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid externalDocumentRef, got %v", err) } // TestCase 5: error in extractedLicensingInfo parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid extractedLicensingInfo, got %v", err) } // TestCase 6: error in annotation parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid extractedLicensingInfo, got %v", err) } // TestCase 7: invalid predicate parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to unknown predicate, got %v", err) } // TestCase 7: everything valid parser, _ = parserFromBodyContent(` SPDX-2.1 /test/example DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 2.6 Person: spdx (y) Organization: Tool: spdx2 2018-08-24T19:55:34Z test Another example reviewer. 2011-03-13T00:00:00Z Person: Suzanne Reviewer 2011-01-29T18:30:22Z test annotation Person: Rishabh Bhatnagar `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err != nil { t.Errorf("unexpected error: %v", err) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parser.go000066400000000000000000000124201463371440000220440ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "errors" "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" gordfWriter "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // returns a new instance of rdfParser2_2 given the gordf object and nodeToTriples mapping func NewParser2_2(gordfParserObj *gordfParser.Parser, nodeToTriples map[string][]*gordfParser.Triple) *rdfParser2_2 { parser := rdfParser2_2{ gordfParserObj: gordfParserObj, nodeStringToTriples: nodeToTriples, doc: &v2_2.Document{ ExternalDocumentReferences: []v2_2.ExternalDocumentRef{}, CreationInfo: &v2_2.CreationInfo{}, Packages: []*v2_2.Package{}, Files: []*v2_2.File{}, OtherLicenses: []*v2_2.OtherLicense{}, Relationships: []*v2_2.Relationship{}, Annotations: []*v2_2.Annotation{}, Reviews: []*v2_2.Review{}, }, files: map[common.ElementID]*v2_2.File{}, assocWithPackage: map[common.ElementID]bool{}, cache: map[string]*nodeState{}, } return &parser } // main function which takes in a gordfParser and returns // a spdxDocument model or the error encountered while parsing it func LoadFromGoRDFParser(gordfParserObj *gordfParser.Parser) (*v2_2.Document, error) { // nodeToTriples is a mapping from a node to list of triples. // for every node in the set of subjects of all the triples, // it provides a list of triples that are associated with that subject node. nodeToTriples := gordfWriter.GetNodeToTriples(gordfParserObj.Triples) parser := NewParser2_2(gordfParserObj, nodeToTriples) spdxDocumentNode, err := parser.getSpdxDocNode() if err != nil { return nil, err } err = parser.parseSpdxDocumentNode(spdxDocumentNode) if err != nil { return nil, err } // parsing other root elements for _, rootNode := range gordfWriter.GetRootNodes(parser.gordfParserObj.Triples) { typeTriples := gordfWriter.FilterTriples(gordfParserObj.Triples, &rootNode.ID, &RDF_TYPE, nil) if len(typeTriples) != 1 { return nil, fmt.Errorf("every node must be associated with exactly 1 type Triple. found %d type triples", len(typeTriples)) } switch typeTriples[0].Object.ID { case SPDX_SPDX_DOCUMENT_CAPITALIZED: continue // it is already parsed. case SPDX_SNIPPET: snippet, err := parser.getSnippetInformationFromNode2_2(typeTriples[0].Subject) if err != nil { return nil, fmt.Errorf("error parsing a snippet: %v", err) } err = parser.setSnippetToFileWithID(snippet, snippet.SnippetFromFileSPDXIdentifier) if err != nil { return nil, err } // todo: check other root node attributes. default: continue // because in rdf it is quite possible that the root node is an // element that has been used in the some other element as a child } } // parsing packages and files sets the files to a files variable which is // associated with the parser and not the document. following method is // necessary to transfer the files which are not set in the packages to the // Files attribute of the document // WARNING: do not relocate following function call. It must be at the end of the function parser.setUnpackagedFiles() return parser.doc, nil } // from the given parser object, returns the SpdxDocument Node defined in the root elements. // returns error if the document is associated with no SpdxDocument or // associated with more than one SpdxDocument node. func (parser *rdfParser2_2) getSpdxDocNode() (node *gordfParser.Node, err error) { /* Possible Questions: 1. why are you traversing the root nodes only? why not directly filter out all the triples with rdf:type=spdx:SpdxDocument? Ans: It is quite possible that the relatedElement or any other attribute to have dependency of another SpdxDocument. In that case, that element will reference the dependency using SpdxDocument tag which will cause false positives when direct filtering is done. */ // iterate over root nodes and find the node which has a property of rdf:type=spdx:SpdxDocument var spdxDocNode *gordfParser.Node for _, rootNode := range gordfWriter.GetRootNodes(parser.gordfParserObj.Triples) { typeTriples := gordfWriter.FilterTriples( parser.nodeToTriples(rootNode), // triples &rootNode.ID, // Subject &RDF_TYPE, // Predicate nil, // Object ) if typeTriples[0].Object.ID == SPDX_SPDX_DOCUMENT_CAPITALIZED { // we found a SpdxDocument Node // must be associated with exactly one rdf:type. if len(typeTriples) != 1 { return nil, fmt.Errorf("rootNode (%v) must be associated with exactly one"+ " triple of predicate rdf:type, found %d triples", rootNode, len(typeTriples)) } // checking if we've already found a node and it is not same as the current one. if spdxDocNode != nil && spdxDocNode.ID != typeTriples[0].Subject.ID { return nil, fmt.Errorf("found more than one SpdxDocument Node (%v and %v)", spdxDocNode, typeTriples[0].Subject) } spdxDocNode = typeTriples[0].Subject } } if spdxDocNode == nil { return nil, errors.New("RDF files must be associated with a SpdxDocument tag. No tag found") } return spdxDocNode, nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/parser_test.go000066400000000000000000000140101463371440000231000ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" ) func TestNewParser2_2(t *testing.T) { // testing if the attributes are initialised well and no top-level is left uninitialized. // primarily, checking if all the maps are initialized because // uninitialized slices are by default slices of length 0 p, _ := parserFromBodyContent(``) parser := NewParser2_2(p.gordfParserObj, p.nodeStringToTriples) if parser.files == nil { t.Errorf("files should've been initialised, got %v", parser.files) } if parser.assocWithPackage == nil { t.Errorf("assocWithPackage should've been initialised, got %v", parser.assocWithPackage) } if parser.doc.CreationInfo == nil { t.Errorf("doc.CreationInfo should've been initialised, got %v", parser.doc.CreationInfo) } if parser.doc.Packages == nil { t.Errorf("doc.Packages should've been initialised, got %v", parser.doc.Packages) } if parser.doc.Files == nil { t.Errorf("doc.Files should've been initialised, got %v", parser.doc.Files) } } func TestLoadFromGoRDFParser(t *testing.T) { var parser *rdfParser2_2 var err error // TestCase 1: gordfparser without a SpdxDocument node triple: parser, _ = parserFromBodyContent("") _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error because of absence of SpdxDocument node, got %v", err) } // TestCase 2: invalid SpdxDocumentNode parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error because of absence of SpdxDocument node, got %v", err) } // TestCase 3: >1 type triples for subnode of a SpdxDocument: parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error due to more than one type triples, got %v", err) } // TestCase 4: invalid snippet must raise an error. parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error due to invalid Snippet, got %v", err) } // TestCase 5: invalid snippet not associated with any File must raise an error. parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error due to invalid Snippet File, got %v", err) } // TestCase 6: other Tag alongwith the SpdxDocument node mustn't raise any error. parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err != nil { t.Errorf("unexpected error: %v", err) } // TestCase 5: everything valid: parser, _ = parserFromBodyContent(` from linux kernel Copyright 2008-2010 John Smith 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. Copyright 2010, 2011 Source Auditor Inc. Open Logic Inc. ./src/org/spdx/parser/DOAPProject.java Black Duck Software In.c `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err != nil { t.Errorf("error parsing a valid example: %v", err) } } func Test_rdfParser2_2_getSpdxDocNode(t *testing.T) { var parser *rdfParser2_2 var err error // TestCase 1: more than one association type for a single node. parser, _ = parserFromBodyContent(` `) _, err = parser.getSpdxDocNode() t.Log(err) if err == nil { t.Errorf("expected and error due to more than one type triples for the SpdxDocument Node, got %v", err) } // TestCase 2: must be associated with exactly one rdf:type. parser, _ = parserFromBodyContent(` `) _, err = parser.getSpdxDocNode() t.Log(err) if err == nil { t.Errorf("rootNode must be associated with exactly one triple of predicate rdf:type, got %v", err) } // TestCase 3: two different spdx nodes found in a single document. parser, _ = parserFromBodyContent(` `) _, err = parser.getSpdxDocNode() if err == nil { t.Errorf("expected and error due to more than one type SpdxDocument Node, got %v", err) } // TestCase 4: no spdx document parser, _ = parserFromBodyContent(``) _, err = parser.getSpdxDocNode() if err == nil { t.Errorf("expected and error due to no SpdxDocument Node, got %v", err) } // TestCase 5: valid spdxDocument node parser, _ = parserFromBodyContent(` `) _, err = parser.getSpdxDocNode() if err != nil { t.Errorf("unexpected error: %v", err) } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/types.go000066400000000000000000000055471463371440000217300ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // copied from tvloader/parser2v2/types.go package reader import ( gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) type rdfParser2_2 struct { // fields associated with gordf project which // will be required by rdfloader gordfParserObj *gordfParser.Parser nodeStringToTriples map[string][]*gordfParser.Triple // document into which data is being parsed doc *v2_2.Document // map of packages and files. files map[common.ElementID]*v2_2.File assocWithPackage map[common.ElementID]bool // mapping of nodeStrings to parsed object to save double computation. cache map[string]*nodeState } type Color int const ( GREY Color = iota // represents that the node is being visited WHITE // unvisited node BLACK // visited node ) type nodeState struct { // object will be pointer to the parsed or element being parsed. object interface{} // color of a state represents if the node is visited/unvisited/being-visited. Color Color } type AnyLicenseInfo interface { // ToLicenseString returns the representation of license about how it will // be stored in the tools-golang data model ToLicenseString() string } type SimpleLicensingInfo struct { AnyLicenseInfo comment string licenseID string name string seeAlso []string example string } type ExtractedLicensingInfo struct { SimpleLicensingInfo extractedText string } type OrLaterOperator struct { AnyLicenseInfo member SimpleLicensingInfo } type ConjunctiveLicenseSet struct { AnyLicenseInfo members []AnyLicenseInfo } type DisjunctiveLicenseSet struct { AnyLicenseInfo members []AnyLicenseInfo } type License struct { SimpleLicensingInfo isOsiApproved bool licenseText string standardLicenseHeader string standardLicenseTemplate string standardLicenseHeaderTemplate string isDeprecatedLicenseID bool isFsfLibre bool } type ListedLicense struct { License } type LicenseException struct { licenseExceptionId string licenseExceptionText string seeAlso string // must be a valid uri name string example string comment string } type WithExceptionOperator struct { AnyLicenseInfo member SimpleLicensingInfo licenseException LicenseException } // custom LicenseType to provide support for licences of // type Noassertion, None and customLicenses type SpecialLicense struct { AnyLicenseInfo value SpecialLicenseValue } type SpecialLicenseValue string const ( NONE SpecialLicenseValue = "NONE" NOASSERTION SpecialLicenseValue = "NOASSERTION" ) type RangeType string const ( BYTE_RANGE RangeType = "byteRange" LINE_RANGE RangeType = "lineRange" ) tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/utils.go000066400000000000000000000120031463371440000217050ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "errors" "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" urilib "github.com/spdx/gordf/uri" "github.com/spdx/tools-golang/spdx/v2/common" ) // a uri is of type baseURI#fragment or baseFragment/subFragment // returns fragment or subFragment when given as an input. func getLastPartOfURI(uri string) string { if strings.Contains(uri, "#") { parts := strings.Split(uri, "#") return parts[len(parts)-1] } parts := strings.Split(uri, "/") return parts[len(parts)-1] } func isUriValid(uri string) bool { _, err := urilib.NewURIRef(uri) return err == nil } func getNodeTypeFromTriples(triples []*gordfParser.Triple, node *gordfParser.Node) (string, error) { if node == nil { return "", errors.New("empty node passed to find node type") } typeTriples := rdfwriter.FilterTriples(triples, &node.ID, &RDF_TYPE, nil) switch len(typeTriples) { case 0: return "", fmt.Errorf("node{%v} not associated with any type triple", node) case 1: return typeTriples[0].Object.ID, nil default: return "", fmt.Errorf("node{%v} is associated with more than one type triples", node) } } func (parser *rdfParser2_2) nodeToTriples(node *gordfParser.Node) []*gordfParser.Triple { if node == nil { return []*gordfParser.Triple{} } return parser.nodeStringToTriples[node.String()] } // returns which boolean was given as an input // string(bool) is the only possible input for which it will not raise any error. func boolFromString(boolString string) (bool, error) { switch strings.ToLower(boolString) { case "true": return true, nil case "false": return false, nil default: return false, fmt.Errorf("boolean string can be either true/false") } } /* Function Below this line is taken from the tvloader/parser2v2/utils.go */ // used to extract DocumentRef and SPDXRef values from an SPDX Identifier // which can point either to this document or to a different one func ExtractDocElementID(value string) (common.DocElementID, error) { docRefID := "" idStr := value // check prefix to see if it's a DocumentRef ID if strings.HasPrefix(idStr, "DocumentRef-") { // extract the part that comes between "DocumentRef-" and ":" strs := strings.Split(idStr, ":") // should be exactly two, part before and part after if len(strs) < 2 { return common.DocElementID{}, fmt.Errorf("no colon found although DocumentRef- prefix present") } if len(strs) > 2 { return common.DocElementID{}, fmt.Errorf("more than one colon found") } // trim the prefix and confirm non-empty docRefID = strings.TrimPrefix(strs[0], "DocumentRef-") if docRefID == "" { return common.DocElementID{}, fmt.Errorf("document identifier has nothing after prefix") } // and use remainder for element ID parsing idStr = strs[1] } // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(idStr, "SPDXRef-") { return common.DocElementID{}, fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(idStr, ":") { // we know this means there was no DocumentRef- prefix, because // we would have handled multiple colons above if it was return common.DocElementID{}, fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(idStr, "SPDXRef-") if eltRefID == "" { return common.DocElementID{}, fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.DocElementID{DocumentRefID: docRefID, ElementRefID: common.ElementID(eltRefID)}, nil } // used to extract SPDXRef values only from an SPDX Identifier which can point // to this document only. Use extractDocElementID for parsing IDs that can // refer either to this document or a different one. func ExtractElementID(value string) (common.ElementID, error) { // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(value, "SPDXRef-") { return common.ElementID(""), fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(value, ":") { return common.ElementID(""), fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(value, "SPDXRef-") if eltRefID == "" { return common.ElementID(""), fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.ElementID(eltRefID), nil } // used to extract key / value from embedded substrings // returns subkey, subvalue, nil if no error, or "", "", error otherwise func ExtractSubs(value string, sep string) (string, string, error) { // parse the value to see if it's a valid subvalue format sp := strings.SplitN(value, sep, 2) if len(sp) == 1 { return "", "", fmt.Errorf("invalid subvalue format for %s (no %s found)", value, sep) } subkey := strings.TrimSpace(sp[0]) subvalue := strings.TrimSpace(sp[1]) return subkey, subvalue, nil } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader/utils_test.go000066400000000000000000000230541463371440000227540ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" ) func Test_getLastPartOfURI(t *testing.T) { // uri of type baseFragment#fragment input := "baseFragment#fragment" expectedOutput := "fragment" output := getLastPartOfURI(input) if output != expectedOutput { t.Errorf("expected %s, found %s", expectedOutput, output) } // uri of type baseFragment/subFragment input = "baseFragment/subFragment" expectedOutput = "subFragment" output = getLastPartOfURI(input) if output != expectedOutput { t.Errorf("expected %s, found %s", expectedOutput, output) } // neither of the case mustn't raise any error. input = "www.github.com" expectedOutput = input output = getLastPartOfURI(input) if output != expectedOutput { t.Errorf("expected %s, found %s", expectedOutput, output) } } func Test_isUriValid(t *testing.T) { // TestCase 1: Valid Input URI input := "https://www.github.com" isValid := isUriValid(input) if !isValid { t.Errorf("valid input(%s) detected as invalid.", input) } // TestCase 2: Invalid Input URI input = `http\:www.github.com` isValid = isUriValid(input) if isValid { t.Errorf("invalid input(%s) detected as valid", input) } } func Test_rdfParser2_2_nodeToTriples(t *testing.T) { var parser *rdfParser2_2 var output, expectedOutput []*gordfParser.Triple // TestCase 1: a nil node shouldn't raise any error or panic. parser, _ = parserFromBodyContent(``) output = parser.nodeToTriples(nil) if output == nil { t.Errorf("nil input should return an empty slice and not nil") } expectedOutput = []*gordfParser.Triple{} if !reflect.DeepEqual(output, expectedOutput) { t.Errorf("expected %+v, got %+v", expectedOutput, output) } // TestCase 2: node should be addressable based on the node content and not the pointer. // It should allow new nodes same as the older ones to retrieve the associated triples. parser, _ = parserFromBodyContent(` 75068c26abbed3ad3980685bae21d7202d288317 `) newNode := &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#checksum", } output = parser.nodeToTriples(newNode) // The output must have 3 triples: // 1. newNode rdf:type Checksum // 2. newNode spdx:algorithm http://spdx.org/rdf/terms#checksumAlgorithm_sha1 // 3. newNode spdx:checksumValue 75068c26abbed3ad3980685bae21d7202d288317 if len(output) != 3 { t.Errorf("expected output to have 3 triples, got %d", len(output)) } } func Test_boolFromString(t *testing.T) { // TestCase 1: Valid Input: "true" // mustn't raise any error input := "true" val, err := boolFromString(input) if err != nil { t.Errorf("function raised an error for a valid input(%s): %s", input, err) } if val != true { t.Errorf("invalid output. Expected %v, found %v", true, val) } // TestCase 2: Valid Input: "true" // mustn't raise any error input = "false" val, err = boolFromString(input) if err != nil { t.Errorf("function raised an error for a valid input(%s): %s", input, err) } if val != false { t.Errorf("invalid output. Expected %v, found %v", false, val) } // TestCase 3: invalid input: "" // it must raise an error input = "" val, err = boolFromString(input) if err == nil { t.Errorf("invalid input should've raised an error") } } func Test_getNodeTypeFromTriples(t *testing.T) { var err error var node *gordfParser.Node var triples []*gordfParser.Triple var nodeType, expectedNodeType string // TestCase 1: nil node must raise an error because, // nil nodes cannot be associated with any rdf:type attribute. _, err = getNodeTypeFromTriples(triples, nil) if err == nil { t.Errorf("expected an error due to nil node, got %v", err) } // TestCase 2: none of the triples give information about the rdf:type of a node. node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "N0", } _, err = getNodeTypeFromTriples(triples, node) if err == nil { t.Errorf("expected an error saying no rdf:type found, got %v", err) } // TestCase 3: node is associated with exactly one rdf:type triples typeTriple := &gordfParser.Triple{ Subject: node, Predicate: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: RDF_TYPE, }, Object: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "http://spdx.org/rdf/terms#Checksum", }, } triples = append(triples, typeTriple) expectedNodeType = "http://spdx.org/rdf/terms#Checksum" nodeType, err = getNodeTypeFromTriples(triples, node) if err != nil { t.Fatalf("unexpected error: %v", err) } if nodeType != expectedNodeType { t.Errorf("expected: %v, got: %v", nodeType, expectedNodeType) } // TestCase 4: node associated with more than one rdf:type triples must raise an error. typeTriple = &gordfParser.Triple{ Subject: node, Predicate: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: RDF_TYPE, }, Object: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "http://spdx.org/rdf/terms#Snippet", }, } triples = append(triples, typeTriple) _, err = getNodeTypeFromTriples(triples, node) if err == nil { t.Errorf("expected an error saying more than one rdf:type found, got %v", err) } } // following tests are copy pasted from tvloader/parser2v2/util_test.go func TestCanExtractDocumentAndElementRefsFromID(t *testing.T) { // test with valid ID in this document helperForExtractDocElementID(t, "SPDXRef-file1", false, "", "file1") // test with valid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file2", false, "doc2", "file2") // test with invalid ID in this document helperForExtractDocElementID(t, "a:SPDXRef-file1", true, "", "") helperForExtractDocElementID(t, "file1", true, "", "") helperForExtractDocElementID(t, "SPDXRef-", true, "", "") helperForExtractDocElementID(t, "SPDXRef-file1:", true, "", "") // test with invalid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:a", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:SPDXRef-file1", true, "", "") // test with invalid formats helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file1:file2", true, "", "") } func helperForExtractDocElementID(t *testing.T, tst string, wantErr bool, wantDoc string, wantElt string) { deID, err := ExtractDocElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if deID.DocumentRefID != wantDoc { if wantDoc == "" { t.Errorf("testing %v: want empty string for DocumentRefID, got %v", tst, deID.DocumentRefID) } else { t.Errorf("testing %v: want %v for DocumentRefID, got %v", tst, wantDoc, deID.DocumentRefID) } } if deID.ElementRefID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, deID.ElementRefID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, deID.ElementRefID) } } } func TestCanExtractElementRefsOnlyFromID(t *testing.T) { // test with valid ID in this document helperForExtractElementID(t, "SPDXRef-file1", false, "file1") // test with valid ID in another document helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-file2", true, "") // test with invalid ID in this document helperForExtractElementID(t, "a:SPDXRef-file1", true, "") helperForExtractElementID(t, "file1", true, "") helperForExtractElementID(t, "SPDXRef-", true, "") helperForExtractElementID(t, "SPDXRef-file1:", true, "") // test with invalid ID in another document helperForExtractElementID(t, "DocumentRef-doc2", true, "") helperForExtractElementID(t, "DocumentRef-doc2:", true, "") helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-", true, "") helperForExtractElementID(t, "DocumentRef-doc2:a", true, "") helperForExtractElementID(t, "DocumentRef-:", true, "") helperForExtractElementID(t, "DocumentRef-:SPDXRef-file1", true, "") } func helperForExtractElementID(t *testing.T, tst string, wantErr bool, wantElt string) { eID, err := ExtractElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if eID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, eID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, eID) } } } func TestCanExtractSubvalues(t *testing.T) { subkey, subvalue, err := ExtractSubs("SHA1: abc123", ":") if err != nil { t.Errorf("got error when calling extractSubs: %v", err) } if subkey != "SHA1" { t.Errorf("got %v for subkey", subkey) } if subvalue != "abc123" { t.Errorf("got %v for subvalue", subvalue) } } func TestReturnsErrorForInvalidSubvalueFormat(t *testing.T) { _, _, err := ExtractSubs("blah", ":") if err == nil { t.Errorf("expected error when calling extractSubs for invalid format (0 colons), got nil") } } tools-golang-0.5.5/spdx/v2/v2_2/rdf/reader_test.go000066400000000000000000000013761463371440000216170ustar00rootroot00000000000000package rdf import ( "io" "strings" "testing" ) func Test_Read(t *testing.T) { var reader io.Reader var err error // TestCase 1: invalid rdf/xml must raise an error reader = strings.NewReader("") _, err = Read(reader) if err == nil { t.Errorf("expected an EOF error reading an empty file, got %v", err) } // TestCase 2: Valid rdf/xml but invalid spdx document must raise an error reader = strings.NewReader(` `) _, err = Read(reader) if err == nil { t.Errorf("expected an error due to no SpdxDocument Node in the document") } } tools-golang-0.5.5/spdx/v2/v2_2/relationship.go000066400000000000000000000014271463371440000212410ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Relationship is a Relationship section of an SPDX Document for // version 2.2 of the spec. type Relationship struct { // 11.1: Relationship // Cardinality: optional, one or more; one per Relationship // one mandatory for SPDX Document with multiple packages // RefA and RefB are first and second item // Relationship is type from 11.1.1 RefA common.DocElementID `json:"spdxElementId"` RefB common.DocElementID `json:"relatedSpdxElement"` Relationship string `json:"relationshipType"` // 11.2: Relationship Comment // Cardinality: optional, one RelationshipComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_2/review.go000066400000000000000000000013351463371440000200370ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 // Review is a Review section of an SPDX Document for version 2.2 of the spec. // DEPRECATED in version 2.0 of spec; retained here for compatibility. type Review struct { // DEPRECATED in version 2.0 of spec // 13.1: Reviewer // Cardinality: optional, one Reviewer string // including AnnotatorType: one of "Person", "Organization" or "Tool" ReviewerType string // DEPRECATED in version 2.0 of spec // 13.2: Review Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: conditional (mandatory, one) if there is a Reviewer ReviewDate string // DEPRECATED in version 2.0 of spec // 13.3: Review Comment // Cardinality: optional, one ReviewComment string } tools-golang-0.5.5/spdx/v2/v2_2/snippet.go000066400000000000000000000032171463371440000202210ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_2 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Snippet is a Snippet section of an SPDX Document for version 2.2 of the spec. type Snippet struct { // 9.1: Snippet SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one SnippetSPDXIdentifier common.ElementID `json:"SPDXID"` // 9.2: Snippet from File SPDX Identifier // Cardinality: mandatory, one SnippetFromFileSPDXIdentifier common.ElementID `json:"snippetFromFile"` // Ranges denotes the start/end byte offsets or line numbers that the snippet is relevant to Ranges []common.SnippetRange `json:"ranges"` // 9.5: Snippet Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: mandatory, one SnippetLicenseConcluded string `json:"licenseConcluded"` // 9.6: License Information in Snippet: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one or many LicenseInfoInSnippet []string `json:"licenseInfoInSnippets,omitempty"` // 9.7: Snippet Comments on License // Cardinality: optional, one SnippetLicenseComments string `json:"licenseComments,omitempty"` // 9.8: Snippet Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: mandatory, one SnippetCopyrightText string `json:"copyrightText"` // 9.9: Snippet Comment // Cardinality: optional, one SnippetComment string `json:"comment,omitempty"` // 9.10: Snippet Name // Cardinality: optional, one SnippetName string `json:"name,omitempty"` // 9.11: Snippet Attribution Text // Cardinality: optional, one or many SnippetAttributionTexts []string `json:"-"` } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/000077500000000000000000000000001463371440000200155ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/000077500000000000000000000000001463371440000212575ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_annotation.go000066400000000000000000000020531463371440000251520ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" ) func (parser *tvParser) parsePairForAnnotation(tag string, value string) error { if parser.ann == nil { return fmt.Errorf("no annotation struct created in parser ann pointer") } switch tag { case "Annotator": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if subkey == "Person" || subkey == "Organization" || subkey == "Tool" { parser.ann.Annotator.AnnotatorType = subkey parser.ann.Annotator.Annotator = subvalue return nil } return fmt.Errorf("unrecognized Annotator type %v", subkey) case "AnnotationDate": parser.ann.AnnotationDate = value case "AnnotationType": parser.ann.AnnotationType = value case "SPDXREF": deID, err := extractDocElementID(value) if err != nil { return err } parser.ann.AnnotationSPDXIdentifier = deID case "AnnotationComment": parser.ann.AnnotationComment = value default: return fmt.Errorf("received unknown tag %v in Annotation section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_annotation_test.go000066400000000000000000000110061463371440000262070ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Annotation section tests ===== func TestParserFailsIfAnnotationNotSet(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairForAnnotation("Annotator", "Person: John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected error when calling parsePairFromAnnotation without setting ann pointer") } } func TestParserFailsIfAnnotationTagUnknown(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // start with valid annotator err := parser.parsePair("Annotator", "Person: John Doe (jdoe@example.com)") if err != nil { t.Errorf("expected nil error, got %v", err) } // parse invalid tag, using parsePairForAnnotation( err = parser.parsePairForAnnotation("blah", "oops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotationFieldsWithoutAnnotation(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("AnnotationDate", "2018-09-15T17:25:00Z") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationDate without Annotator first") } err = parser.parsePair("AnnotationType", "REVIEW") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationType without Annotator first") } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err == nil { t.Errorf("expected error when calling parsePair for SPDXREF without Annotator first") } err = parser.parsePair("AnnotationComment", "comment whatever") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationComment without Annotator first") } } func TestParserCanParseAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Annotator without email address err := parser.parsePair("Annotator", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.Annotator.Annotator != "John Doe" { t.Errorf("got %+v for Annotator, expected John Doe", parser.ann.Annotator.Annotator) } if parser.ann.Annotator.AnnotatorType != "Person" { t.Errorf("got %v for AnnotatorType, expected Person", parser.ann.Annotator.AnnotatorType) } // Annotation Date dt := "2018-09-15T17:32:00Z" err = parser.parsePair("AnnotationDate", dt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationDate != dt { t.Errorf("got %v for AnnotationDate, expected %v", parser.ann.AnnotationDate, dt) } // Annotation type aType := "REVIEW" err = parser.parsePair("AnnotationType", aType) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationType != aType { t.Errorf("got %v for AnnotationType, expected %v", parser.ann.AnnotationType, aType) } // SPDX Identifier Reference ref := "SPDXRef-30" err = parser.parsePair("SPDXREF", ref) if err != nil { t.Errorf("expected nil error, got %v", err) } deID := parser.ann.AnnotationSPDXIdentifier if deID.DocumentRefID != "" || deID.ElementRefID != "30" { t.Errorf("got %v for SPDXREF, expected %v", parser.ann.AnnotationSPDXIdentifier, "30") } // Annotation Comment cmt := "this is a comment" err = parser.parsePair("AnnotationComment", cmt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationComment != cmt { t.Errorf("got %v for AnnotationComment, expected %v", parser.ann.AnnotationComment, cmt) } } func TestParserFailsIfAnnotatorInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotatorTypeInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Human: John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotationRefInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // start with valid annotator err := parser.parsePair("Annotator", "Person: John Doe (jdoe@example.com)") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePair("SPDXREF", "blah:other") if err == nil { t.Errorf("expected non-nil error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_creation_info.go000066400000000000000000000117541463371440000256270ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *tvParser) parsePairFromCreationInfo(tag string, value string) error { // fail if not in Creation Info parser state if parser.st != psCreationInfo { return fmt.Errorf("got invalid state %v in parsePairFromCreationInfo", parser.st) } // create an SPDX Creation Info data struct if we don't have one already if parser.doc.CreationInfo == nil { parser.doc.CreationInfo = &v2_2.CreationInfo{} } ci := parser.doc.CreationInfo switch tag { case "LicenseListVersion": ci.LicenseListVersion = value case "Creator": subkey, subvalue, err := extractSubs(value) if err != nil { return err } creator := common.Creator{Creator: subvalue} switch subkey { case "Person", "Organization", "Tool": creator.CreatorType = subkey default: return fmt.Errorf("unrecognized Creator type %v", subkey) } ci.Creators = append(ci.Creators, creator) case "Created": ci.Created = value case "CreatorComment": ci.CreatorComment = value // tag for going on to package section case "PackageName": // error if last file does not have an identifier // this may be a null case: can we ever have a "last file" in // the "creation info" state? should go on to "file" state // even when parsing unpackaged files. if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.st = psPackage parser.pkg = &v2_2.Package{ FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, } return parser.parsePairFromPackage(tag, value) // tag for going on to _unpackaged_ file section case "FileName": // leave pkg as nil, so that packages will be placed in Files parser.st = psFile parser.pkg = nil return parser.parsePairFromFile(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_2.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_2.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) case "DocumentComment": parser.st = psStart return parser.parsePairFromStart(tag, value) default: return fmt.Errorf("received unknown tag %v in CreationInfo section", tag) } return nil } // ===== Helper functions ===== func extractExternalDocumentReference(value string) (string, string, string, string, error) { sp := strings.Split(value, " ") // remove any that are just whitespace keepSp := []string{} for _, s := range sp { ss := strings.TrimSpace(s) if ss != "" { keepSp = append(keepSp, ss) } } var documentRefID, uri, alg, checksum string // now, should have 4 items (or 3, if Alg and Checksum were joined) // and should be able to map them if len(keepSp) == 4 { documentRefID = keepSp[0] uri = keepSp[1] alg = keepSp[2] // check that colon is present for alg, and remove it if !strings.HasSuffix(alg, ":") { return "", "", "", "", fmt.Errorf("algorithm does not end with colon") } alg = strings.TrimSuffix(alg, ":") checksum = keepSp[3] } else if len(keepSp) == 3 { documentRefID = keepSp[0] uri = keepSp[1] // split on colon into alg and checksum parts := strings.SplitN(keepSp[2], ":", 2) if len(parts) != 2 { return "", "", "", "", fmt.Errorf("missing colon separator between algorithm and checksum") } alg = parts[0] checksum = parts[1] } else { return "", "", "", "", fmt.Errorf("expected 4 elements, got %d", len(keepSp)) } // additionally, we should be able to parse the first element as a // DocumentRef- ID string, and we should remove that prefix if !strings.HasPrefix(documentRefID, "DocumentRef-") { return "", "", "", "", fmt.Errorf("expected first element to have DocumentRef- prefix") } documentRefID = strings.TrimPrefix(documentRefID, "DocumentRef-") if documentRefID == "" { return "", "", "", "", fmt.Errorf("document identifier has nothing after prefix") } return documentRefID, uri, alg, checksum, nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_creation_info_test.go000066400000000000000000000332451463371440000266650ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Parser creation info state change tests ===== func TestParserCIMovesToPackageAfterParsingPackageNameTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } pkgName := "testPkg" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the package should NOT be in the SPDX Document's map of packages, // because it doesn't have an SPDX identifier yet if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } } func TestParserCIMovesToFileAfterParsingFileNameTagWithNoPackages(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("FileName", "testFile") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } // and current package should be nil, meaning Files are placed in the // Files map instead of in a Package if parser.pkg != nil { t.Fatalf("expected pkg to be nil, got non-nil pkg") } } func TestParserCIMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } } func TestParserCIMovesToOtherLicenseAfterParsingDocumentTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("DocumentComment", "doccomment") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } } func TestParserCIMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } } func TestParserCIStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } } func TestParserCIStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this spdx file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } } func TestParserFailsParsingCreationInfoWithInvalidState(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psPackage, } err := parser.parsePairFromCreationInfo("SPDXVersion", "SPDX-2.2") if err == nil { t.Errorf("expected non-nil error, got nil") } } // ===== Creation Info section tests ===== func TestParserHasCreationInfoAfterCallToParseFirstTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("LicenseListVersion", "3.9") if err != nil { t.Errorf("got error when calling parsePairFromCreationInfo: %v", err) } if parser.doc.CreationInfo == nil { t.Errorf("doc.CreationInfo is still nil after parsing first pair") } } func TestParserCanParseCreationInfoTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // License List Version err := parser.parsePairFromCreationInfo("LicenseListVersion", "2.2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.LicenseListVersion != "2.2" { t.Errorf("got %v for LicenseListVersion", parser.doc.CreationInfo.LicenseListVersion) } // Creators: Persons refPersons := []string{ "Person: Person A", "Person: Person B", } err = parser.parsePairFromCreationInfo("Creator", refPersons[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refPersons[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 2 || parser.doc.CreationInfo.Creators[0].Creator != "Person A" || parser.doc.CreationInfo.Creators[1].Creator != "Person B" { t.Errorf("got %v for CreatorPersons", parser.doc.CreationInfo.Creators) } // Creators: Organizations refOrgs := []string{ "Organization: Organization A", "Organization: Organization B", } err = parser.parsePairFromCreationInfo("Creator", refOrgs[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refOrgs[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 4 || parser.doc.CreationInfo.Creators[2].Creator != "Organization A" || parser.doc.CreationInfo.Creators[3].Creator != "Organization B" { t.Errorf("got %v for CreatorOrganizations", parser.doc.CreationInfo.Creators) } // Creators: Tools refTools := []string{ "Tool: Tool A", "Tool: Tool B", } err = parser.parsePairFromCreationInfo("Creator", refTools[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refTools[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 6 || parser.doc.CreationInfo.Creators[4].Creator != "Tool A" || parser.doc.CreationInfo.Creators[5].Creator != "Tool B" { t.Errorf("got %v for CreatorTools", parser.doc.CreationInfo.Creators) } // Created date err = parser.parsePairFromCreationInfo("Created", "2018-09-10T11:46:00Z") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.Created != "2018-09-10T11:46:00Z" { t.Errorf("got %v for Created", parser.doc.CreationInfo.Created) } // Creator Comment err = parser.parsePairFromCreationInfo("CreatorComment", "Blah whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.CreatorComment != "Blah whatever" { t.Errorf("got %v for CreatorComment", parser.doc.CreationInfo.CreatorComment) } } func TestParserInvalidCreatorTagsFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("Creator", "blah: somebody") if err == nil { t.Errorf("expected error from parsing invalid Creator format, got nil") } err = parser.parsePairFromCreationInfo("Creator", "Tool with no colons") if err == nil { t.Errorf("expected error from parsing invalid Creator format, got nil") } } func TestParserCreatorTagWithMultipleColonsPasses(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("Creator", "Tool: tool1:2:3") if err != nil { t.Errorf("unexpected error from parsing valid Creator format") } } func TestParserCIUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserCICreatesRelationship(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserCICreatesAnnotation(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } // ===== Helper function tests ===== func TestCanExtractExternalDocumentReference(t *testing.T) { refstring := "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1:d6a770ba38583ed4bb4525bd96e50461655d2759" wantDocumentRefID := "spdx-tool-1.2" wantURI := "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" wantAlg := "SHA1" wantChecksum := "d6a770ba38583ed4bb4525bd96e50461655d2759" gotDocumentRefID, gotURI, gotAlg, gotChecksum, err := extractExternalDocumentReference(refstring) if err != nil { t.Errorf("got non-nil error: %v", err) } if wantDocumentRefID != gotDocumentRefID { t.Errorf("wanted document ref ID %s, got %s", wantDocumentRefID, gotDocumentRefID) } if wantURI != gotURI { t.Errorf("wanted URI %s, got %s", wantURI, gotURI) } if wantAlg != gotAlg { t.Errorf("wanted alg %s, got %s", wantAlg, gotAlg) } if wantChecksum != gotChecksum { t.Errorf("wanted checksum %s, got %s", wantChecksum, gotChecksum) } } func TestCanExtractExternalDocumentReferenceWithExtraWhitespace(t *testing.T) { refstring := " DocumentRef-spdx-tool-1.2 \t http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 \t SHA1: \t d6a770ba38583ed4bb4525bd96e50461655d2759" wantDocumentRefID := "spdx-tool-1.2" wantURI := "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" wantAlg := "SHA1" wantChecksum := "d6a770ba38583ed4bb4525bd96e50461655d2759" gotDocumentRefID, gotURI, gotAlg, gotChecksum, err := extractExternalDocumentReference(refstring) if err != nil { t.Errorf("got non-nil error: %v", err) } if wantDocumentRefID != gotDocumentRefID { t.Errorf("wanted document ref ID %s, got %s", wantDocumentRefID, gotDocumentRefID) } if wantURI != gotURI { t.Errorf("wanted URI %s, got %s", wantURI, gotURI) } if wantAlg != gotAlg { t.Errorf("wanted alg %s, got %s", wantAlg, gotAlg) } if wantChecksum != gotChecksum { t.Errorf("wanted checksum %s, got %s", wantChecksum, gotChecksum) } } func TestFailsExternalDocumentReferenceWithInvalidFormats(t *testing.T) { invalidRefs := []string{ "whoops", "DocumentRef-", "DocumentRef- ", "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 d6a770ba38583ed4bb4525bd96e50461655d2759", "DocumentRef-spdx-tool-1.2", "spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1:d6a770ba38583ed4bb4525bd96e50461655d2759", } for _, refstring := range invalidRefs { _, _, _, _, err := extractExternalDocumentReference(refstring) if err == nil { t.Errorf("expected non-nil error for %s, got nil", refstring) } } } func TestParserForDocumentComment(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("DocumentComment", "document comment") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_file.go000066400000000000000000000117331463371440000237240ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *tvParser) parsePairFromFile(tag string, value string) error { // expire fileAOP for anything other than an AOPHomePage or AOPURI // (we'll actually handle the HomePage and URI further below) if tag != "ArtifactOfProjectHomePage" && tag != "ArtifactOfProjectURI" { parser.fileAOP = nil } switch tag { // tag for creating new file section case "FileName": // check if the previous file contained an spdx Id or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.file = &v2_2.File{} parser.file.FileName = value // tag for creating new package section and going back to parsing Package case "PackageName": parser.st = psPackage // check if the previous file contained an spdx Id or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.file = nil return parser.parsePairFromPackage(tag, value) // tag for going on to snippet section case "SnippetSPDXID": parser.st = psSnippet return parser.parsePairFromSnippet(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tags for file data case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.file.FileSPDXIdentifier = eID if parser.pkg == nil { if parser.doc.Files == nil { parser.doc.Files = []*v2_2.File{} } parser.doc.Files = append(parser.doc.Files, parser.file) } else { if parser.pkg.Files == nil { parser.pkg.Files = []*v2_2.File{} } parser.pkg.Files = append(parser.pkg.Files, parser.file) } case "FileType": parser.file.FileTypes = append(parser.file.FileTypes, value) case "FileChecksum": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if parser.file.Checksums == nil { parser.file.Checksums = []common.Checksum{} } switch common.ChecksumAlgorithm(subkey) { case common.SHA1, common.SHA224, common.SHA256, common.SHA384, common.SHA512, common.MD2, common.MD4, common.MD5, common.MD6: algorithm := common.ChecksumAlgorithm(subkey) parser.file.Checksums = append(parser.file.Checksums, common.Checksum{Algorithm: algorithm, Value: subvalue}) default: return fmt.Errorf("got unknown checksum type %s", subkey) } case "LicenseConcluded": parser.file.LicenseConcluded = value case "LicenseInfoInFile": parser.file.LicenseInfoInFiles = append(parser.file.LicenseInfoInFiles, value) case "LicenseComments": parser.file.LicenseComments = value case "FileCopyrightText": parser.file.FileCopyrightText = value case "ArtifactOfProjectName": parser.fileAOP = &v2_2.ArtifactOfProject{} parser.file.ArtifactOfProjects = append(parser.file.ArtifactOfProjects, parser.fileAOP) parser.fileAOP.Name = value case "ArtifactOfProjectHomePage": if parser.fileAOP == nil { return fmt.Errorf("no current ArtifactOfProject found") } parser.fileAOP.HomePage = value case "ArtifactOfProjectURI": if parser.fileAOP == nil { return fmt.Errorf("no current ArtifactOfProject found") } parser.fileAOP.URI = value case "FileComment": parser.file.FileComment = value case "FileNotice": parser.file.FileNotice = value case "FileContributor": parser.file.FileContributors = append(parser.file.FileContributors, value) case "FileDependency": parser.file.FileDependencies = append(parser.file.FileDependencies, value) case "FileAttributionText": parser.file.FileAttributionTexts = append(parser.file.FileAttributionTexts, value) // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_2.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_2.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in File section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_file_test.go000066400000000000000000000752371463371440000247740ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "testing" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Parser file section state change tests ===== func TestParserFileStartsNewFileAfterParsingFileNameTag(t *testing.T) { // create the first file fileOldName := "f1.txt" parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: fileOldName, FileSPDXIdentifier: "f1"}, } fileOld := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, fileOld) // the Package's Files should have this one only if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } // now add a new file fileName := "f2.txt" err := parser.parsePair("FileName", fileName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } // and a file should be created if parser.file == nil { t.Fatalf("parser didn't create new file") } // and the file name should be as expected if parser.file.FileName != fileName { t.Errorf("expected file name %s, got %s", fileName, parser.file.FileName) } // and the Package's Files should still be of size 1 and not have this new // one yet, since it hasn't seen an SPDX identifier if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } // now parse an SPDX identifier tag err = parser.parsePair("SPDXID", "SPDXRef-f2ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // and the Package's Files should now be of size 2 and have this new one if len(parser.pkg.Files) != 2 { t.Fatalf("expected 2 files, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } if parser.pkg.Files[1] != parser.file { t.Errorf("expected file %v in Files[f2ID], got %v", parser.file, parser.pkg.Files[1]) } if parser.pkg.Files[1].FileName != fileName { t.Errorf("expected file name %s in Files[f2ID], got %s", fileName, parser.pkg.Files[1].FileName) } } func TestParserFileAddsToPackageOrUnpackagedFiles(t *testing.T) { // start with no packages parser := tvParser{ doc: &v2_2.Document{}, st: psCreationInfo, } // add a file and SPDX identifier fileName := "f2.txt" err := parser.parsePair("FileName", fileName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-f2ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } fileOld := parser.file // should have been added to Files if len(parser.doc.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.doc.Files)) } if parser.doc.Files[0] != fileOld { t.Errorf("expected file %v in Files[f2ID], got %v", fileOld, parser.doc.Files[0]) } // now create a package and a new file err = parser.parsePair("PackageName", "package1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-pkg1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("FileName", "f3.txt") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-f3ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // Files should still be size 1 and have old file only if len(parser.doc.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.doc.Files)) } if parser.doc.Files[0] != fileOld { t.Errorf("expected file %v in Files[f2ID], got %v", fileOld, parser.doc.Files[0]) } // and new package should have gotten the new file if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != parser.file { t.Errorf("expected file %v in Files[f3ID], got %v", parser.file, parser.pkg.Files[0]) } } func TestParserFileStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { // create the first file and package p1Name := "package1" f1Name := "f1.txt" parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: p1Name, PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: f1Name, FileSPDXIdentifier: "f1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, p1) parser.pkg.Files = append(parser.pkg.Files, f1) // now add a new package p2Name := "package2" err := parser.parsePair("PackageName", p2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should go back to Package if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new pkg") } // and the package name should be as expected if parser.pkg.PackageName != p2Name { t.Errorf("expected package name %s, got %s", p2Name, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the new Package should have no files if len(parser.pkg.Files) != 0 { t.Errorf("Expected no files in pkg.Files, got %d", len(parser.pkg.Files)) } // and the Document's Packages should still be of size 1 and not have this // new one, because no SPDX identifier has been seen yet if len(parser.doc.Packages) != 1 { t.Fatalf("expected 1 package, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != p1 { t.Errorf("Expected package %v in Packages[package1], got %v", p1, parser.doc.Packages[0]) } if parser.doc.Packages[0].PackageName != p1Name { t.Errorf("expected package name %s in Packages[package1], got %s", p1Name, parser.doc.Packages[0].PackageName) } // and the first Package's Files should be of size 1 and have f1 only if len(parser.doc.Packages[0].Files) != 1 { t.Errorf("Expected 1 file in Packages[package1].Files, got %d", len(parser.doc.Packages[0].Files)) } if parser.doc.Packages[0].Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.doc.Packages[0].Files[0]) } if parser.doc.Packages[0].Files[0].FileName != f1Name { t.Errorf("expected file name %s in Files[f1], got %s", f1Name, parser.doc.Packages[0].Files[0].FileName) } // and the current file should be nil if parser.file != nil { t.Errorf("Expected nil for parser.file, got %v", parser.file) } } func TestParserFileMovesToSnippetAfterParsingSnippetSPDXIDTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) fileCurrent := parser.file err := parser.parsePair("SnippetSPDXID", "SPDXRef-Test1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and current file should remain what it was if parser.file != fileCurrent { t.Fatalf("expected file to remain %v, got %v", fileCurrent, parser.file) } } func TestParserFileMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserFileMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserFileStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } } func TestParserFileStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } } // ===== File data section tests ===== func TestParserCanParseFileTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileName != "f1.txt" { t.Errorf("got %v for FileName", parser.file.FileName) } // should not yet be added to the Packages file list, because we haven't // seen an SPDX identifier yet if len(parser.pkg.Files) != 0 { t.Errorf("expected 0 files, got %d", len(parser.pkg.Files)) } // File SPDX Identifier err = parser.parsePairFromFile("SPDXID", "SPDXRef-f1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileSPDXIdentifier != "f1" { t.Errorf("got %v for FileSPDXIdentifier", parser.file.FileSPDXIdentifier) } // should now be added to the Packages file list if len(parser.pkg.Files) != 1 { t.Errorf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != parser.file { t.Errorf("expected Files[f1] to be %v, got %v", parser.file, parser.pkg.Files[0]) } // File Type fileTypes := []string{ "TEXT", "DOCUMENTATION", } for _, ty := range fileTypes { err = parser.parsePairFromFile("FileType", ty) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, typeWant := range fileTypes { flagFound := false for _, typeCheck := range parser.file.FileTypes { if typeWant == typeCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileTypes", typeWant) } } if len(fileTypes) != len(parser.file.FileTypes) { t.Errorf("expected %d types in FileTypes, got %d", len(fileTypes), len(parser.file.FileTypes)) } testChecksums := map[common.ChecksumAlgorithm]string{ "MD5": "624c1abb3664f4b35547e7c73864ad24", "SHA1": "85ed0817af83a24ad8da68c2b5094de69833983c", "SHA256": "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", "SHA512": "4ced3267f5ed38df65ceebc43e97aa6c2948cc7ef3288c2e5074e7df7fab544cc93339604513ea5f65616f9ed1c48581465043c8a9b693ef20fd4fddaf25e1b9", } for algo, tc := range testChecksums { if err := parser.parsePairFromFile( "FileChecksum", fmt.Sprintf("%s: %s", algo, tc)); err != nil { t.Errorf("expected error, got %v", err) } } for _, checksum := range parser.file.Checksums { if checksum.Value != testChecksums[checksum.Algorithm] { t.Errorf( "expected %s for FileChecksum%s, got %s", testChecksums[checksum.Algorithm], checksum.Algorithm, checksum.Value, ) } } // Concluded License err = parser.parsePairFromFile("LicenseConcluded", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.LicenseConcluded != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for LicenseConcluded", parser.file.LicenseConcluded) } // License Information in File lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromFile("LicenseInfoInFile", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.file.LicenseInfoInFiles { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseInfoInFiles", licWant) } } if len(lics) != len(parser.file.LicenseInfoInFiles) { t.Errorf("expected %d licenses in LicenseInfoInFiles, got %d", len(lics), len(parser.file.LicenseInfoInFiles)) } // Comments on License err = parser.parsePairFromFile("LicenseComments", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.LicenseComments != "this is a comment" { t.Errorf("got %v for LicenseComments", parser.file.LicenseComments) } // Copyright Text err = parser.parsePairFromFile("FileCopyrightText", "copyright (c) me") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileCopyrightText != "copyright (c) me" { t.Errorf("got %v for FileCopyrightText", parser.file.FileCopyrightText) } // Artifact of Projects: Name, HomePage and URI // Artifact set 1 err = parser.parsePairFromFile("ArtifactOfProjectName", "project1") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/1/") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/1/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 2 -- just name err = parser.parsePairFromFile("ArtifactOfProjectName", "project2") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 3 -- just name and home page err = parser.parsePairFromFile("ArtifactOfProjectName", "project3") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/3/") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 4 -- just name and URI err = parser.parsePairFromFile("ArtifactOfProjectName", "project4") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/4/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.file.ArtifactOfProjects) != 4 { t.Fatalf("expected len %d, got %d", 4, len(parser.file.ArtifactOfProjects)) } aop := parser.file.ArtifactOfProjects[0] if aop.Name != "project1" { t.Errorf("expected %v, got %v", "project1", aop.Name) } if aop.HomePage != "http://example.com/1/" { t.Errorf("expected %v, got %v", "http://example.com/1/", aop.HomePage) } if aop.URI != "http://example.com/1/uri.whatever" { t.Errorf("expected %v, got %v", "http://example.com/1/uri.whatever", aop.URI) } aop = parser.file.ArtifactOfProjects[1] if aop.Name != "project2" { t.Errorf("expected %v, got %v", "project2", aop.Name) } if aop.HomePage != "" { t.Errorf("expected %v, got %v", "", aop.HomePage) } if aop.URI != "" { t.Errorf("expected %v, got %v", "", aop.URI) } aop = parser.file.ArtifactOfProjects[2] if aop.Name != "project3" { t.Errorf("expected %v, got %v", "project3", aop.Name) } if aop.HomePage != "http://example.com/3/" { t.Errorf("expected %v, got %v", "http://example.com/3/", aop.HomePage) } if aop.URI != "" { t.Errorf("expected %v, got %v", "", aop.URI) } aop = parser.file.ArtifactOfProjects[3] if aop.Name != "project4" { t.Errorf("expected %v, got %v", "project4", aop.Name) } if aop.HomePage != "" { t.Errorf("expected %v, got %v", "", aop.HomePage) } if aop.URI != "http://example.com/4/uri.whatever" { t.Errorf("expected %v, got %v", "http://example.com/4/uri.whatever", aop.URI) } // File Comment err = parser.parsePairFromFile("FileComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileComment != "this is a comment" { t.Errorf("got %v for FileComment", parser.file.FileComment) } // File Notice err = parser.parsePairFromFile("FileNotice", "this is a Notice") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileNotice != "this is a Notice" { t.Errorf("got %v for FileNotice", parser.file.FileNotice) } // File Contributor contribs := []string{ "John Doe jdoe@example.com", "EvilCorp", } for _, contrib := range contribs { err = parser.parsePairFromFile("FileContributor", contrib) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, contribWant := range contribs { flagFound := false for _, contribCheck := range parser.file.FileContributors { if contribWant == contribCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileContributors", contribWant) } } if len(contribs) != len(parser.file.FileContributors) { t.Errorf("expected %d contribenses in FileContributors, got %d", len(contribs), len(parser.file.FileContributors)) } // File Dependencies deps := []string{ "f-1.txt", "g.txt", } for _, dep := range deps { err = parser.parsePairFromFile("FileDependency", dep) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, depWant := range deps { flagFound := false for _, depCheck := range parser.file.FileDependencies { if depWant == depCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileDependency", depWant) } } if len(deps) != len(parser.file.FileDependencies) { t.Errorf("expected %d depenses in FileDependency, got %d", len(deps), len(parser.file.FileDependencies)) } // File Attribution Texts attrs := []string{ "Include this notice in all advertising materials", "This is a \nmulti-line string", } for _, attr := range attrs { err = parser.parsePairFromFile("FileAttributionText", attr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, attrWant := range attrs { flagFound := false for _, attrCheck := range parser.file.FileAttributionTexts { if attrWant == attrCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileAttributionText", attrWant) } } if len(attrs) != len(parser.file.FileAttributionTexts) { t.Errorf("expected %d attribution texts in FileAttributionTexts, got %d", len(attrs), len(parser.file.FileAttributionTexts)) } } func TestParserFileCreatesRelationshipInDocument(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserFileCreatesAnnotationInDocument(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } func TestParserFileUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromFile("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestFileAOPPointerChangesAfterTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromFile("ArtifactOfProjectName", "project1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP == nil { t.Errorf("expected non-nil AOP pointer, got nil") } curPtr := parser.fileAOP // now, a home page; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/1/") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != curPtr { t.Errorf("expected no change in AOP pointer, was %v, got %v", curPtr, parser.fileAOP) } // a URI; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/1/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != curPtr { t.Errorf("expected no change in AOP pointer, was %v, got %v", curPtr, parser.fileAOP) } // now, another artifact name; pointer should change but be non-nil // now, a home page; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectName", "project2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP == curPtr { t.Errorf("expected change in AOP pointer, got no change") } // finally, an unrelated tag; pointer should go away err = parser.parsePairFromFile("FileComment", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != nil { t.Errorf("expected nil AOP pointer, got %v", parser.fileAOP) } } func TestParserFailsIfInvalidSPDXIDInFileSection(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid SPDX Identifier err = parser.parsePairFromFile("SPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidChecksumFormatInFileSection(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid format for checksum line, missing colon err = parser.parsePairFromFile("FileChecksum", "SHA1 85ed0817af83a24ad8da68c2b5094de69833983c") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownChecksumTypeInFileSection(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // unknown checksum type err = parser.parsePairFromFile("FileChecksum", "Special: 85ed0817af83a24ad8da68c2b5094de69833983c") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfArtifactHomePageBeforeArtifactName(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // artifact home page appears before artifact name err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "https://example.com") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfArtifactURIBeforeArtifactName(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // artifact home page appears before artifact name err = parser.parsePairFromFile("ArtifactOfProjectURI", "https://example.com") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowError(t *testing.T) { // case 1: The previous file (packaged or unpackaged) does not contain spdx ID parser1 := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, file: &v2_2.File{FileName: "FileName"}, } err := parser1.parsePair("FileName", "f2") if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } // case 2: Invalid file with snippet // Last unpackaged file before the snippet start fileName := "f2.txt" sid1 := common.ElementID("s1") parser2 := tvParser{ doc: &v2_2.Document{}, st: psCreationInfo, file: &v2_2.File{FileName: fileName}, } err = parser2.parsePair("SnippetSPDXID", string(sid1)) if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } // case 3: Invalid File without snippets // Last unpackaged file before the package starts // Last file of a package and New package starts parser3 := tvParser{ doc: &v2_2.Document{}, st: psCreationInfo, } fileName = "f3.txt" err = parser3.parsePair("FileName", fileName) if err != nil { t.Errorf("%s", err) } err = parser3.parsePair("PackageName", "p2") if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_other_license.go000066400000000000000000000035361463371440000256320ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *tvParser) parsePairFromOtherLicense(tag string, value string) error { switch tag { // tag for creating new other license section case "LicenseID": parser.otherLic = &v2_2.OtherLicense{} parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.otherLic.LicenseIdentifier = value case "ExtractedText": parser.otherLic.ExtractedText = value case "LicenseName": parser.otherLic.LicenseName = value case "LicenseCrossReference": parser.otherLic.LicenseCrossReferences = append(parser.otherLic.LicenseCrossReferences, value) case "LicenseComment": parser.otherLic.LicenseComment = value // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_2.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_2.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in OtherLicense section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_other_license_test.go000066400000000000000000000306351463371440000266710ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Parser other license section state change tests ===== func TestParserOLStartsNewOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { // create the first other license olid1 := "LicenseRef-Lic11" olname1 := "License 11" parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psOtherLicense, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: olid1, LicenseName: olname1, }, } olic1 := parser.otherLic parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // the Document's OtherLicenses should have this one only if parser.doc.OtherLicenses[0] != olic1 { t.Errorf("Expected other license %v in OtherLicenses[0], got %v", olic1, parser.doc.OtherLicenses[0]) } if parser.doc.OtherLicenses[0].LicenseName != olname1 { t.Errorf("expected other license name %s in OtherLicenses[0], got %s", olname1, parser.doc.OtherLicenses[0].LicenseName) } // now add a new other license olid2 := "LicenseRef-22" olname2 := "License 22" err := parser.parsePair("LicenseID", olid2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and an other license should be created if parser.otherLic == nil { t.Fatalf("parser didn't create new other license") } // also parse the new license's name err = parser.parsePair("LicenseName", olname2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still be correct if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and the other license name should be as expected if parser.otherLic.LicenseName != olname2 { t.Errorf("expected other license name %s, got %s", olname2, parser.otherLic.LicenseName) } // and the Document's Other Licenses should be of size 2 and have these two if len(parser.doc.OtherLicenses) != 2 { t.Fatalf("Expected OtherLicenses to have len 2, got %d", len(parser.doc.OtherLicenses)) } if parser.doc.OtherLicenses[0] != olic1 { t.Errorf("Expected other license %v in OtherLicenses[0], got %v", olic1, parser.doc.OtherLicenses[0]) } if parser.doc.OtherLicenses[0].LicenseIdentifier != olid1 { t.Errorf("expected other license ID %s in OtherLicenses[0], got %s", olid1, parser.doc.OtherLicenses[0].LicenseIdentifier) } if parser.doc.OtherLicenses[0].LicenseName != olname1 { t.Errorf("expected other license name %s in OtherLicenses[0], got %s", olname1, parser.doc.OtherLicenses[0].LicenseName) } if parser.doc.OtherLicenses[1] != parser.otherLic { t.Errorf("Expected other license %v in OtherLicenses[1], got %v", parser.otherLic, parser.doc.OtherLicenses[1]) } if parser.doc.OtherLicenses[1].LicenseIdentifier != olid2 { t.Errorf("expected other license ID %s in OtherLicenses[1], got %s", olid2, parser.doc.OtherLicenses[1].LicenseIdentifier) } if parser.doc.OtherLicenses[1].LicenseName != olname2 { t.Errorf("expected other license name %s in OtherLicenses[1], got %s", olname2, parser.doc.OtherLicenses[1].LicenseName) } } func TestParserOLMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psOtherLicense, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserOtherLicenseStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psOtherLicense, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-whatever", LicenseName: "the whatever license", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserOtherLicenseStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psOtherLicense, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-whatever", LicenseName: "the whatever license", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } func TestParserOLFailsAfterParsingOtherSectionTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psOtherLicense, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // can't go back to old sections err := parser.parsePair("SPDXVersion", "SPDX-2.2") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("PackageName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("FileName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } } // ===== Other License data section tests ===== func TestParserCanParseOtherLicenseTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psOtherLicense, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // License Identifier err := parser.parsePairFromOtherLicense("LicenseID", "LicenseRef-Lic11") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseIdentifier != "LicenseRef-Lic11" { t.Errorf("got %v for LicenseID", parser.otherLic.LicenseIdentifier) } // Extracted Text err = parser.parsePairFromOtherLicense("ExtractedText", "You are permitted to do anything with the software, hooray!") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.ExtractedText != "You are permitted to do anything with the software, hooray!" { t.Errorf("got %v for ExtractedText", parser.otherLic.ExtractedText) } // License Name err = parser.parsePairFromOtherLicense("LicenseName", "License 11") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseName != "License 11" { t.Errorf("got %v for LicenseName", parser.otherLic.LicenseName) } // License Cross Reference crossRefs := []string{ "https://example.com/1", "https://example.com/2", "https://example.com/3", } for _, cr := range crossRefs { err = parser.parsePairFromOtherLicense("LicenseCrossReference", cr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, refWant := range crossRefs { flagFound := false for _, refCheck := range parser.otherLic.LicenseCrossReferences { if refWant == refCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseCrossReferences", refWant) } } if len(crossRefs) != len(parser.otherLic.LicenseCrossReferences) { t.Errorf("expected %d types in LicenseCrossReferences, got %d", len(crossRefs), len(parser.otherLic.LicenseCrossReferences)) } // License Comment err = parser.parsePairFromOtherLicense("LicenseComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseComment != "this is a comment" { t.Errorf("got %v for LicenseComment", parser.otherLic.LicenseComment) } } func TestParserOLUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psOtherLicense, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePairFromOtherLicense("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_package.go000066400000000000000000000166331463371440000244040ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *tvParser) parsePairFromPackage(tag string, value string) error { // expire pkgExtRef for anything other than a comment // (we'll actually handle the comment further below) if tag != "ExternalRefComment" { parser.pkgExtRef = nil } switch tag { case "PackageName": // if package already has a name, create and go on to a new package if parser.pkg == nil || parser.pkg.PackageName != "" { // check if the previous package contained an spdx Id or not if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName) } parser.pkg = &v2_2.Package{ FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, } } parser.pkg.PackageName = value // tag for going on to file section case "FileName": parser.st = psFile return parser.parsePairFromFile(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.pkg.PackageSPDXIdentifier = eID if parser.doc.Packages == nil { parser.doc.Packages = []*v2_2.Package{} } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) case "PackageVersion": parser.pkg.PackageVersion = value case "PackageFileName": parser.pkg.PackageFileName = value case "PackageSupplier": supplier := &common.Supplier{Supplier: value} if value == "NOASSERTION" { parser.pkg.PackageSupplier = supplier break } subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person", "Organization": supplier.Supplier = subvalue supplier.SupplierType = subkey default: return fmt.Errorf("unrecognized PackageSupplier type %v", subkey) } parser.pkg.PackageSupplier = supplier case "PackageOriginator": originator := &common.Originator{Originator: value} if value == "NOASSERTION" { parser.pkg.PackageOriginator = originator break } subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person", "Organization": originator.Originator = subvalue originator.OriginatorType = subkey default: return fmt.Errorf("unrecognized PackageOriginator type %v", subkey) } parser.pkg.PackageOriginator = originator case "PackageDownloadLocation": parser.pkg.PackageDownloadLocation = value case "FilesAnalyzed": parser.pkg.IsFilesAnalyzedTagPresent = true if value == "false" { parser.pkg.FilesAnalyzed = false } else if value == "true" { parser.pkg.FilesAnalyzed = true } case "PackageVerificationCode": parser.pkg.PackageVerificationCode = extractCodeAndExcludes(value) case "PackageChecksum": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if parser.pkg.PackageChecksums == nil { parser.pkg.PackageChecksums = []common.Checksum{} } switch common.ChecksumAlgorithm(subkey) { case common.SHA1, common.SHA224, common.SHA256, common.SHA384, common.SHA512, common.MD2, common.MD4, common.MD5, common.MD6: algorithm := common.ChecksumAlgorithm(subkey) parser.pkg.PackageChecksums = append(parser.pkg.PackageChecksums, common.Checksum{Algorithm: algorithm, Value: subvalue}) default: return fmt.Errorf("got unknown checksum type %s", subkey) } case "PackageHomePage": parser.pkg.PackageHomePage = value case "PackageSourceInfo": parser.pkg.PackageSourceInfo = value case "PackageLicenseConcluded": parser.pkg.PackageLicenseConcluded = value case "PackageLicenseInfoFromFiles": parser.pkg.PackageLicenseInfoFromFiles = append(parser.pkg.PackageLicenseInfoFromFiles, value) case "PackageLicenseDeclared": parser.pkg.PackageLicenseDeclared = value case "PackageLicenseComments": parser.pkg.PackageLicenseComments = value case "PackageCopyrightText": parser.pkg.PackageCopyrightText = value case "PackageSummary": parser.pkg.PackageSummary = value case "PackageDescription": parser.pkg.PackageDescription = value case "PackageComment": parser.pkg.PackageComment = value case "PackageAttributionText": parser.pkg.PackageAttributionTexts = append(parser.pkg.PackageAttributionTexts, value) case "ExternalRef": parser.pkgExtRef = &v2_2.PackageExternalReference{} parser.pkg.PackageExternalReferences = append(parser.pkg.PackageExternalReferences, parser.pkgExtRef) category, refType, locator, err := extractPackageExternalReference(value) if err != nil { return err } parser.pkgExtRef.Category = category parser.pkgExtRef.RefType = refType parser.pkgExtRef.Locator = locator case "ExternalRefComment": if parser.pkgExtRef == nil { return fmt.Errorf("no current ExternalRef found") } parser.pkgExtRef.ExternalRefComment = value // now, expire pkgExtRef anyway because it can have at most one comment parser.pkgExtRef = nil // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_2.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_2.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in Package section", tag) } return nil } // ===== Helper functions ===== func extractCodeAndExcludes(value string) common.PackageVerificationCode { // FIXME this should probably be done using regular expressions instead // split by paren + word "excludes:" sp := strings.SplitN(value, "(excludes:", 2) if len(sp) < 2 { // not found; return the whole string as just the code return common.PackageVerificationCode{Value: value, ExcludedFiles: []string{}} } // if we're here, code is in first part and excludes filename is in // second part, with trailing paren code := strings.TrimSpace(sp[0]) parsedSp := strings.SplitN(sp[1], ")", 2) fileName := strings.TrimSpace(parsedSp[0]) return common.PackageVerificationCode{Value: code, ExcludedFiles: []string{fileName}} } func extractPackageExternalReference(value string) (string, string, string, error) { sp := strings.Split(value, " ") // remove any that are just whitespace keepSp := []string{} for _, s := range sp { ss := strings.TrimSpace(s) if ss != "" { keepSp = append(keepSp, ss) } } // now, should have 3 items and should be able to map them if len(keepSp) != 3 { return "", "", "", fmt.Errorf("expected 3 elements, got %d", len(keepSp)) } return keepSp[0], keepSp[1], keepSp[2], nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_package_test.go000066400000000000000000001075711463371440000254450ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "testing" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Parser package section state change tests ===== func TestParserPackageStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { // create the first package pkgOldName := "p1" parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: pkgOldName, PackageSPDXIdentifier: "p1"}, } pkgOld := parser.pkg parser.doc.Packages = append(parser.doc.Packages, pkgOld) // the Document's Packages should have this one only if parser.doc.Packages[0] != pkgOld { t.Errorf("expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } // now add a new package pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and it should not be pkgOld if parser.pkg == pkgOld { t.Errorf("expected new package, got pkgOld") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should still be of size 1 and have pkgOld only if parser.doc.Packages[0] != pkgOld { t.Errorf("Expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } } func TestParserPackageStartsNewPackageAfterParsingPackageNameTagWhileInUnpackaged(t *testing.T) { // pkg is nil, so that Files appearing before the first PackageName tag // are added to Files instead of Packages parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psFile, pkg: nil, } // the Document's Packages should be empty if len(parser.doc.Packages) != 0 { t.Errorf("Expected zero packages, got %d", len(parser.doc.Packages)) } // now add a new package pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should be of size 0, because the prior was // unpackaged files and this one won't be added until an SPDXID is seen if len(parser.doc.Packages) != 0 { t.Errorf("Expected %v packages in doc, got %v", 0, len(parser.doc.Packages)) } } func TestParserPackageMovesToFileAfterParsingFileNameTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) pkgCurrent := parser.pkg err := parser.parsePair("FileName", "testFile") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } // and current package should remain what it was if parser.pkg != pkgCurrent { t.Fatalf("expected package to remain %v, got %v", pkgCurrent, parser.pkg) } } func TestParserPackageMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserPackageMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserPackageStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } } func TestParserPackageStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this package") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } } // ===== Package data section tests ===== func TestParserCanParsePackageTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // should not yet be in Packages map, b/c no SPDX identifier if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } // Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageName != "p1" { t.Errorf("got %v for PackageName", parser.pkg.PackageName) } // still should not yet be in Packages map, b/c no SPDX identifier if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } // Package SPDX Identifier err = parser.parsePairFromPackage("SPDXID", "SPDXRef-p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // "SPDXRef-" prefix should be removed from the item if parser.pkg.PackageSPDXIdentifier != "p1" { t.Errorf("got %v for PackageSPDXIdentifier", parser.pkg.PackageSPDXIdentifier) } // and it should now be added to the Packages map if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != parser.pkg { t.Errorf("expected to point to parser.pkg, got %v", parser.doc.Packages[0]) } // Package Version err = parser.parsePairFromPackage("PackageVersion", "2.1.1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVersion != "2.1.1" { t.Errorf("got %v for PackageVersion", parser.pkg.PackageVersion) } // Package File Name err = parser.parsePairFromPackage("PackageFileName", "p1-2.1.1.tar.gz") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageFileName != "p1-2.1.1.tar.gz" { t.Errorf("got %v for PackageFileName", parser.pkg.PackageFileName) } // Package Supplier // SKIP -- separate tests for subvalues below // Package Originator // SKIP -- separate tests for subvalues below // Package Download Location err = parser.parsePairFromPackage("PackageDownloadLocation", "https://example.com/whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageDownloadLocation != "https://example.com/whatever" { t.Errorf("got %v for PackageDownloadLocation", parser.pkg.PackageDownloadLocation) } // Files Analyzed err = parser.parsePairFromPackage("FilesAnalyzed", "false") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.FilesAnalyzed != false { t.Errorf("got %v for FilesAnalyzed", parser.pkg.FilesAnalyzed) } if parser.pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("got %v for IsFilesAnalyzedTagPresent", parser.pkg.IsFilesAnalyzedTagPresent) } // Package Verification Code // SKIP -- separate tests for "excludes", or not, below testChecksums := map[common.ChecksumAlgorithm]string{ "MD5": "624c1abb3664f4b35547e7c73864ad24", "SHA1": "85ed0817af83a24ad8da68c2b5094de69833983c", "SHA256": "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", "SHA512": "4ced3267f5ed38df65ceebc43e97aa6c2948cc7ef3288c2e5074e7df7fab544cc93339604513ea5f65616f9ed1c48581465043c8a9b693ef20fd4fddaf25e1b9", } for algo, tc := range testChecksums { if err := parser.parsePairFromPackage( "PackageChecksum", fmt.Sprintf("%s: %s", algo, tc)); err != nil { t.Errorf("expected error, got %v", err) } } for _, checksum := range parser.pkg.PackageChecksums { if checksum.Value != testChecksums[checksum.Algorithm] { t.Errorf( "expected %s for PackageChecksum%s, got %s", testChecksums[checksum.Algorithm], checksum.Algorithm, checksum.Value, ) } } // Package Home Page err = parser.parsePairFromPackage("PackageHomePage", "https://example.com/whatever2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageHomePage != "https://example.com/whatever2" { t.Errorf("got %v for PackageHomePage", parser.pkg.PackageHomePage) } // Package Source Info err = parser.parsePairFromPackage("PackageSourceInfo", "random comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSourceInfo != "random comment" { t.Errorf("got %v for PackageSourceInfo", parser.pkg.PackageSourceInfo) } // Package License Concluded err = parser.parsePairFromPackage("PackageLicenseConcluded", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseConcluded != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for PackageLicenseConcluded", parser.pkg.PackageLicenseConcluded) } // All Licenses Info From Files lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromPackage("PackageLicenseInfoFromFiles", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.pkg.PackageLicenseInfoFromFiles { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in PackageLicenseInfoFromFiles", licWant) } } if len(lics) != len(parser.pkg.PackageLicenseInfoFromFiles) { t.Errorf("expected %d licenses in PackageLicenseInfoFromFiles, got %d", len(lics), len(parser.pkg.PackageLicenseInfoFromFiles)) } // Package License Declared err = parser.parsePairFromPackage("PackageLicenseDeclared", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseDeclared != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for PackageLicenseDeclared", parser.pkg.PackageLicenseDeclared) } // Package License Comments err = parser.parsePairFromPackage("PackageLicenseComments", "this is a license comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseComments != "this is a license comment" { t.Errorf("got %v for PackageLicenseComments", parser.pkg.PackageLicenseComments) } // Package Copyright Text err = parser.parsePairFromPackage("PackageCopyrightText", "Copyright (c) me myself and i") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageCopyrightText != "Copyright (c) me myself and i" { t.Errorf("got %v for PackageCopyrightText", parser.pkg.PackageCopyrightText) } // Package Summary err = parser.parsePairFromPackage("PackageSummary", "i wrote this package") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSummary != "i wrote this package" { t.Errorf("got %v for PackageSummary", parser.pkg.PackageSummary) } // Package Description err = parser.parsePairFromPackage("PackageDescription", "i wrote this package a lot") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageDescription != "i wrote this package a lot" { t.Errorf("got %v for PackageDescription", parser.pkg.PackageDescription) } // Package Comment err = parser.parsePairFromPackage("PackageComment", "i scanned this package") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageComment != "i scanned this package" { t.Errorf("got %v for PackageComment", parser.pkg.PackageComment) } // Package Attribution Text attrs := []string{ "Include this notice in all advertising materials", "This is a \nmulti-line string", } for _, attr := range attrs { err = parser.parsePairFromPackage("PackageAttributionText", attr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, attrWant := range attrs { flagFound := false for _, attrCheck := range parser.pkg.PackageAttributionTexts { if attrWant == attrCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in PackageAttributionText", attrWant) } } if len(attrs) != len(parser.pkg.PackageAttributionTexts) { t.Errorf("expected %d attribution texts in PackageAttributionTexts, got %d", len(attrs), len(parser.pkg.PackageAttributionTexts)) } // Package External References and Comments ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" ref1Category := "SECURITY" ref1Type := "cpe23Type" ref1Locator := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" ref1Comment := "this is comment #1" ref2 := "OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3alpha" ref2Category := "OTHER" ref2Type := "LocationRef-acmeforge" ref2Locator := "acmecorp/acmenator/4.1.3alpha" ref2Comment := "this is comment #2" err = parser.parsePairFromPackage("ExternalRef", ref1) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.pkg.PackageExternalReferences) != 1 { t.Errorf("expected 1 external reference, got %d", len(parser.pkg.PackageExternalReferences)) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil pkgExtRef, got nil") } if parser.pkg.PackageExternalReferences[0] == nil { t.Errorf("expected non-nil PackageExternalReferences[0], got nil") } if parser.pkgExtRef != parser.pkg.PackageExternalReferences[0] { t.Errorf("expected pkgExtRef to match PackageExternalReferences[0], got no match") } err = parser.parsePairFromPackage("ExternalRefComment", ref1Comment) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromPackage("ExternalRef", ref2) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.pkg.PackageExternalReferences) != 2 { t.Errorf("expected 2 external references, got %d", len(parser.pkg.PackageExternalReferences)) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil pkgExtRef, got nil") } if parser.pkg.PackageExternalReferences[1] == nil { t.Errorf("expected non-nil PackageExternalReferences[1], got nil") } if parser.pkgExtRef != parser.pkg.PackageExternalReferences[1] { t.Errorf("expected pkgExtRef to match PackageExternalReferences[1], got no match") } err = parser.parsePairFromPackage("ExternalRefComment", ref2Comment) if err != nil { t.Errorf("expected nil error, got %v", err) } // finally, check these values gotRef1 := parser.pkg.PackageExternalReferences[0] if gotRef1.Category != ref1Category { t.Errorf("expected ref1 category to be %s, got %s", gotRef1.Category, ref1Category) } if gotRef1.RefType != ref1Type { t.Errorf("expected ref1 type to be %s, got %s", gotRef1.RefType, ref1Type) } if gotRef1.Locator != ref1Locator { t.Errorf("expected ref1 locator to be %s, got %s", gotRef1.Locator, ref1Locator) } if gotRef1.ExternalRefComment != ref1Comment { t.Errorf("expected ref1 comment to be %s, got %s", gotRef1.ExternalRefComment, ref1Comment) } gotRef2 := parser.pkg.PackageExternalReferences[1] if gotRef2.Category != ref2Category { t.Errorf("expected ref2 category to be %s, got %s", gotRef2.Category, ref2Category) } if gotRef2.RefType != ref2Type { t.Errorf("expected ref2 type to be %s, got %s", gotRef2.RefType, ref2Type) } if gotRef2.Locator != ref2Locator { t.Errorf("expected ref2 locator to be %s, got %s", gotRef2.Locator, ref2Locator) } if gotRef2.ExternalRefComment != ref2Comment { t.Errorf("expected ref2 comment to be %s, got %s", gotRef2.ExternalRefComment, ref2Comment) } } func TestParserCanParsePackageSupplierPersonTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: Person err := parser.parsePairFromPackage("PackageSupplier", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "John Doe" { t.Errorf("got %v for PackageSupplierPerson", parser.pkg.PackageSupplier.Supplier) } } func TestParserCanParsePackageSupplierOrganizationTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: Organization err := parser.parsePairFromPackage("PackageSupplier", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "John Doe, Inc." { t.Errorf("got %v for PackageSupplierOrganization", parser.pkg.PackageSupplier.Supplier) } } func TestParserCanParsePackageSupplierNOASSERTIONTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: NOASSERTION err := parser.parsePairFromPackage("PackageSupplier", "NOASSERTION") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "NOASSERTION" { t.Errorf("got value for Supplier, expected NOASSERTION") } } func TestParserCanParsePackageOriginatorPersonTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: Person err := parser.parsePairFromPackage("PackageOriginator", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "John Doe" { t.Errorf("got %v for PackageOriginator", parser.pkg.PackageOriginator.Originator) } } func TestParserCanParsePackageOriginatorOrganizationTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: Organization err := parser.parsePairFromPackage("PackageOriginator", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "John Doe, Inc." { t.Errorf("got %v for PackageOriginator", parser.pkg.PackageOriginator.Originator) } } func TestParserCanParsePackageOriginatorNOASSERTIONTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: NOASSERTION err := parser.parsePairFromPackage("PackageOriginator", "NOASSERTION") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "NOASSERTION" { t.Errorf("got false for PackageOriginatorNOASSERTION") } } func TestParserCanParsePackageVerificationCodeTagWithExcludes(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Verification Code with excludes parenthetical code := "d6a770ba38583ed4bb4525bd96e50461655d2758" fileName := "./package.spdx" fullCodeValue := "d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx)" err := parser.parsePairFromPackage("PackageVerificationCode", fullCodeValue) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVerificationCode.Value != code { t.Errorf("got %v for PackageVerificationCode", parser.pkg.PackageVerificationCode) } if len(parser.pkg.PackageVerificationCode.ExcludedFiles) != 1 || parser.pkg.PackageVerificationCode.ExcludedFiles[0] != fileName { t.Errorf("got %v for PackageVerificationCodeExcludedFile", parser.pkg.PackageVerificationCode.ExcludedFiles) } } func TestParserCanParsePackageVerificationCodeTagWithoutExcludes(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Verification Code without excludes parenthetical code := "d6a770ba38583ed4bb4525bd96e50461655d2758" err := parser.parsePairFromPackage("PackageVerificationCode", code) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVerificationCode.Value != code { t.Errorf("got %v for PackageVerificationCode", parser.pkg.PackageVerificationCode) } if len(parser.pkg.PackageVerificationCode.ExcludedFiles) != 0 { t.Errorf("got %v for PackageVerificationCodeExcludedFile", parser.pkg.PackageVerificationCode.ExcludedFiles) } } func TestParserPackageExternalRefPointerChangesAfterTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" err := parser.parsePairFromPackage("ExternalRef", ref1) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil external reference pointer, got nil") } // now, a comment; pointer should go away err = parser.parsePairFromPackage("ExternalRefComment", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef != nil { t.Errorf("expected nil external reference pointer, got non-nil") } ref2 := "Other LocationRef-something https://example.com/whatever" err = parser.parsePairFromPackage("ExternalRef", ref2) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil external reference pointer, got nil") } // and some other random tag makes the pointer go away too err = parser.parsePairFromPackage("PackageSummary", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef != nil { t.Errorf("expected nil external reference pointer, got non-nil") } } func TestParserPackageCreatesRelationshipInDocument(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserPackageCreatesAnnotationInDocument(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } func TestParserPackageUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePairFromPackage("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserFailsIfInvalidSPDXIDInPackageSection(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid ID format err = parser.parsePairFromPackage("SPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageSupplierFormat(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid supplier format err = parser.parsePairFromPackage("PackageSupplier", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownPackageSupplierType(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid supplier type err = parser.parsePairFromPackage("PackageSupplier", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageOriginatorFormat(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid originator format err = parser.parsePairFromPackage("PackageOriginator", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownPackageOriginatorType(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid originator type err = parser.parsePairFromPackage("PackageOriginator", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserSetsFilesAnalyzedTagsCorrectly(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // set tag err = parser.parsePairFromPackage("FilesAnalyzed", "true") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.FilesAnalyzed != true { t.Errorf("expected %v, got %v", true, parser.pkg.FilesAnalyzed) } if parser.pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("expected %v, got %v", true, parser.pkg.IsFilesAnalyzedTagPresent) } } func TestParserFailsIfInvalidPackageChecksumFormat(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid checksum format err = parser.parsePairFromPackage("PackageChecksum", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageChecksumType(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid checksum type err = parser.parsePairFromPackage("PackageChecksum", "whoops: blah") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidExternalRefFormat(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid external ref format err = parser.parsePairFromPackage("ExternalRef", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfExternalRefCommentBeforeExternalRef(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // external ref comment before external ref err = parser.parsePairFromPackage("ExternalRefComment", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } // ===== Helper function tests ===== func TestCanCheckAndExtractExcludesFilenameAndCode(t *testing.T) { code := "d6a770ba38583ed4bb4525bd96e50461655d2758" fileName := "./package.spdx" fullCodeValue := "d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx)" gotCode := extractCodeAndExcludes(fullCodeValue) if gotCode.Value != code { t.Errorf("got %v for gotCode", gotCode) } if len(gotCode.ExcludedFiles) != 1 || gotCode.ExcludedFiles[0] != fileName { t.Errorf("got %v for gotFileName", gotCode.ExcludedFiles) } } func TestCanExtractPackageExternalReference(t *testing.T) { ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" category := "SECURITY" refType := "cpe23Type" location := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" gotCategory, gotRefType, gotLocation, err := extractPackageExternalReference(ref1) if err != nil { t.Errorf("got non-nil error: %v", err) } if gotCategory != category { t.Errorf("expected category %s, got %s", category, gotCategory) } if gotRefType != refType { t.Errorf("expected refType %s, got %s", refType, gotRefType) } if gotLocation != location { t.Errorf("expected location %s, got %s", location, gotLocation) } } func TestCanExtractPackageExternalReferenceWithExtraWhitespace(t *testing.T) { ref1 := " SECURITY \t cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* \t " category := "SECURITY" refType := "cpe23Type" location := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" gotCategory, gotRefType, gotLocation, err := extractPackageExternalReference(ref1) if err != nil { t.Errorf("got non-nil error: %v", err) } if gotCategory != category { t.Errorf("expected category %s, got %s", category, gotCategory) } if gotRefType != refType { t.Errorf("expected refType %s, got %s", refType, gotRefType) } if gotLocation != location { t.Errorf("expected location %s, got %s", location, gotLocation) } } func TestFailsPackageExternalRefWithInvalidFormat(t *testing.T) { _, _, _, err := extractPackageExternalReference("whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserPackageWithoutSpdxIdentifierThrowsError(t *testing.T) { // More than one package, the previous package doesn't contain an SPDX ID pkgOldName := "p1" parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psPackage, pkg: &v2_2.Package{PackageName: pkgOldName}, } pkgOld := parser.pkg parser.doc.Packages = append(parser.doc.Packages, pkgOld) // the Document's Packages should have this one only if parser.doc.Packages[0] != pkgOld { t.Errorf("expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err == nil { t.Errorf("package without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_relationship.go000066400000000000000000000024131463371440000255010ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" ) func (parser *tvParser) parsePairForRelationship(tag string, value string) error { if parser.rln == nil { return fmt.Errorf("no relationship struct created in parser rln pointer") } if tag == "Relationship" { // parse the value to see if it's a valid relationship format sp := strings.SplitN(value, " ", -1) // filter out any purely-whitespace items var rp []string for _, v := range sp { v = strings.TrimSpace(v) if v != "" { rp = append(rp, v) } } if len(rp) != 3 { return fmt.Errorf("invalid relationship format for %s", value) } aID, err := extractDocElementID(strings.TrimSpace(rp[0])) if err != nil { return err } parser.rln.RefA = aID parser.rln.Relationship = strings.TrimSpace(rp[1]) // NONE and NOASSERTION are permitted on right side permittedSpecial := []string{"NONE", "NOASSERTION"} bID, err := extractDocElementSpecial(strings.TrimSpace(rp[2]), permittedSpecial) if err != nil { return err } parser.rln.RefB = bID return nil } if tag == "RelationshipComment" { parser.rln.RelationshipComment = value return nil } return fmt.Errorf("received unknown tag %v in Relationship section", tag) } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_relationship_test.go000066400000000000000000000132551463371440000265460ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Relationship section tests ===== func TestParserFailsIfRelationshipNotSet(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairForRelationship("Relationship", "SPDXRef-A CONTAINS SPDXRef-B") if err == nil { t.Errorf("expected error when calling parsePairFromRelationship without setting rln pointer") } } func TestParserFailsIfRelationshipCommentWithoutRelationship(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("RelationshipComment", "comment whatever") if err == nil { t.Errorf("expected error when calling parsePair for RelationshipComment without Relationship first") } } func TestParserCanParseRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Relationship err := parser.parsePair("Relationship", "SPDXRef-something CONTAINS DocumentRef-otherdoc:SPDXRef-something-else") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rln.RefA.DocumentRefID != "" || parser.rln.RefA.ElementRefID != "something" { t.Errorf("got %v for first part of Relationship, expected something", parser.rln.RefA) } if parser.rln.RefB.DocumentRefID != "otherdoc" || parser.rln.RefB.ElementRefID != "something-else" { t.Errorf("got %v for second part of Relationship, expected otherdoc:something-else", parser.rln.RefB) } if parser.rln.Relationship != "CONTAINS" { t.Errorf("got %v for Relationship type, expected CONTAINS", parser.rln.Relationship) } // Relationship Comment cmt := "this is a comment" err = parser.parsePair("RelationshipComment", cmt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rln.RelationshipComment != cmt { t.Errorf("got %v for RelationshipComment, expected %v", parser.rln.RelationshipComment, cmt) } } func TestParserInvalidRelationshipTagsNoValueFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // no items parser.rln = nil err := parser.parsePair("Relationship", "") if err == nil { t.Errorf("expected error for empty items in relationship, got nil") } } func TestParserInvalidRelationshipTagsOneValueFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // one item parser.rln = nil err := parser.parsePair("Relationship", "DESCRIBES") if err == nil { t.Errorf("expected error for only one item in relationship, got nil") } } func TestParserInvalidRelationshipTagsTwoValuesFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // two items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-DOCUMENT DESCRIBES") if err == nil { t.Errorf("expected error for only two items in relationship, got nil") } } func TestParserInvalidRelationshipTagsThreeValuesSucceed(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // three items but with interspersed additional whitespace parser.rln = nil err := parser.parsePair("Relationship", " SPDXRef-DOCUMENT \t DESCRIBES SPDXRef-something-else ") if err != nil { t.Errorf("expected pass for three items in relationship w/ extra whitespace, got: %v", err) } } func TestParserInvalidRelationshipTagsFourValuesFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // four items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-a DESCRIBES SPDXRef-b SPDXRef-c") if err == nil { t.Errorf("expected error for more than three items in relationship, got nil") } } func TestParserInvalidRelationshipTagsInvalidRefIDs(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // four items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-a DESCRIBES b") if err == nil { t.Errorf("expected error for missing SPDXRef- prefix, got nil") } parser.rln = nil err = parser.parsePair("Relationship", "a DESCRIBES SPDXRef-b") if err == nil { t.Errorf("expected error for missing SPDXRef- prefix, got nil") } } func TestParserSpecialValuesValidForRightSideOfRelationship(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // NONE in right side of relationship should pass err := parser.parsePair("Relationship", "SPDXRef-a CONTAINS NONE") if err != nil { t.Errorf("expected nil error for CONTAINS NONE, got %v", err) } // NOASSERTION in right side of relationship should pass err = parser.parsePair("Relationship", "SPDXRef-a CONTAINS NOASSERTION") if err != nil { t.Errorf("expected nil error for CONTAINS NOASSERTION, got %v", err) } // NONE in left side of relationship should fail err = parser.parsePair("Relationship", "NONE CONTAINS SPDXRef-a") if err == nil { t.Errorf("expected non-nil error for NONE CONTAINS, got nil") } // NOASSERTION in left side of relationship should fail err = parser.parsePair("Relationship", "NOASSERTION CONTAINS SPDXRef-a") if err == nil { t.Errorf("expected non-nil error for NOASSERTION CONTAINS, got nil") } } func TestParserFailsToParseUnknownTagInRelationshipSection(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Relationship err := parser.parsePair("Relationship", "SPDXRef-something CONTAINS DocumentRef-otherdoc:SPDXRef-something-else") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid tag err = parser.parsePairForRelationship("blah", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_review.go000066400000000000000000000035541463371440000243100ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *tvParser) parsePairFromReview(tag string, value string) error { switch tag { // tag for creating new review section case "Reviewer": parser.rev = &v2_2.Review{} parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Person" case "Organization": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Organization" case "Tool": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Tool" default: return fmt.Errorf("unrecognized Reviewer type %v", subkey) } case "ReviewDate": parser.rev.ReviewDate = value case "ReviewComment": parser.rev.ReviewComment = value // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_2.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_2.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) default: return fmt.Errorf("received unknown tag %v in Review section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_review_test.go000066400000000000000000000342301463371440000253420ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Parser review section state change tests ===== func TestParserReviewStartsNewReviewAfterParsingReviewerTag(t *testing.T) { // create the first review rev1 := "John Doe" parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{ Reviewer: rev1, ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) r1 := parser.rev // the Document's Reviews should have this one only if len(parser.doc.Reviews) != 1 { t.Errorf("Expected only one review, got %d", len(parser.doc.Reviews)) } if parser.doc.Reviews[0] != r1 { t.Errorf("Expected review %v in Reviews[0], got %v", r1, parser.doc.Reviews[0]) } if parser.doc.Reviews[0].Reviewer != rev1 { t.Errorf("expected review name %s in Reviews[0], got %s", rev1, parser.doc.Reviews[0].Reviewer) } // now add a new review rev2 := "Steve" rp2 := "Person: Steve" err := parser.parsePair("Reviewer", rp2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } // and a review should be created if parser.rev == nil { t.Fatalf("parser didn't create new review") } // and the reviewer's name should be as expected if parser.rev.Reviewer != rev2 { t.Errorf("expected reviewer name %s, got %s", rev2, parser.rev.Reviewer) } // and the Document's reviews should be of size 2 and have these two if len(parser.doc.Reviews) != 2 { t.Fatalf("Expected Reviews to have len 2, got %d", len(parser.doc.Reviews)) } if parser.doc.Reviews[0] != r1 { t.Errorf("Expected review %v in Reviews[0], got %v", r1, parser.doc.Reviews[0]) } if parser.doc.Reviews[0].Reviewer != rev1 { t.Errorf("expected reviewer name %s in Reviews[0], got %s", rev1, parser.doc.Reviews[0].Reviewer) } if parser.doc.Reviews[1] != parser.rev { t.Errorf("Expected review %v in Reviews[1], got %v", parser.rev, parser.doc.Reviews[1]) } if parser.doc.Reviews[1].Reviewer != rev2 { t.Errorf("expected reviewer name %s in Reviews[1], got %s", rev2, parser.doc.Reviews[1].Reviewer) } } func TestParserReviewStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{ Reviewer: "Jane Doe", ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserReviewStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{ Reviewer: "Jane Doe", ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } func TestParserReviewFailsAfterParsingOtherSectionTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // can't go back to old sections err := parser.parsePair("SPDXVersion", "SPDX-2.2") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("PackageName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("FileName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("LicenseID", "LicenseRef-Lic22") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } } // ===== Review data section tests ===== func TestParserCanParseReviewTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer (DEPRECATED) // handled in subsequent subtests // Review Date (DEPRECATED) err := parser.parsePairFromReview("ReviewDate", "2018-09-23T08:30:00Z") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.ReviewDate != "2018-09-23T08:30:00Z" { t.Errorf("got %v for ReviewDate", parser.rev.ReviewDate) } // Review Comment (DEPRECATED) err = parser.parsePairFromReview("ReviewComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.ReviewComment != "this is a comment" { t.Errorf("got %v for ReviewComment", parser.rev.ReviewComment) } } func TestParserCanParseReviewerPersonTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Person err := parser.parsePairFromReview("Reviewer", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "John Doe" { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Person" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserCanParseReviewerOrganizationTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Organization err := parser.parsePairFromReview("Reviewer", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "John Doe, Inc." { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Organization" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserCanParseReviewerToolTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Tool err := parser.parsePairFromReview("Reviewer", "Tool: scannertool - 1.2.12") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "scannertool - 1.2.12" { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Tool" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserFailsIfReviewerInvalidFormat(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, rev: &v2_2.Review{}, } parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("Reviewer", "oops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfReviewerUnknownType(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, rev: &v2_2.Review{}, } parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("Reviewer", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserReviewUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psReview, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &v2_2.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &v2_2.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_snippet.go000066400000000000000000000110161463371440000244610ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strconv" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func (parser *tvParser) parsePairFromSnippet(tag string, value string) error { switch tag { // tag for creating new snippet section case "SnippetSPDXID": // check here whether the file contained an SPDX ID or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.snippet = &v2_2.Snippet{} eID, err := extractElementID(value) if err != nil { return err } // FIXME: how should we handle where not associated with current file? if parser.file != nil { if parser.file.Snippets == nil { parser.file.Snippets = map[common.ElementID]*v2_2.Snippet{} } parser.file.Snippets[eID] = parser.snippet } parser.snippet.SnippetSPDXIdentifier = eID // tag for creating new file section and going back to parsing File case "FileName": parser.st = psFile parser.snippet = nil return parser.parsePairFromFile(tag, value) // tag for creating new package section and going back to parsing Package case "PackageName": parser.st = psPackage parser.file = nil parser.snippet = nil return parser.parsePairFromPackage(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tags for snippet data case "SnippetFromFileSPDXID": deID, err := extractDocElementID(value) if err != nil { return err } parser.snippet.SnippetFromFileSPDXIdentifier = deID.ElementRefID case "SnippetByteRange": byteStart, byteEnd, err := extractSubs(value) if err != nil { return err } bIntStart, err := strconv.Atoi(byteStart) if err != nil { return err } bIntEnd, err := strconv.Atoi(byteEnd) if err != nil { return err } if parser.snippet.Ranges == nil { parser.snippet.Ranges = []common.SnippetRange{} } byteRange := common.SnippetRange{StartPointer: common.SnippetRangePointer{Offset: bIntStart}, EndPointer: common.SnippetRangePointer{Offset: bIntEnd}} parser.snippet.Ranges = append(parser.snippet.Ranges, byteRange) case "SnippetLineRange": lineStart, lineEnd, err := extractSubs(value) if err != nil { return err } lInttStart, err := strconv.Atoi(lineStart) if err != nil { return err } lInttEnd, err := strconv.Atoi(lineEnd) if err != nil { return err } if parser.snippet.Ranges == nil { parser.snippet.Ranges = []common.SnippetRange{} } lineRange := common.SnippetRange{StartPointer: common.SnippetRangePointer{LineNumber: lInttStart}, EndPointer: common.SnippetRangePointer{LineNumber: lInttEnd}} parser.snippet.Ranges = append(parser.snippet.Ranges, lineRange) case "SnippetLicenseConcluded": parser.snippet.SnippetLicenseConcluded = value case "LicenseInfoInSnippet": parser.snippet.LicenseInfoInSnippet = append(parser.snippet.LicenseInfoInSnippet, value) case "SnippetLicenseComments": parser.snippet.SnippetLicenseComments = value case "SnippetCopyrightText": parser.snippet.SnippetCopyrightText = value case "SnippetComment": parser.snippet.SnippetComment = value case "SnippetName": parser.snippet.SnippetName = value case "SnippetAttributionText": parser.snippet.SnippetAttributionTexts = append(parser.snippet.SnippetAttributionTexts, value) // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &v2_2.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &v2_2.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in Snippet section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parse_snippet_test.go000066400000000000000000000567541463371440000255420ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Parser snippet section state change tests ===== func TestParserSnippetStartsNewSnippetAfterParsingSnippetSPDXIDTag(t *testing.T) { // create the first snippet sid1 := common.ElementID("s1") parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{SnippetSPDXIdentifier: sid1}, } s1 := parser.snippet parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets[sid1] = parser.snippet // the File's Snippets should have this one only if len(parser.file.Snippets) != 1 { t.Errorf("Expected len(Snippets) to be 1, got %d", len(parser.file.Snippets)) } if parser.file.Snippets["s1"] != s1 { t.Errorf("Expected snippet %v in Snippets[s1], got %v", s1, parser.file.Snippets["s1"]) } if parser.file.Snippets["s1"].SnippetSPDXIdentifier != sid1 { t.Errorf("expected snippet ID %s in Snippets[s1], got %s", sid1, parser.file.Snippets["s1"].SnippetSPDXIdentifier) } // now add a new snippet err := parser.parsePair("SnippetSPDXID", "SPDXRef-s2") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and a snippet should be created if parser.snippet == nil { t.Fatalf("parser didn't create new snippet") } // and the snippet ID should be as expected if parser.snippet.SnippetSPDXIdentifier != "s2" { t.Errorf("expected snippet ID %s, got %s", "s2", parser.snippet.SnippetSPDXIdentifier) } // and the File's Snippets should be of size 2 and have these two if len(parser.file.Snippets) != 2 { t.Errorf("Expected len(Snippets) to be 2, got %d", len(parser.file.Snippets)) } if parser.file.Snippets["s1"] != s1 { t.Errorf("Expected snippet %v in Snippets[s1], got %v", s1, parser.file.Snippets["s1"]) } if parser.file.Snippets["s1"].SnippetSPDXIdentifier != sid1 { t.Errorf("expected snippet ID %s in Snippets[s1], got %s", sid1, parser.file.Snippets["s1"].SnippetSPDXIdentifier) } if parser.file.Snippets["s2"] != parser.snippet { t.Errorf("Expected snippet %v in Snippets[s2], got %v", parser.snippet, parser.file.Snippets["s2"]) } if parser.file.Snippets["s2"].SnippetSPDXIdentifier != "s2" { t.Errorf("expected snippet ID %s in Snippets[s2], got %s", "s2", parser.file.Snippets["s2"].SnippetSPDXIdentifier) } } func TestParserSnippetStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{SnippetSPDXIdentifier: "s1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet // now add a new package p2Name := "package2" err := parser.parsePair("PackageName", p2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should go back to Package if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new pkg") } // and the package name should be as expected if parser.pkg.PackageName != p2Name { t.Errorf("expected package name %s, got %s", p2Name, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should still be of size 1 b/c no SPDX // identifier has been seen yet if len(parser.doc.Packages) != 1 { t.Errorf("Expected len(Packages) to be 1, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != p1 { t.Errorf("Expected package %v in Packages[package1], got %v", p1, parser.doc.Packages[0]) } if parser.doc.Packages[0].PackageName != "package1" { t.Errorf("expected package name %s in Packages[package1], got %s", "package1", parser.doc.Packages[0].PackageName) } // and the first Package's Files should be of size 1 and have f1 only if len(parser.doc.Packages[0].Files) != 1 { t.Errorf("Expected 1 file in Packages[package1].Files, got %d", len(parser.doc.Packages[0].Files)) } if parser.doc.Packages[0].Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.doc.Packages[0].Files[0]) } if parser.doc.Packages[0].Files[0].FileName != "f1.txt" { t.Errorf("expected file name %s in Files[f1], got %s", "f1.txt", parser.doc.Packages[0].Files[0].FileName) } // and the new Package should have no files if len(parser.pkg.Files) != 0 { t.Errorf("Expected no files in Packages[1].Files, got %d", len(parser.pkg.Files)) } // and the current file should be nil if parser.file != nil { t.Errorf("Expected nil for parser.file, got %v", parser.file) } // and the current snippet should be nil if parser.snippet != nil { t.Errorf("Expected nil for parser.snippet, got %v", parser.snippet) } } func TestParserSnippetMovesToFileAfterParsingFileNameTag(t *testing.T) { f1Name := "f1.txt" parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{SnippetSPDXIdentifier: "s1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet f2Name := "f2.txt" err := parser.parsePair("FileName", f2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and current package should remain what it was if parser.pkg != p1 { t.Fatalf("expected package to remain %v, got %v", p1, parser.pkg) } // and a file should be created if parser.file == nil { t.Fatalf("parser didn't create new file") } // and the file name should be as expected if parser.file.FileName != f2Name { t.Errorf("expected file name %s, got %s", f2Name, parser.file.FileName) } // and the Package's Files should still be of size 1 since we haven't seen // an SPDX identifier yet for this new file if len(parser.pkg.Files) != 1 { t.Errorf("Expected len(Files) to be 1, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != f1Name { t.Errorf("expected file name %s in Files[f1], got %s", f1Name, parser.pkg.Files[0].FileName) } // and the current snippet should be nil if parser.snippet != nil { t.Errorf("Expected nil for parser.snippet, got %v", parser.snippet) } } func TestParserSnippetMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserSnippetMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserSnippetStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } } func TestParserSnippetStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } // ===== Snippet data section tests ===== func TestParserCanParseSnippetTags(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetSPDXIdentifier != "s1" { t.Errorf("got %v for SnippetSPDXIdentifier", parser.snippet.SnippetSPDXIdentifier) } // Snippet from File SPDX Identifier err = parser.parsePairFromSnippet("SnippetFromFileSPDXID", "SPDXRef-f1") if err != nil { t.Errorf("expected nil error, got %v", err) } wantDeID := common.DocElementID{DocumentRefID: "", ElementRefID: common.ElementID("f1")} if parser.snippet.SnippetFromFileSPDXIdentifier != wantDeID.ElementRefID { t.Errorf("got %v for SnippetFromFileSPDXIdentifier", parser.snippet.SnippetFromFileSPDXIdentifier) } // Snippet Byte Range err = parser.parsePairFromSnippet("SnippetByteRange", "20:320") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.Ranges[0].StartPointer.Offset != 20 { t.Errorf("got %v for SnippetByteRangeStart", parser.snippet.Ranges[0].StartPointer.Offset) } if parser.snippet.Ranges[0].EndPointer.Offset != 320 { t.Errorf("got %v for SnippetByteRangeEnd", parser.snippet.Ranges[0].EndPointer.Offset) } // Snippet Line Range err = parser.parsePairFromSnippet("SnippetLineRange", "5:12") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.Ranges[1].StartPointer.LineNumber != 5 { t.Errorf("got %v for SnippetLineRangeStart", parser.snippet.Ranges[1].StartPointer.LineNumber) } if parser.snippet.Ranges[1].EndPointer.LineNumber != 12 { t.Errorf("got %v for SnippetLineRangeEnd", parser.snippet.Ranges[1].EndPointer.LineNumber) } // Snippet Concluded License err = parser.parsePairFromSnippet("SnippetLicenseConcluded", "BSD-3-Clause") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetLicenseConcluded != "BSD-3-Clause" { t.Errorf("got %v for SnippetLicenseConcluded", parser.snippet.SnippetLicenseConcluded) } // License Information in Snippet lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromSnippet("LicenseInfoInSnippet", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.snippet.LicenseInfoInSnippet { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseInfoInSnippet", licWant) } } if len(lics) != len(parser.snippet.LicenseInfoInSnippet) { t.Errorf("expected %d licenses in LicenseInfoInSnippet, got %d", len(lics), len(parser.snippet.LicenseInfoInSnippet)) } // Snippet Comments on License err = parser.parsePairFromSnippet("SnippetLicenseComments", "this is a comment about the licenses") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetLicenseComments != "this is a comment about the licenses" { t.Errorf("got %v for SnippetLicenseComments", parser.snippet.SnippetLicenseComments) } // Snippet Copyright Text err = parser.parsePairFromSnippet("SnippetCopyrightText", "copyright (c) John Doe and friends") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetCopyrightText != "copyright (c) John Doe and friends" { t.Errorf("got %v for SnippetCopyrightText", parser.snippet.SnippetCopyrightText) } // Snippet Comment err = parser.parsePairFromSnippet("SnippetComment", "this is a comment about the snippet") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetComment != "this is a comment about the snippet" { t.Errorf("got %v for SnippetComment", parser.snippet.SnippetComment) } // Snippet Name err = parser.parsePairFromSnippet("SnippetName", "from some other package called abc") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetName != "from some other package called abc" { t.Errorf("got %v for SnippetName", parser.snippet.SnippetName) } // Snippet Attribution Texts attrs := []string{ "Include this notice in all advertising materials", "This is a \nmulti-line string", } for _, attr := range attrs { err = parser.parsePairFromSnippet("SnippetAttributionText", attr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, attrWant := range attrs { flagFound := false for _, attrCheck := range parser.snippet.SnippetAttributionTexts { if attrWant == attrCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in SnippetAttributionText", attrWant) } } if len(attrs) != len(parser.snippet.SnippetAttributionTexts) { t.Errorf("expected %d attribution texts in SnippetAttributionTexts, got %d", len(attrs), len(parser.snippet.SnippetAttributionTexts)) } } func TestParserSnippetUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromSnippet("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserFailsForInvalidSnippetSPDXID(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // invalid Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetFromFileSPDXID(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid From File identifier err = parser.parsePairFromSnippet("SnippetFromFileSPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetByteValues(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid byte formats and values err = parser.parsePairFromSnippet("SnippetByteRange", "200 210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetByteRange", "a:210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetByteRange", "200:a") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetLineValues(t *testing.T) { parser := tvParser{ doc: &v2_2.Document{Packages: []*v2_2.Package{}}, st: psSnippet, pkg: &v2_2.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*v2_2.File{}}, file: &v2_2.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*v2_2.Snippet{}}, snippet: &v2_2.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid byte formats and values err = parser.parsePairFromSnippet("SnippetLineRange", "200 210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetLineRange", "a:210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetLineRange", "200:a") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowErrorWithSnippets(t *testing.T) { // Invalid file with snippet // Last unpackaged file before the snippet starts // Last file of a package and New package starts fileName := "f2.txt" sid1 := common.ElementID("s1") parser2 := tvParser{ doc: &v2_2.Document{}, st: psCreationInfo, file: &v2_2.File{FileName: fileName}, } err := parser2.parsePair("SnippetSPDXID", string(sid1)) if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parser.go000066400000000000000000000061361463371440000231100ustar00rootroot00000000000000// Package parser2v2 contains functions to read, load and parse // SPDX tag-value files, version 2.2. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" "github.com/spdx/tools-golang/tagvalue/reader" ) // ParseTagValues takes a list of (tag, value) pairs, parses it and returns // a pointer to a parsed SPDX Document. func ParseTagValues(tvs []reader.TagValuePair) (*spdx.Document, error) { parser := tvParser{} for _, tv := range tvs { err := parser.parsePair(tv.Tag, tv.Value) if err != nil { return nil, err } } if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return nil, fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId { return nil, fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName) } return parser.doc, nil } func (parser *tvParser) parsePair(tag string, value string) error { switch parser.st { case psStart: return parser.parsePairFromStart(tag, value) case psCreationInfo: return parser.parsePairFromCreationInfo(tag, value) case psPackage: return parser.parsePairFromPackage(tag, value) case psFile: return parser.parsePairFromFile(tag, value) case psSnippet: return parser.parsePairFromSnippet(tag, value) case psOtherLicense: return parser.parsePairFromOtherLicense(tag, value) case psReview: return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("parser state %v not recognized when parsing (%s, %s)", parser.st, tag, value) } } func (parser *tvParser) parsePairFromStart(tag string, value string) error { // fail if not in Start parser state if parser.st != psStart { return fmt.Errorf("got invalid state %v in parsePairFromStart", parser.st) } // create an SPDX Document data struct if we don't have one already if parser.doc == nil { parser.doc = &spdx.Document{ExternalDocumentReferences: []spdx.ExternalDocumentRef{}} } switch tag { case "DocumentComment": parser.doc.DocumentComment = value case "SPDXVersion": parser.doc.SPDXVersion = value case "DataLicense": parser.doc.DataLicense = value case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.doc.SPDXIdentifier = eID case "DocumentName": parser.doc.DocumentName = value case "DocumentNamespace": parser.doc.DocumentNamespace = value case "ExternalDocumentRef": documentRefID, uri, alg, checksum, err := extractExternalDocumentReference(value) if err != nil { return err } edr := spdx.ExternalDocumentRef{ DocumentRefID: documentRefID, URI: uri, Checksum: common.Checksum{Algorithm: common.ChecksumAlgorithm(alg), Value: checksum}, } parser.doc.ExternalDocumentReferences = append(parser.doc.ExternalDocumentReferences, edr) default: // move to Creation Info parser state parser.st = psCreationInfo return parser.parsePairFromCreationInfo(tag, value) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/parser_test.go000066400000000000000000000056231463371440000241470ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" "github.com/spdx/tools-golang/tagvalue/reader" ) // ===== Parser exported entry point tests ===== func TestParserCanParseTagValues(t *testing.T) { var tvPairs []reader.TagValuePair // create some pairs tvPair1 := reader.TagValuePair{Tag: "SPDXVersion", Value: "SPDX-2.2"} tvPairs = append(tvPairs, tvPair1) tvPair2 := reader.TagValuePair{Tag: "DataLicense", Value: spdx.DataLicense} tvPairs = append(tvPairs, tvPair2) tvPair3 := reader.TagValuePair{Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"} tvPairs = append(tvPairs, tvPair3) // now parse them doc, err := ParseTagValues(tvPairs) if err != nil { t.Errorf("got error when calling ParseTagValues: %v", err) } if doc.SPDXVersion != "SPDX-2.2" { t.Errorf("expected SPDXVersion to be SPDX-2.2, got %v", doc.SPDXVersion) } if doc.DataLicense != spdx.DataLicense { t.Errorf("expected DataLicense to be CC0-1.0, got %v", doc.DataLicense) } if doc.SPDXIdentifier != "DOCUMENT" { t.Errorf("expected SPDXIdentifier to be DOCUMENT, got %v", doc.SPDXIdentifier) } } // ===== Parser initialization tests ===== func TestParserInitCreatesResetStatus(t *testing.T) { parser := tvParser{} if parser.st != psStart { t.Errorf("parser did not begin in start state") } if parser.doc != nil { t.Errorf("parser did not begin with nil document") } } func TestParserHasDocumentAfterCallToParseFirstTag(t *testing.T) { parser := tvParser{} err := parser.parsePair("SPDXVersion", "SPDX-2.2") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.doc == nil { t.Errorf("doc is still nil after parsing first pair") } } func TestParserStartFailsToParseIfInInvalidState(t *testing.T) { parser := tvParser{st: psReview} err := parser.parsePairFromStart("SPDXVersion", "SPDX-2.2") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowErrorAtCompleteParse(t *testing.T) { // case: Checks the last file // Last unpackaged file with no packages in doc // Last file of last package in the doc tvPairs := []reader.TagValuePair{ {Tag: "SPDXVersion", Value: "SPDX-2.2"}, {Tag: "DataLicense", Value: spdx.DataLicense}, {Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"}, {Tag: "FileName", Value: "f1"}, } _, err := ParseTagValues(tvPairs) if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } } func TestParserPackageWithoutSpdxIdThrowErrorAtCompleteParse(t *testing.T) { // case: Checks the last package tvPairs := []reader.TagValuePair{ {Tag: "SPDXVersion", Value: "SPDX-2.2"}, {Tag: "DataLicense", Value: spdx.DataLicense}, {Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"}, {Tag: "PackageName", Value: "p1"}, } _, err := ParseTagValues(tvPairs) if err == nil { t.Errorf("package without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/types.go000066400000000000000000000022771463371440000227620ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_2" ) type tvParser struct { // document into which data is being parsed doc *v2_2.Document // current parser state st tvParserState // current SPDX item being filled in, if any pkg *v2_2.Package pkgExtRef *v2_2.PackageExternalReference file *v2_2.File fileAOP *v2_2.ArtifactOfProject snippet *v2_2.Snippet otherLic *v2_2.OtherLicense rln *v2_2.Relationship ann *v2_2.Annotation rev *v2_2.Review // don't need creation info pointer b/c only one, // and we can get to it via doc.CreationInfo } // parser state (SPDX document version 2.2) type tvParserState int const ( // at beginning of document psStart tvParserState = iota // in document creation info section psCreationInfo // in package data section psPackage // in file data section (including "unpackaged" files) psFile // in snippet data section (including "unpackaged" files) psSnippet // in other license section psOtherLicense // in review section psReview ) const nullSpdxElementId = common.ElementID("") tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/util.go000066400000000000000000000100371463371440000225640ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" ) // used to extract key / value from embedded substrings // returns subkey, subvalue, nil if no error, or "", "", error otherwise func extractSubs(value string) (string, string, error) { // parse the value to see if it's a valid subvalue format sp := strings.SplitN(value, ":", 2) if len(sp) == 1 { return "", "", fmt.Errorf("invalid subvalue format for %s (no colon found)", value) } subkey := strings.TrimSpace(sp[0]) subvalue := strings.TrimSpace(sp[1]) return subkey, subvalue, nil } // used to extract DocumentRef and SPDXRef values from an SPDX Identifier // which can point either to this document or to a different one func extractDocElementID(value string) (common.DocElementID, error) { docRefID := "" idStr := value // check prefix to see if it's a DocumentRef ID if strings.HasPrefix(idStr, "DocumentRef-") { // extract the part that comes between "DocumentRef-" and ":" strs := strings.Split(idStr, ":") // should be exactly two, part before and part after if len(strs) < 2 { return common.DocElementID{}, fmt.Errorf("no colon found although DocumentRef- prefix present") } if len(strs) > 2 { return common.DocElementID{}, fmt.Errorf("more than one colon found") } // trim the prefix and confirm non-empty docRefID = strings.TrimPrefix(strs[0], "DocumentRef-") if docRefID == "" { return common.DocElementID{}, fmt.Errorf("document identifier has nothing after prefix") } // and use remainder for element ID parsing idStr = strs[1] } // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(idStr, "SPDXRef-") { return common.DocElementID{}, fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(idStr, ":") { // we know this means there was no DocumentRef- prefix, because // we would have handled multiple colons above if it was return common.DocElementID{}, fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(idStr, "SPDXRef-") if eltRefID == "" { return common.DocElementID{}, fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.DocElementID{DocumentRefID: docRefID, ElementRefID: common.ElementID(eltRefID)}, nil } // used to extract SPDXRef values from an SPDX Identifier, OR "special" strings // from a specified set of permitted values. The primary use case for this is // the right-hand side of Relationships, where beginning in SPDX 2.2 the values // "NONE" and "NOASSERTION" are permitted. If the value does not match one of // the specified permitted values, it will fall back to the ordinary // DocElementID extractor. func extractDocElementSpecial(value string, permittedSpecial []string) (common.DocElementID, error) { // check value against special set first for _, sp := range permittedSpecial { if sp == value { return common.DocElementID{SpecialID: sp}, nil } } // not found, fall back to regular search return extractDocElementID(value) } // used to extract SPDXRef values only from an SPDX Identifier which can point // to this document only. Use extractDocElementID for parsing IDs that can // refer either to this document or a different one. func extractElementID(value string) (common.ElementID, error) { // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(value, "SPDXRef-") { return common.ElementID(""), fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(value, ":") { return common.ElementID(""), fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(value, "SPDXRef-") if eltRefID == "" { return common.ElementID(""), fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.ElementID(eltRefID), nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/reader/util_test.go000066400000000000000000000145021463371440000236240ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" ) // ===== Helper function tests ===== func TestCanExtractSubvalues(t *testing.T) { subkey, subvalue, err := extractSubs("SHA1: abc123") if err != nil { t.Errorf("got error when calling extractSubs: %v", err) } if subkey != "SHA1" { t.Errorf("got %v for subkey", subkey) } if subvalue != "abc123" { t.Errorf("got %v for subvalue", subvalue) } } func TestReturnsErrorForInvalidSubvalueFormat(t *testing.T) { _, _, err := extractSubs("blah") if err == nil { t.Errorf("expected error when calling extractSubs for invalid format (0 colons), got nil") } } func TestCanExtractDocumentAndElementRefsFromID(t *testing.T) { // test with valid ID in this document helperForExtractDocElementID(t, "SPDXRef-file1", false, "", "file1") // test with valid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file2", false, "doc2", "file2") // test with invalid ID in this document helperForExtractDocElementID(t, "a:SPDXRef-file1", true, "", "") helperForExtractDocElementID(t, "file1", true, "", "") helperForExtractDocElementID(t, "SPDXRef-", true, "", "") helperForExtractDocElementID(t, "SPDXRef-file1:", true, "", "") // test with invalid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:a", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:SPDXRef-file1", true, "", "") // test with invalid formats helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file1:file2", true, "", "") } func helperForExtractDocElementID(t *testing.T, tst string, wantErr bool, wantDoc string, wantElt string) { deID, err := extractDocElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if deID.DocumentRefID != wantDoc { if wantDoc == "" { t.Errorf("testing %v: want empty string for DocumentRefID, got %v", tst, deID.DocumentRefID) } else { t.Errorf("testing %v: want %v for DocumentRefID, got %v", tst, wantDoc, deID.DocumentRefID) } } if deID.ElementRefID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want empty string for ElementRefID, got %v", tst, deID.ElementRefID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, deID.ElementRefID) } } } func TestCanExtractSpecialDocumentIDs(t *testing.T) { permittedSpecial := []string{"NONE", "NOASSERTION"} // test with valid special values helperForExtractDocElementSpecial(t, permittedSpecial, "NONE", false, "", "", "NONE") helperForExtractDocElementSpecial(t, permittedSpecial, "NOASSERTION", false, "", "", "NOASSERTION") // test with valid regular IDs helperForExtractDocElementSpecial(t, permittedSpecial, "SPDXRef-file1", false, "", "file1", "") helperForExtractDocElementSpecial(t, permittedSpecial, "DocumentRef-doc2:SPDXRef-file2", false, "doc2", "file2", "") helperForExtractDocElementSpecial(t, permittedSpecial, "a:SPDXRef-file1", true, "", "", "") helperForExtractDocElementSpecial(t, permittedSpecial, "DocumentRef-doc2", true, "", "", "") // test with invalid other words not on permitted list helperForExtractDocElementSpecial(t, permittedSpecial, "FOO", true, "", "", "") } func helperForExtractDocElementSpecial(t *testing.T, permittedSpecial []string, tst string, wantErr bool, wantDoc string, wantElt string, wantSpecial string) { deID, err := extractDocElementSpecial(tst, permittedSpecial) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if deID.DocumentRefID != wantDoc { if wantDoc == "" { t.Errorf("testing %v: want empty string for DocumentRefID, got %v", tst, deID.DocumentRefID) } else { t.Errorf("testing %v: want %v for DocumentRefID, got %v", tst, wantDoc, deID.DocumentRefID) } } if deID.ElementRefID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want empty string for ElementRefID, got %v", tst, deID.ElementRefID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, deID.ElementRefID) } } if deID.SpecialID != wantSpecial { if wantSpecial == "" { t.Errorf("testing %v: want empty string for SpecialID, got %v", tst, deID.SpecialID) } else { t.Errorf("testing %v: want %v for SpecialID, got %v", tst, wantSpecial, deID.SpecialID) } } } func TestCanExtractElementRefsOnlyFromID(t *testing.T) { // test with valid ID in this document helperForExtractElementID(t, "SPDXRef-file1", false, "file1") // test with valid ID in another document helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-file2", true, "") // test with invalid ID in this document helperForExtractElementID(t, "a:SPDXRef-file1", true, "") helperForExtractElementID(t, "file1", true, "") helperForExtractElementID(t, "SPDXRef-", true, "") helperForExtractElementID(t, "SPDXRef-file1:", true, "") // test with invalid ID in another document helperForExtractElementID(t, "DocumentRef-doc2", true, "") helperForExtractElementID(t, "DocumentRef-doc2:", true, "") helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-", true, "") helperForExtractElementID(t, "DocumentRef-doc2:a", true, "") helperForExtractElementID(t, "DocumentRef-:", true, "") helperForExtractElementID(t, "DocumentRef-:SPDXRef-file1", true, "") } func helperForExtractElementID(t *testing.T, tst string, wantErr bool, wantElt string) { eID, err := extractElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if eID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, eID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, eID) } } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/000077500000000000000000000000001463371440000213315ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_annotation.go000066400000000000000000000016011463371440000250460ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func renderAnnotation(ann *spdx.Annotation, w io.Writer) error { if ann.Annotator.Annotator != "" && ann.Annotator.AnnotatorType != "" { fmt.Fprintf(w, "Annotator: %s: %s\n", ann.Annotator.AnnotatorType, ann.Annotator.Annotator) } if ann.AnnotationDate != "" { fmt.Fprintf(w, "AnnotationDate: %s\n", ann.AnnotationDate) } if ann.AnnotationType != "" { fmt.Fprintf(w, "AnnotationType: %s\n", ann.AnnotationType) } annIDStr := common.RenderDocElementID(ann.AnnotationSPDXIdentifier) if annIDStr != "SPDXRef-" { fmt.Fprintf(w, "SPDXREF: %s\n", annIDStr) } if ann.AnnotationComment != "" { fmt.Fprintf(w, "AnnotationComment: %s\n", textify(ann.AnnotationComment)) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_annotation_test.go000066400000000000000000000065211463371440000261130ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Annotation section Saver tests ===== func TestSaverAnnotationSavesTextForPerson(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Person", Annotator: "John Doe"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Person: John Doe AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverAnnotationSavesTextForOrganization(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Organization", Annotator: "John Doe, Inc."}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Organization: John Doe, Inc. AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverAnnotationSavesTextForTool(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Tool", Annotator: "magictool-1.1"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Tool: magictool-1.1 AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } // note that the annotation has no optional or multiple fields tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_creation_info.go000066400000000000000000000012771463371440000255240ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func renderCreationInfo(ci *spdx.CreationInfo, w io.Writer) error { if ci.LicenseListVersion != "" { fmt.Fprintf(w, "LicenseListVersion: %s\n", ci.LicenseListVersion) } for _, creator := range ci.Creators { fmt.Fprintf(w, "Creator: %s: %s\n", creator.CreatorType, creator.Creator) } if ci.Created != "" { fmt.Fprintf(w, "Created: %s\n", ci.Created) } if ci.CreatorComment != "" { fmt.Fprintf(w, "CreatorComment: %s\n", textify(ci.CreatorComment)) } // add blank newline b/c end of a main section fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_creation_info_test.go000066400000000000000000000057331463371440000265640ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Creation Info section Saver tests ===== func TestSaverCISavesText(t *testing.T) { ci := &spdx.CreationInfo{ LicenseListVersion: "3.9", Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, {Creator: "Jane Doe (janedoe@example.com)", CreatorType: "Person"}, {Creator: "John Doe, Inc.", CreatorType: "Organization"}, {Creator: "Jane Doe LLC", CreatorType: "Organization"}, {Creator: "magictool1-1.0", CreatorType: "Tool"}, {Creator: "magictool2-1.0", CreatorType: "Tool"}, {Creator: "magictool3-1.0", CreatorType: "Tool"}, }, Created: "2018-10-10T06:20:00Z", CreatorComment: "this is a creator comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseListVersion: 3.9 Creator: Person: John Doe Creator: Person: Jane Doe (janedoe@example.com) Creator: Organization: John Doe, Inc. Creator: Organization: Jane Doe LLC Creator: Tool: magictool1-1.0 Creator: Tool: magictool2-1.0 Creator: Tool: magictool3-1.0 Created: 2018-10-10T06:20:00Z CreatorComment: this is a creator comment `) // render as buffer of bytes var got bytes.Buffer err := renderCreationInfo(ci, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverCIOmitsOptionalFieldsIfEmpty(t *testing.T) { // --- need at least one creator; do first for Persons --- ci1 := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, }, Created: "2018-10-10T06:20:00Z", } // what we want to get, as a buffer of bytes want1 := bytes.NewBufferString(`Creator: Person: John Doe Created: 2018-10-10T06:20:00Z `) // render as buffer of bytes var got1 bytes.Buffer err := renderCreationInfo(ci1, &got1) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c1 := bytes.Compare(want1.Bytes(), got1.Bytes()) if c1 != 0 { t.Errorf("Expected %v, got %v", want1.String(), got1.String()) } // --- need at least one creator; now switch to organization --- ci2 := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe, Inc.", CreatorType: "Organization"}, }, Created: "2018-10-10T06:20:00Z", } // what we want to get, as a buffer of bytes want2 := bytes.NewBufferString(`Creator: Organization: John Doe, Inc. Created: 2018-10-10T06:20:00Z `) // render as buffer of bytes var got2 bytes.Buffer err = renderCreationInfo(ci2, &got2) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c2 := bytes.Compare(want2.Bytes(), got2.Bytes()) if c2 != 0 { t.Errorf("Expected %v, got %v", want2.String(), got2.String()) } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_document.go000066400000000000000000000057451463371440000245270ustar00rootroot00000000000000// Package saver2v2 contains functions to render and write a tag-value // formatted version of an in-memory SPDX document and its sections // (version 2.2). // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // RenderDocument is the main entry point to take an SPDX in-memory // Document (version 2.2), and render it to the received io.Writer. // It is only exported in order to be available to the tvsaver package, // and typically does not need to be called by client code. func RenderDocument(doc *spdx.Document, w io.Writer) error { if doc.CreationInfo == nil { return fmt.Errorf("Document had nil CreationInfo section") } if doc.SPDXVersion != "" { fmt.Fprintf(w, "SPDXVersion: %s\n", doc.SPDXVersion) } if doc.DataLicense != "" { fmt.Fprintf(w, "DataLicense: %s\n", doc.DataLicense) } if doc.SPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(doc.SPDXIdentifier)) } if doc.DocumentName != "" { fmt.Fprintf(w, "DocumentName: %s\n", doc.DocumentName) } if doc.DocumentNamespace != "" { fmt.Fprintf(w, "DocumentNamespace: %s\n", doc.DocumentNamespace) } // print EDRs in order sorted by identifier sort.Slice(doc.ExternalDocumentReferences, func(i, j int) bool { return doc.ExternalDocumentReferences[i].DocumentRefID < doc.ExternalDocumentReferences[j].DocumentRefID }) for _, edr := range doc.ExternalDocumentReferences { fmt.Fprintf(w, "ExternalDocumentRef: DocumentRef-%s %s %s:%s\n", edr.DocumentRefID, edr.URI, edr.Checksum.Algorithm, edr.Checksum.Value) } if doc.DocumentComment != "" { fmt.Fprintf(w, "DocumentComment: %s\n", textify(doc.DocumentComment)) } renderCreationInfo(doc.CreationInfo, w) if len(doc.Files) > 0 { fmt.Fprintf(w, "##### Unpackaged files\n\n") sort.Slice(doc.Files, func(i, j int) bool { return doc.Files[i].FileSPDXIdentifier < doc.Files[j].FileSPDXIdentifier }) for _, fi := range doc.Files { renderFile(fi, w) } } // sort Packages by identifier sort.Slice(doc.Packages, func(i, j int) bool { return doc.Packages[i].PackageSPDXIdentifier < doc.Packages[j].PackageSPDXIdentifier }) for _, pkg := range doc.Packages { fmt.Fprintf(w, "##### Package: %s\n\n", pkg.PackageName) renderPackage(pkg, w) } if len(doc.OtherLicenses) > 0 { fmt.Fprintf(w, "##### Other Licenses\n\n") for _, ol := range doc.OtherLicenses { renderOtherLicense(ol, w) } } if len(doc.Relationships) > 0 { fmt.Fprintf(w, "##### Relationships\n\n") for _, rln := range doc.Relationships { renderRelationship(rln, w) } fmt.Fprintf(w, "\n") } if len(doc.Annotations) > 0 { fmt.Fprintf(w, "##### Annotations\n\n") for _, ann := range doc.Annotations { renderAnnotation(ann, w) fmt.Fprintf(w, "\n") } } if len(doc.Reviews) > 0 { fmt.Fprintf(w, "##### Reviews\n\n") for _, rev := range doc.Reviews { renderReview(rev, w) } } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_document_test.go000066400000000000000000000240441463371440000255570ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== entire Document Saver tests ===== func TestSaverDocumentSavesText(t *testing.T) { // Creation Info section ci := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, }, Created: "2018-10-10T06:20:00Z", } // unpackaged files f1 := &spdx.File{ FileName: "/tmp/whatever1.txt", FileSPDXIdentifier: common.ElementID("File1231"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983c", Algorithm: common.SHA1}}, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{"Apache-2.0"}, FileCopyrightText: "Copyright (c) Jane Doe", } f2 := &spdx.File{ FileName: "/tmp/whatever2.txt", FileSPDXIdentifier: common.ElementID("File1232"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983d", Algorithm: common.SHA1}}, LicenseConcluded: "MIT", LicenseInfoInFiles: []string{"MIT"}, FileCopyrightText: "Copyright (c) John Doe", } unFiles := []*spdx.File{ f1, f2, } // Package 1: packaged files with snippets sn1 := &spdx.Snippet{ SnippetSPDXIdentifier: "Snippet19", SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "FileHasSnippets").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}}}, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } sn2 := &spdx.Snippet{ SnippetSPDXIdentifier: "Snippet20", SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "FileHasSnippets").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 268}, EndPointer: common.SnippetRangePointer{Offset: 309}}}, SnippetLicenseConcluded: "WTFPL", SnippetCopyrightText: "NOASSERTION", } f3 := &spdx.File{ FileName: "/tmp/file-with-snippets.txt", FileSPDXIdentifier: common.ElementID("FileHasSnippets"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983e", Algorithm: common.SHA1}}, LicenseConcluded: "GPL-2.0-or-later AND WTFPL", LicenseInfoInFiles: []string{ "Apache-2.0", "GPL-2.0-or-later", "WTFPL", }, FileCopyrightText: "Copyright (c) Jane Doe", Snippets: map[common.ElementID]*spdx.Snippet{ common.ElementID("Snippet19"): sn1, common.ElementID("Snippet20"): sn2, }, } f4 := &spdx.File{ FileName: "/tmp/another-file.txt", FileSPDXIdentifier: common.ElementID("FileAnother"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983f", Algorithm: common.SHA1}}, LicenseConcluded: "BSD-3-Clause", LicenseInfoInFiles: []string{"BSD-3-Clause"}, FileCopyrightText: "Copyright (c) Jane Doe LLC", } pkgWith := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageLicenseConcluded: "GPL-2.0-or-later AND BSD-3-Clause AND WTFPL", PackageLicenseInfoFromFiles: []string{ "Apache-2.0", "GPL-2.0-or-later", "WTFPL", "BSD-3-Clause", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", Files: []*spdx.File{ f3, f4, }, } // Other Licenses 1 and 2 ol1 := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", } ol2 := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-2", ExtractedText: `License 2 text - this is a license that does some stuff`, LicenseName: "License 2", } // Relationships rln1 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p1"), Relationship: "DESCRIBES", } rln2 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "File1231"), Relationship: "DESCRIBES", } rln3 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "File1232"), Relationship: "DESCRIBES", } // Annotations ann1 := &spdx.Annotation{ Annotator: common.Annotator{Annotator: "John Doe", AnnotatorType: "Person"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } ann2 := &spdx.Annotation{ Annotator: common.Annotator{Annotator: "John Doe, Inc.", AnnotatorType: "Organization"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "p1"), AnnotationComment: "This is an annotation about Package p1", } // Reviews rev1 := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", } rev2 := &spdx.Review{ Reviewer: "Jane Doe LLC", ReviewerType: "Organization", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: "I have reviewed this SPDX document and it is awesome", } // now, build the document doc := &spdx.Document{ SPDXVersion: "SPDX-2.2", DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), DocumentName: "tools-golang-0.0.1.abcdef", DocumentNamespace: "https://github.com/spdx/spdx-docs/tools-golang/tools-golang-0.0.1.abcdef.whatever", CreationInfo: ci, Packages: []*spdx.Package{ pkgWith, }, Files: unFiles, OtherLicenses: []*spdx.OtherLicense{ ol1, ol2, }, Relationships: []*spdx.Relationship{ rln1, rln2, rln3, }, Annotations: []*spdx.Annotation{ ann1, ann2, }, Reviews: []*spdx.Review{ rev1, rev2, }, } want := bytes.NewBufferString(`SPDXVersion: SPDX-2.2 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: tools-golang-0.0.1.abcdef DocumentNamespace: https://github.com/spdx/spdx-docs/tools-golang/tools-golang-0.0.1.abcdef.whatever Creator: Person: John Doe Created: 2018-10-10T06:20:00Z ##### Unpackaged files FileName: /tmp/whatever1.txt SPDXID: SPDXRef-File1231 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe FileName: /tmp/whatever2.txt SPDXID: SPDXRef-File1232 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983d LicenseConcluded: MIT LicenseInfoInFile: MIT FileCopyrightText: Copyright (c) John Doe ##### Package: p1 PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: true PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 PackageLicenseConcluded: GPL-2.0-or-later AND BSD-3-Clause AND WTFPL PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseInfoFromFiles: WTFPL PackageLicenseInfoFromFiles: BSD-3-Clause PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. FileName: /tmp/another-file.txt SPDXID: SPDXRef-FileAnother FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983f LicenseConcluded: BSD-3-Clause LicenseInfoInFile: BSD-3-Clause FileCopyrightText: Copyright (c) Jane Doe LLC FileName: /tmp/file-with-snippets.txt SPDXID: SPDXRef-FileHasSnippets FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983e LicenseConcluded: GPL-2.0-or-later AND WTFPL LicenseInfoInFile: Apache-2.0 LicenseInfoInFile: GPL-2.0-or-later LicenseInfoInFile: WTFPL FileCopyrightText: Copyright (c) Jane Doe SnippetSPDXID: SPDXRef-Snippet19 SnippetFromFileSPDXID: SPDXRef-FileHasSnippets SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetSPDXID: SPDXRef-Snippet20 SnippetFromFileSPDXID: SPDXRef-FileHasSnippets SnippetByteRange: 268:309 SnippetLicenseConcluded: WTFPL SnippetCopyrightText: NOASSERTION ##### Other Licenses LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 LicenseID: LicenseRef-2 ExtractedText: License 2 text - this is a license that does some stuff LicenseName: License 2 ##### Relationships Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-p1 Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File1231 Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File1232 ##### Annotations Annotator: Person: John Doe AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document Annotator: Organization: John Doe, Inc. AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-p1 AnnotationComment: This is an annotation about Package p1 ##### Reviews Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z Reviewer: Organization: Jane Doe LLC ReviewDate: 2018-10-14T10:28:00Z ReviewComment: I have reviewed this SPDX document and it is awesome `) // render as buffer of bytes var got bytes.Buffer err := RenderDocument(doc, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected {{{%v}}}, got {{{%v}}}", want.String(), got.String()) } } func TestSaverDocumentReturnsErrorIfNilCreationInfo(t *testing.T) { doc := &spdx.Document{} var got bytes.Buffer err := RenderDocument(doc, &got) if err == nil { t.Errorf("Expected error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_file.go000066400000000000000000000041611463371440000236170ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func renderFile(f *spdx.File, w io.Writer) error { if f.FileName != "" { fmt.Fprintf(w, "FileName: %s\n", f.FileName) } if f.FileSPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(f.FileSPDXIdentifier)) } for _, s := range f.FileTypes { fmt.Fprintf(w, "FileType: %s\n", s) } for _, checksum := range f.Checksums { fmt.Fprintf(w, "FileChecksum: %s: %s\n", checksum.Algorithm, checksum.Value) } if f.LicenseConcluded != "" { fmt.Fprintf(w, "LicenseConcluded: %s\n", f.LicenseConcluded) } for _, s := range f.LicenseInfoInFiles { fmt.Fprintf(w, "LicenseInfoInFile: %s\n", s) } if f.LicenseComments != "" { fmt.Fprintf(w, "LicenseComments: %s\n", textify(f.LicenseComments)) } if f.FileCopyrightText != "" { fmt.Fprintf(w, "FileCopyrightText: %s\n", textify(f.FileCopyrightText)) } for _, aop := range f.ArtifactOfProjects { fmt.Fprintf(w, "ArtifactOfProjectName: %s\n", aop.Name) if aop.HomePage != "" { fmt.Fprintf(w, "ArtifactOfProjectHomePage: %s\n", aop.HomePage) } if aop.URI != "" { fmt.Fprintf(w, "ArtifactOfProjectURI: %s\n", aop.URI) } } if f.FileComment != "" { fmt.Fprintf(w, "FileComment: %s\n", textify(f.FileComment)) } if f.FileNotice != "" { fmt.Fprintf(w, "FileNotice: %s\n", textify(f.FileNotice)) } for _, s := range f.FileContributors { fmt.Fprintf(w, "FileContributor: %s\n", s) } for _, s := range f.FileAttributionTexts { fmt.Fprintf(w, "FileAttributionText: %s\n", textify(s)) } for _, s := range f.FileDependencies { fmt.Fprintf(w, "FileDependency: %s\n", s) } fmt.Fprintf(w, "\n") // also render any snippets for this file // get slice of Snippet identifiers so we can sort them snippetKeys := []string{} for k := range f.Snippets { snippetKeys = append(snippetKeys, string(k)) } sort.Strings(snippetKeys) for _, sID := range snippetKeys { s := f.Snippets[common.ElementID(sID)] renderSnippet(s, w) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_file_test.go000066400000000000000000000220341463371440000246550ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== File section Saver tests ===== func TestSaverFileSavesText(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), FileTypes: []string{ "TEXT", "DOCUMENTATION", }, Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, {Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"}, {Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", "Apache-1.1", }, LicenseComments: "this is a license comment(s)", FileCopyrightText: "Copyright (c) Jane Doe", ArtifactOfProjects: []*spdx.ArtifactOfProject{ &spdx.ArtifactOfProject{ Name: "project1", HomePage: "http://example.com/1/", URI: "http://example.com/1/uri.whatever", }, &spdx.ArtifactOfProject{ Name: "project2", }, &spdx.ArtifactOfProject{ Name: "project3", HomePage: "http://example.com/3/", }, &spdx.ArtifactOfProject{ Name: "project4", URI: "http://example.com/4/uri.whatever", }, }, FileComment: "this is a file comment", FileNotice: "This file may be used under either Apache-2.0 or Apache-1.1.", FileContributors: []string{ "John Doe jdoe@example.com", "EvilCorp", }, FileAttributionTexts: []string{ "attributions", `multi-line attribution`, }, FileDependencies: []string{ "f-1.txt", "g.txt", }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileType: TEXT FileType: DOCUMENTATION FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c FileChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd FileChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 LicenseInfoInFile: Apache-1.1 LicenseComments: this is a license comment(s) FileCopyrightText: Copyright (c) Jane Doe ArtifactOfProjectName: project1 ArtifactOfProjectHomePage: http://example.com/1/ ArtifactOfProjectURI: http://example.com/1/uri.whatever ArtifactOfProjectName: project2 ArtifactOfProjectName: project3 ArtifactOfProjectHomePage: http://example.com/3/ ArtifactOfProjectName: project4 ArtifactOfProjectURI: http://example.com/4/uri.whatever FileComment: this is a file comment FileNotice: This file may be used under either Apache-2.0 or Apache-1.1. FileContributor: John Doe jdoe@example.com FileContributor: EvilCorp FileAttributionText: attributions FileAttributionText: multi-line attribution FileDependency: f-1.txt FileDependency: g.txt `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileSavesSnippetsAlso(t *testing.T) { sn1 := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet19"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File123").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}}}, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } sn2 := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet20"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File123").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 268}, EndPointer: common.SnippetRangePointer{Offset: 309}}}, SnippetLicenseConcluded: "WTFPL", SnippetCopyrightText: "NOASSERTION", } sns := map[common.ElementID]*spdx.Snippet{ common.ElementID("Snippet19"): sn1, common.ElementID("Snippet20"): sn2, } f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", Snippets: sns, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe SnippetSPDXID: SPDXRef-Snippet19 SnippetFromFileSPDXID: SPDXRef-File123 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetSPDXID: SPDXRef-Snippet20 SnippetFromFileSPDXID: SPDXRef-File123 SnippetByteRange: 268:309 SnippetLicenseConcluded: WTFPL SnippetCopyrightText: NOASSERTION `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileOmitsOptionalFieldsIfEmpty(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileWrapsCopyrightMultiLine(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: `Copyright (c) Jane Doe Copyright (c) John Doe`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe Copyright (c) John Doe `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileWrapsCommentsAndNoticesMultiLine(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseComments: `this is a multi-line license comment`, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", FileComment: `this is a multi-line file comment`, FileNotice: `This file may be used under either Apache-2.0 or Apache-1.1.`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 LicenseComments: this is a multi-line license comment FileCopyrightText: Copyright (c) Jane Doe FileComment: this is a multi-line file comment FileNotice: This file may be used under either Apache-2.0 or Apache-1.1. `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_other_license.go000066400000000000000000000013421463371440000255210ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func renderOtherLicense(ol *spdx.OtherLicense, w io.Writer) error { if ol.LicenseIdentifier != "" { fmt.Fprintf(w, "LicenseID: %s\n", ol.LicenseIdentifier) } if ol.ExtractedText != "" { fmt.Fprintf(w, "ExtractedText: %s\n", textify(ol.ExtractedText)) } if ol.LicenseName != "" { fmt.Fprintf(w, "LicenseName: %s\n", ol.LicenseName) } for _, s := range ol.LicenseCrossReferences { fmt.Fprintf(w, "LicenseCrossReference: %s\n", s) } if ol.LicenseComment != "" { fmt.Fprintf(w, "LicenseComment: %s\n", textify(ol.LicenseComment)) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_other_license_test.go000066400000000000000000000037461463371440000265720ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Other License section Saver tests ===== func TestSaverOtherLicenseSavesText(t *testing.T) { ol := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", LicenseCrossReferences: []string{ "http://example.com/License1/", "http://example.com/License1AnotherURL/", }, LicenseComment: "this is a license comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 LicenseCrossReference: http://example.com/License1/ LicenseCrossReference: http://example.com/License1AnotherURL/ LicenseComment: this is a license comment `) // render as buffer of bytes var got bytes.Buffer err := renderOtherLicense(ol, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverOtherLicenseOmitsOptionalFieldsIfEmpty(t *testing.T) { ol := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 `) // render as buffer of bytes var got bytes.Buffer err := renderOtherLicense(ol, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_package.go000066400000000000000000000075571463371440000243070ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "strings" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func renderPackage(pkg *spdx.Package, w io.Writer) error { if pkg.PackageName != "" { fmt.Fprintf(w, "PackageName: %s\n", pkg.PackageName) } if pkg.PackageSPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(pkg.PackageSPDXIdentifier)) } if pkg.PackageVersion != "" { fmt.Fprintf(w, "PackageVersion: %s\n", pkg.PackageVersion) } if pkg.PackageFileName != "" { fmt.Fprintf(w, "PackageFileName: %s\n", pkg.PackageFileName) } if pkg.PackageSupplier != nil && pkg.PackageSupplier.Supplier != "" { if pkg.PackageSupplier.SupplierType == "" { fmt.Fprintf(w, "PackageSupplier: %s\n", pkg.PackageSupplier.Supplier) } else { fmt.Fprintf(w, "PackageSupplier: %s: %s\n", pkg.PackageSupplier.SupplierType, pkg.PackageSupplier.Supplier) } } if pkg.PackageOriginator != nil && pkg.PackageOriginator.Originator != "" { if pkg.PackageOriginator.OriginatorType == "" { fmt.Fprintf(w, "PackageOriginator: %s\n", pkg.PackageOriginator.Originator) } else { fmt.Fprintf(w, "PackageOriginator: %s: %s\n", pkg.PackageOriginator.OriginatorType, pkg.PackageOriginator.Originator) } } if pkg.PackageDownloadLocation != "" { fmt.Fprintf(w, "PackageDownloadLocation: %s\n", pkg.PackageDownloadLocation) } if pkg.FilesAnalyzed { if pkg.IsFilesAnalyzedTagPresent { fmt.Fprintf(w, "FilesAnalyzed: true\n") } } else { fmt.Fprintf(w, "FilesAnalyzed: false\n") } if pkg.PackageVerificationCode.Value != "" && pkg.FilesAnalyzed { if len(pkg.PackageVerificationCode.ExcludedFiles) == 0 { fmt.Fprintf(w, "PackageVerificationCode: %s\n", pkg.PackageVerificationCode.Value) } else { fmt.Fprintf(w, "PackageVerificationCode: %s (excludes: %s)\n", pkg.PackageVerificationCode.Value, strings.Join(pkg.PackageVerificationCode.ExcludedFiles, ", ")) } } for _, checksum := range pkg.PackageChecksums { fmt.Fprintf(w, "PackageChecksum: %s: %s\n", checksum.Algorithm, checksum.Value) } if pkg.PackageHomePage != "" { fmt.Fprintf(w, "PackageHomePage: %s\n", pkg.PackageHomePage) } if pkg.PackageSourceInfo != "" { fmt.Fprintf(w, "PackageSourceInfo: %s\n", textify(pkg.PackageSourceInfo)) } if pkg.PackageLicenseConcluded != "" { fmt.Fprintf(w, "PackageLicenseConcluded: %s\n", pkg.PackageLicenseConcluded) } if pkg.FilesAnalyzed { for _, s := range pkg.PackageLicenseInfoFromFiles { fmt.Fprintf(w, "PackageLicenseInfoFromFiles: %s\n", s) } } if pkg.PackageLicenseDeclared != "" { fmt.Fprintf(w, "PackageLicenseDeclared: %s\n", pkg.PackageLicenseDeclared) } if pkg.PackageLicenseComments != "" { fmt.Fprintf(w, "PackageLicenseComments: %s\n", textify(pkg.PackageLicenseComments)) } if pkg.PackageCopyrightText != "" { fmt.Fprintf(w, "PackageCopyrightText: %s\n", textify(pkg.PackageCopyrightText)) } if pkg.PackageSummary != "" { fmt.Fprintf(w, "PackageSummary: %s\n", textify(pkg.PackageSummary)) } if pkg.PackageDescription != "" { fmt.Fprintf(w, "PackageDescription: %s\n", textify(pkg.PackageDescription)) } if pkg.PackageComment != "" { fmt.Fprintf(w, "PackageComment: %s\n", textify(pkg.PackageComment)) } for _, s := range pkg.PackageExternalReferences { fmt.Fprintf(w, "ExternalRef: %s %s %s\n", s.Category, s.RefType, s.Locator) if s.ExternalRefComment != "" { fmt.Fprintf(w, "ExternalRefComment: %s\n", textify(s.ExternalRefComment)) } } for _, s := range pkg.PackageAttributionTexts { fmt.Fprintf(w, "PackageAttributionText: %s\n", textify(s)) } fmt.Fprintf(w, "\n") // also render any files for this package sort.Slice(pkg.Files, func(i, j int) bool { return pkg.Files[i].FileSPDXIdentifier < pkg.Files[j].FileSPDXIdentifier }) for _, fi := range pkg.Files { renderFile(fi, w) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_package_test.go000066400000000000000000000432021463371440000253310ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Package section Saver tests ===== func TestSaverPackageSavesTextCombo1(t *testing.T) { // include package external refs // test Supplier:Organization, Originator:Person // FilesAnalyzed true, IsFilesAnalyzedTagPresent true // PackageVerificationCodeExcludedFile has string // NOTE, this is an entirely made up CPE and the format is likely invalid per1 := &spdx.PackageExternalReference{ Category: "SECURITY", RefType: "cpe22Type", Locator: "cpe:/a:john_doe_inc:p1:0.1.0", ExternalRefComment: "this is an external ref comment #1", } // NOTE, this is an entirely made up NPM per2 := &spdx.PackageExternalReference{ Category: "PACKAGE-MANAGER", RefType: "npm", Locator: "p1@0.1.0", ExternalRefComment: `this is a multi-line external ref comment`, } // NOTE, this is an entirely made up SWH persistent ID per3 := &spdx.PackageExternalReference{ Category: "PERSISTENT-ID", RefType: "swh", Locator: "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", // no ExternalRefComment for this one } per4 := &spdx.PackageExternalReference{ Category: "OTHER", RefType: "anything", Locator: "anything-without-spaces-can-go-here", // no ExternalRefComment for this one } pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{SupplierType: "Organization", Supplier: "John Doe, Inc."}, PackageOriginator: &common.Originator{Originator: "John Doe", OriginatorType: "Person"}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: common.PackageVerificationCode{ Value: "0123456789abcdef0123456789abcdef01234567", ExcludedFiles: []string{"p1-0.1.0.spdx"}, }, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", PackageAttributionTexts: []string{"Include this notice in all advertising materials"}, PackageExternalReferences: []*spdx.PackageExternalReference{ per1, per2, per3, per4, }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: Organization: John Doe, Inc. PackageOriginator: Person: John Doe PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: true PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 (excludes: p1-0.1.0.spdx) PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseInfoFromFiles: Apache-1.1 PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment ExternalRef: SECURITY cpe22Type cpe:/a:john_doe_inc:p1:0.1.0 ExternalRefComment: this is an external ref comment #1 ExternalRef: PACKAGE-MANAGER npm p1@0.1.0 ExternalRefComment: this is a multi-line external ref comment ExternalRef: PERSISTENT-ID swh swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2 ExternalRef: OTHER anything anything-without-spaces-can-go-here PackageAttributionText: Include this notice in all advertising materials `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesTextCombo2(t *testing.T) { // no package external refs // test Supplier:NOASSERTION, Originator:Organization // FilesAnalyzed true, IsFilesAnalyzedTagPresent false // PackageVerificationCodeExcludedFile is empty pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{Supplier: "NOASSERTION"}, PackageOriginator: &common.Originator{OriginatorType: "Organization", Originator: "John Doe, Inc."}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, PackageVerificationCode: common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", PackageAttributionTexts: []string{"Include this notice in all advertising materials"}, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: NOASSERTION PackageOriginator: Organization: John Doe, Inc. PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseInfoFromFiles: Apache-1.1 PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment PackageAttributionText: Include this notice in all advertising materials `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesTextCombo3(t *testing.T) { // no package external refs // test Supplier:Person, Originator:NOASSERTION // FilesAnalyzed false, IsFilesAnalyzedTagPresent true // PackageVerificationCodeExcludedFile is empty // three PackageAttributionTexts, one with multi-line text pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{Supplier: "John Doe", SupplierType: "Person"}, PackageOriginator: &common.Originator{Originator: "NOASSERTION"}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output // since FilesAnalyzed is false PackageVerificationCode: common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", PackageAttributionTexts: []string{ "Include this notice in all advertising materials", "and also this notice", `and this multi-line notice which goes across two lines`, }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: Person: John Doe PackageOriginator: NOASSERTION PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment PackageAttributionText: Include this notice in all advertising materials PackageAttributionText: and also this notice PackageAttributionText: and this multi-line notice which goes across two lines `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSaveOmitsOptionalFieldsIfEmpty(t *testing.T) { pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output, // even if present in model, since FilesAnalyzed is false PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // even if present in model, since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesFilesIfPresent(t *testing.T) { f1 := &spdx.File{ FileName: "/tmp/whatever1.txt", FileSPDXIdentifier: common.ElementID("File1231"), Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{"Apache-2.0"}, FileCopyrightText: "Copyright (c) Jane Doe", } f2 := &spdx.File{ FileName: "/tmp/whatever2.txt", FileSPDXIdentifier: common.ElementID("File1232"), Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983d", }, }, LicenseConcluded: "MIT", LicenseInfoInFiles: []string{"MIT"}, FileCopyrightText: "Copyright (c) John Doe", } pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output, // even if present in model, since FilesAnalyzed is false PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // even if present in model, since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", Files: []*spdx.File{ f1, f2, }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. FileName: /tmp/whatever1.txt SPDXID: SPDXRef-File1231 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe FileName: /tmp/whatever2.txt SPDXID: SPDXRef-File1232 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983d LicenseConcluded: MIT LicenseInfoInFile: MIT FileCopyrightText: Copyright (c) John Doe `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageWrapsMultiLine(t *testing.T) { pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: `Copyright (c) John Doe, Inc. Copyright Jane Doe`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. Copyright Jane Doe `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_relationship.go000066400000000000000000000012251463371440000253770ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func renderRelationship(rln *spdx.Relationship, w io.Writer) error { rlnAStr := common.RenderDocElementID(rln.RefA) rlnBStr := common.RenderDocElementID(rln.RefB) if rlnAStr != "SPDXRef-" && rlnBStr != "SPDXRef-" && rln.Relationship != "" { fmt.Fprintf(w, "Relationship: %s %s %s\n", rlnAStr, rln.Relationship, rlnBStr) } if rln.RelationshipComment != "" { fmt.Fprintf(w, "RelationshipComment: %s\n", textify(rln.RelationshipComment)) } return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_relationship_test.go000066400000000000000000000076671463371440000264560ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Relationship section Saver tests ===== func TestSaverRelationshipSavesText(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", RelationshipComment: "this is a comment", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2 RelationshipComment: this is a comment `) // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipOmitsOptionalFieldsIfEmpty(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString("Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2\n") // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipCanHaveNONEOnRight(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "PackageA"), RefB: common.MakeDocElementSpecial("NONE"), Relationship: "DEPENDS_ON", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString("Relationship: SPDXRef-PackageA DEPENDS_ON NONE\n") // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipCanHaveNOASSERTIONOnRight(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "PackageA"), RefB: common.MakeDocElementSpecial("NOASSERTION"), Relationship: "DEPENDS_ON", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString("Relationship: SPDXRef-PackageA DEPENDS_ON NOASSERTION\n") // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipWrapsCommentMultiLine(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", RelationshipComment: `this is a multi-line comment`, } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2 RelationshipComment: this is a multi-line comment `) // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_review.go000066400000000000000000000010541463371440000241770ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func renderReview(rev *spdx.Review, w io.Writer) error { if rev.Reviewer != "" && rev.ReviewerType != "" { fmt.Fprintf(w, "Reviewer: %s: %s\n", rev.ReviewerType, rev.Reviewer) } if rev.ReviewDate != "" { fmt.Fprintf(w, "ReviewDate: %s\n", rev.ReviewDate) } if rev.ReviewComment != "" { fmt.Fprintf(w, "ReviewComment: %s\n", textify(rev.ReviewComment)) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_review_test.go000066400000000000000000000043431463371440000252420ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Review section Saver tests ===== func TestSaverReviewSavesText(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: "this is a review comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z ReviewComment: this is a review comment `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverReviewOmitsOptionalFieldsIfEmpty(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverReviewWrapsMultiLine(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: `this is a multi-line review comment`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z ReviewComment: this is a multi-line review comment `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_snippet.go000066400000000000000000000034401463371440000243610ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) func renderSnippet(sn *spdx.Snippet, w io.Writer) error { if sn.SnippetSPDXIdentifier != "" { fmt.Fprintf(w, "SnippetSPDXID: %s\n", common.RenderElementID(sn.SnippetSPDXIdentifier)) } snFromFileIDStr := common.RenderElementID(sn.SnippetFromFileSPDXIdentifier) if snFromFileIDStr != "" { fmt.Fprintf(w, "SnippetFromFileSPDXID: %s\n", snFromFileIDStr) } for _, snippetRange := range sn.Ranges { if snippetRange.StartPointer.Offset != 0 && snippetRange.EndPointer.Offset != 0 { fmt.Fprintf(w, "SnippetByteRange: %d:%d\n", snippetRange.StartPointer.Offset, snippetRange.EndPointer.Offset) } if snippetRange.StartPointer.LineNumber != 0 && snippetRange.EndPointer.LineNumber != 0 { fmt.Fprintf(w, "SnippetLineRange: %d:%d\n", snippetRange.StartPointer.LineNumber, snippetRange.EndPointer.LineNumber) } } if sn.SnippetLicenseConcluded != "" { fmt.Fprintf(w, "SnippetLicenseConcluded: %s\n", sn.SnippetLicenseConcluded) } for _, s := range sn.LicenseInfoInSnippet { fmt.Fprintf(w, "LicenseInfoInSnippet: %s\n", s) } if sn.SnippetLicenseComments != "" { fmt.Fprintf(w, "SnippetLicenseComments: %s\n", textify(sn.SnippetLicenseComments)) } if sn.SnippetCopyrightText != "" { fmt.Fprintf(w, "SnippetCopyrightText: %s\n", textify(sn.SnippetCopyrightText)) } if sn.SnippetComment != "" { fmt.Fprintf(w, "SnippetComment: %s\n", textify(sn.SnippetComment)) } if sn.SnippetName != "" { fmt.Fprintf(w, "SnippetName: %s\n", sn.SnippetName) } for _, s := range sn.SnippetAttributionTexts { fmt.Fprintf(w, "SnippetAttributionText: %s\n", textify(s)) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/save_snippet_test.go000066400000000000000000000102721463371440000254210ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" ) // ===== Snippet section Saver tests ===== func TestSaverSnippetSavesText(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{LineNumber: 3}, EndPointer: common.SnippetRangePointer{LineNumber: 8}, }, { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", LicenseInfoInSnippet: []string{ "GPL-2.0-or-later", "MIT", }, SnippetLicenseComments: "this is a comment(s) about the snippet license", SnippetCopyrightText: "Copyright (c) John Doe 20x6", SnippetComment: "this is a snippet comment", SnippetName: "from John's program", SnippetAttributionTexts: []string{"some attributions"}, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetLineRange: 3:8 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later LicenseInfoInSnippet: GPL-2.0-or-later LicenseInfoInSnippet: MIT SnippetLicenseComments: this is a comment(s) about the snippet license SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetComment: this is a snippet comment SnippetName: from John's program SnippetAttributionText: some attributions `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverSnippetOmitsOptionalFieldsIfEmpty(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverSnippetWrapsCopyrightMultiline(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: `Copyright (c) John Doe 20x6 Copyright (c) John Doe 20x6`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 Copyright (c) John Doe 20x6 `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/util.go000066400000000000000000000003451463371440000226370ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "strings" ) func textify(s string) string { if strings.Contains(s, "\n") { return fmt.Sprintf("%s", s) } return s } tools-golang-0.5.5/spdx/v2/v2_2/tagvalue/writer/util_test.go000066400000000000000000000011011463371440000236650ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "testing" ) // ===== Utility function tests ===== func TestTextifyWrapsStringWithNewline(t *testing.T) { s := `this text has a newline in it` want := `this text has a newline in it` got := textify(s) if want != got { t.Errorf("Expected %s, got %s", want, got) } } func TestTextifyDoesNotWrapsStringWithNoNewline(t *testing.T) { s := `this text has no newline in it` want := s got := textify(s) if want != got { t.Errorf("Expected %s, got %s", want, got) } } tools-golang-0.5.5/spdx/v2/v2_2/yaml/000077500000000000000000000000001463371440000171475ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_2/yaml/yaml_test.go000066400000000000000000000036331463371440000215040ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package yaml import ( "bytes" jsonenc "encoding/json" "fmt" "os" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" spdx "github.com/spdx/tools-golang/spdx/v2/v2_2" "github.com/spdx/tools-golang/spdx/v2/v2_2/example" "github.com/spdx/tools-golang/yaml" ) func Test_Read(t *testing.T) { want := example.Copy() file, err := os.Open("../../../../examples/sample-docs/yaml/SPDXYAMLExample-2.2.spdx.yaml") if err != nil { panic(fmt.Errorf("error opening File: %s", err)) } var got spdx.Document err = yaml.ReadInto(file, &got) if err != nil { t.Errorf("yaml.Read() error = %v", err) return } if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing YAML example: %s", diff) return } } func Test_Write(t *testing.T) { want := example.Copy() // we always output FilesAnalyzed, even though we handle reading files where it is omitted for _, p := range want.Packages { p.IsFilesAnalyzedTagPresent = true } w := &bytes.Buffer{} if err := yaml.Write(want, w); err != nil { t.Errorf("Save() error = %v", err.Error()) return } // we should be able to parse what the writer wrote, and it should be identical to the original handwritten struct var got spdx.Document err := yaml.ReadInto(bytes.NewReader(w.Bytes()), &got) if err != nil { t.Errorf("failed to parse written document: %v", err.Error()) return } if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after writing and re-parsing YAML example: %s", diff) return } } func relationshipLess(a, b *spdx.Relationship) bool { aStr, _ := jsonenc.Marshal(a) bStr, _ := jsonenc.Marshal(b) return string(aStr) < string(bStr) } tools-golang-0.5.5/spdx/v2/v2_3/000077500000000000000000000000001463371440000162065ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/annotation.go000066400000000000000000000021521463371440000207070ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Annotation is an Annotation section of an SPDX Document type Annotation struct { // 12.1: Annotator // Cardinality: conditional (mandatory, one) if there is an Annotation Annotator common.Annotator `json:"annotator"` // 12.2: Annotation Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationDate string `json:"annotationDate"` // 12.3: Annotation Type: "REVIEW" or "OTHER" // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationType string `json:"annotationType"` // 12.4: SPDX Identifier Reference // Cardinality: conditional (mandatory, one) if there is an Annotation // This field is not used in hierarchical data formats where the referenced element is clear, such as JSON or YAML. AnnotationSPDXIdentifier common.DocElementID `json:"-" yaml:"-"` // 12.5: Annotation Comment // Cardinality: conditional (mandatory, one) if there is an Annotation AnnotationComment string `json:"comment"` } tools-golang-0.5.5/spdx/v2/v2_3/creation_info.go000066400000000000000000000013761463371440000213630ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // CreationInfo is a Document Creation Information section of an SPDX Document type CreationInfo struct { // 6.7: License List Version // Cardinality: optional, one LicenseListVersion string `json:"licenseListVersion,omitempty"` // 6.8: Creators: may have multiple keys for Person, Organization // and/or Tool // Cardinality: mandatory, one or many Creators []common.Creator `json:"creators"` // 6.9: Created: data format YYYY-MM-DDThh:mm:ssZ // Cardinality: mandatory, one Created string `json:"created"` // 6.10: Creator Comment // Cardinality: optional, one CreatorComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_3/document.go000066400000000000000000000115261463371440000203600ustar00rootroot00000000000000// Package v2_3 Package contains the struct definition for an SPDX Document // and its constituent parts. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 import ( "encoding/json" "fmt" converter "github.com/anchore/go-struct-converter" "github.com/spdx/tools-golang/spdx/v2/common" ) const Version = "SPDX-2.3" const DataLicense = "CC0-1.0" // ExternalDocumentRef is a reference to an external SPDX document as defined in section 6.6 type ExternalDocumentRef struct { // DocumentRefID is the ID string defined in the start of the // reference. It should _not_ contain the "DocumentRef-" part // of the mandatory ID string. DocumentRefID string `json:"externalDocumentId"` // URI is the URI defined for the external document URI string `json:"spdxDocument"` // Checksum is the actual hash data Checksum common.Checksum `json:"checksum"` } // Document is an SPDX Document: // See https://spdx.github.io/spdx-spec/v2.3/document-creation-information type Document struct { // 6.1: SPDX Version; should be in the format "SPDX-" // Cardinality: mandatory, one SPDXVersion string `json:"spdxVersion"` // 6.2: Data License; should be "CC0-1.0" // Cardinality: mandatory, one DataLicense string `json:"dataLicense"` // 6.3: SPDX Identifier; should be "DOCUMENT" to represent // mandatory identifier of SPDXRef-DOCUMENT // Cardinality: mandatory, one SPDXIdentifier common.ElementID `json:"SPDXID"` // 6.4: Document Name // Cardinality: mandatory, one DocumentName string `json:"name"` // 6.5: Document Namespace // Cardinality: mandatory, one DocumentNamespace string `json:"documentNamespace"` // 6.6: External Document References // Cardinality: optional, one or many ExternalDocumentReferences []ExternalDocumentRef `json:"externalDocumentRefs,omitempty"` // 6.11: Document Comment // Cardinality: optional, one DocumentComment string `json:"comment,omitempty"` CreationInfo *CreationInfo `json:"creationInfo"` Packages []*Package `json:"packages,omitempty"` Files []*File `json:"files,omitempty"` OtherLicenses []*OtherLicense `json:"hasExtractedLicensingInfos,omitempty"` Relationships []*Relationship `json:"relationships,omitempty"` Annotations []*Annotation `json:"annotations,omitempty"` Snippets []Snippet `json:"snippets,omitempty"` // DEPRECATED in version 2.0 of spec Reviews []*Review `json:"-" yaml:"-"` } func (d *Document) ConvertFrom(_ interface{}) error { d.SPDXVersion = Version return nil } var _ converter.ConvertFrom = (*Document)(nil) func (d *Document) UnmarshalJSON(b []byte) error { type doc Document type extras struct { DocumentDescribes []common.DocElementID `json:"documentDescribes"` } var d2 doc if err := json.Unmarshal(b, &d2); err != nil { return err } var e extras if err := json.Unmarshal(b, &e); err != nil { return err } *d = Document(d2) relationshipExists := map[string]bool{} serializeRel := func(r *Relationship) string { refA := r.RefA refB := r.RefB rel := r.Relationship // we need to serialize the opposite for CONTAINED_BY and DESCRIBED_BY // so that it will match when we try to de-duplicate during deserialization. switch r.Relationship { case common.TypeRelationshipContainedBy: rel = common.TypeRelationshipContains refA = r.RefB refB = r.RefA case common.TypeRelationshipDescribeBy: rel = common.TypeRelationshipDescribe refA = r.RefB refB = r.RefA } return fmt.Sprintf("%v-%v->%v", common.RenderDocElementID(refA), rel, common.RenderDocElementID(refB)) } // remove null relationships for i := 0; i < len(d.Relationships); i++ { if d.Relationships[i] == nil { d.Relationships = append(d.Relationships[0:i], d.Relationships[i+1:]...) i-- } } // index current list of relationships to ensure no duplication for _, r := range d.Relationships { relationshipExists[serializeRel(r)] = true } // build relationships for documentDescribes field for _, id := range e.DocumentDescribes { r := &Relationship{ RefA: common.DocElementID{ ElementRefID: d.SPDXIdentifier, }, RefB: id, Relationship: common.TypeRelationshipDescribe, } if !relationshipExists[serializeRel(r)] { d.Relationships = append(d.Relationships, r) relationshipExists[serializeRel(r)] = true } } // build relationships for package hasFiles field // build relationships for package hasFiles field for _, p := range d.Packages { for _, f := range p.hasFiles { r := &Relationship{ RefA: common.DocElementID{ ElementRefID: p.PackageSPDXIdentifier, }, RefB: f, Relationship: common.TypeRelationshipContains, } if !relationshipExists[serializeRel(r)] { d.Relationships = append(d.Relationships, r) relationshipExists[serializeRel(r)] = true } } p.hasFiles = nil } return nil } var _ json.Unmarshaler = (*Document)(nil) tools-golang-0.5.5/spdx/v2/v2_3/example/000077500000000000000000000000001463371440000176415ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/example/example.go000066400000000000000000000575671463371440000216470ustar00rootroot00000000000000package example import ( "fmt" converter "github.com/anchore/go-struct-converter" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // Copy provides a deep copy of the example func Copy() spdx.Document { out := spdx.Document{} err := converter.Convert(example, &out) if err != nil { panic(fmt.Errorf("unable to convert example doc: %w", err)) } return out } // Example is handwritten translation of an official example SPDX document into a Go struct. // We expect that the result of parsing the official document should be this value. // We expect that the result of writing this struct should match the official example document. var example = spdx.Document{ DataLicense: spdx.DataLicense, SPDXVersion: spdx.Version, SPDXIdentifier: "DOCUMENT", DocumentName: "SPDX-Tools-v2.0", DocumentNamespace: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", CreationInfo: &spdx.CreationInfo{ LicenseListVersion: "3.9", Creators: []common.Creator{ {CreatorType: "Tool", Creator: "LicenseFind-1.0"}, {CreatorType: "Organization", Creator: "ExampleCodeInspect ()"}, {CreatorType: "Person", Creator: "Jane Doe ()"}, }, Created: "2010-01-29T18:30:22Z", CreatorComment: "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.", }, DocumentComment: "This document was created using SPDX 2.0 using licenses from the web site.", ExternalDocumentReferences: []spdx.ExternalDocumentRef{ { DocumentRefID: "DocumentRef-spdx-tool-1.2", URI: "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", Checksum: common.Checksum{ Algorithm: common.SHA1, Value: "d6a770ba38583ed4bb4525bd96e50461655d2759", }, }, }, OtherLicenses: []*spdx.OtherLicense{ { LicenseIdentifier: "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*/", }, { LicenseIdentifier: "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.", }, { LicenseIdentifier: "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*/", }, { LicenseIdentifier: "LicenseRef-Beerware-4.2", 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", LicenseComment: "The beerware license has a couple of other standard variants.", LicenseName: "Beer-Ware License (Version 42)", LicenseCrossReferences: []string{"http://people.freebsd.org/~phk/"}, }, { LicenseIdentifier: "LicenseRef-3", 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.", LicenseName: "CyberNeko License", LicenseCrossReferences: []string{ "http://people.apache.org/~andyc/neko/LICENSE", "http://justasample.url.com", }, LicenseComment: "This is tye CyperNeko License", }, }, Annotations: []*spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "Jane Doe ()", AnnotatorType: "Person", }, AnnotationDate: "2010-01-29T18:30:22Z", AnnotationType: "OTHER", AnnotationComment: "Document level annotation", }, { Annotator: common.Annotator{ Annotator: "Joe Reviewer", AnnotatorType: "Person", }, AnnotationDate: "2010-02-10T00:00:00Z", AnnotationType: "REVIEW", AnnotationComment: "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses", }, { Annotator: common.Annotator{ Annotator: "Suzanne Reviewer", AnnotatorType: "Person", }, AnnotationDate: "2011-03-13T00:00:00Z", AnnotationType: "REVIEW", AnnotationComment: "Another example reviewer.", }, }, Packages: []*spdx.Package{ { PackageName: "glibc", PackageSPDXIdentifier: "Package", PackageVersion: "2.11.1", PackageFileName: "glibc-2.11.1.tar.gz", PackageSupplier: &common.Supplier{ Supplier: "Jane Doe (jane.doe@example.com)", SupplierType: "Person", }, PackageOriginator: &common.Originator{ Originator: "ExampleCodeInspect (contact@example.com)", OriginatorType: "Organization", }, PackageDownloadLocation: "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: &common.PackageVerificationCode{ Value: "d6a770ba38583ed4bb4525bd96e50461655d2758", ExcludedFiles: []string{"./package.spdx"}, }, PackageChecksums: []common.Checksum{ { Algorithm: "MD5", Value: "624c1abb3664f4b35547e7c73864ad24", }, { Algorithm: "SHA1", Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: "SHA256", Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, }, PackageHomePage: "http://ftp.gnu.org/gnu/glibc", PackageSourceInfo: "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.", PackageLicenseConcluded: "(LGPL-2.0-only OR LicenseRef-3)", PackageLicenseInfoFromFiles: []string{ "GPL-2.0-only", "LicenseRef-2", "LicenseRef-1", }, PackageLicenseDeclared: "(LGPL-2.0-only AND LicenseRef-3)", PackageLicenseComments: "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.", PackageCopyrightText: "Copyright 2008-2010 John Smith", PackageSummary: "GNU C library.", PackageDescription: "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.", PackageComment: "", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: "SECURITY", RefType: "cpe23Type", Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", }, { Category: "OTHER", RefType: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge", Locator: "acmecorp/acmenator/4.1.3-alpha", ExternalRefComment: "This is the external ref for Acme", }, }, PackageAttributionTexts: []string{ "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.", }, Files: nil, Annotations: []spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "Package Commenter", AnnotatorType: "Person", }, AnnotationDate: "2011-01-29T18:30:22Z", AnnotationType: "OTHER", AnnotationComment: "Package level annotation", }, }, }, { PackageSPDXIdentifier: "fromDoap-1", PackageCopyrightText: "NOASSERTION", PackageDownloadLocation: "NOASSERTION", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, PackageHomePage: "http://commons.apache.org/proper/commons-lang/", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", PackageName: "Apache Commons Lang", }, { PackageName: "Jena", PackageSPDXIdentifier: "fromDoap-0", PackageCopyrightText: "NOASSERTION", PackageDownloadLocation: "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: "PACKAGE-MANAGER", RefType: "purl", Locator: "pkg:maven/org.apache.jena/apache-jena@3.12.0", }, }, FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, PackageHomePage: "http://www.openjena.org/", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", PackageVersion: "3.12.0", }, { PackageSPDXIdentifier: "Saxon", PackageChecksums: []common.Checksum{ { Algorithm: "SHA1", Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, }, PackageCopyrightText: "Copyright Saxonica Ltd", PackageDescription: "The Saxon package is a collection of tools for processing XML documents.", PackageDownloadLocation: "https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, PackageHomePage: "http://saxon.sourceforge.net/", PackageLicenseComments: "Other versions available for a commercial license", PackageLicenseConcluded: "MPL-1.0", PackageLicenseDeclared: "MPL-1.0", PackageName: "Saxon", PackageFileName: "saxonB-8.8.zip", PackageVersion: "8.8", }, { PrimaryPackagePurpose: "CONTAINER", PackageSPDXIdentifier: "CentOS-7", PackageCopyrightText: "NOASSERTION", PackageDescription: "The CentOS container used to run the application.", PackageDownloadLocation: "NOASSERTION", FilesAnalyzed: true, PackageHomePage: "https://www.centos.org/", PackageName: "centos", PackageFileName: "saxonB-8.8.zip", PackageVersion: "centos7.9.2009", BuiltDate: "2021-09-15T02:38:00Z", ValidUntilDate: "2022-10-15T02:38:00Z", ReleaseDate: "2021-10-15T02:38:00Z", }, }, Files: []*spdx.File{ { FileName: "./src/org/spdx/parser/DOAPProject.java", FileSPDXIdentifier: "DoapSource", FileTypes: []string{ "SOURCE", }, Checksums: []common.Checksum{ { Algorithm: "SHA1", Value: "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", }, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright 2010, 2011 Source Auditor Inc.", FileContributors: []string{ "Protecode Inc.", "SPDX Technical Team Members", "Open Logic Inc.", "Source Auditor Inc.", "Black Duck Software In.c", }, }, { FileSPDXIdentifier: "CommonsLangSrc", Checksums: []common.Checksum{ { Algorithm: "SHA1", Value: "c2b4e1c67a2d28fced849ee1bb76e7391b93f125", }, }, FileComment: "This file is used by Jena", FileCopyrightText: "Copyright 2001-2011 The Apache Software Foundation", FileContributors: []string{"Apache Software Foundation"}, FileName: "./lib-source/commons-lang3-3.1-sources.jar", FileTypes: []string{"ARCHIVE"}, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{"Apache-2.0"}, FileNotice: "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())", }, { FileSPDXIdentifier: "JenaLib", Checksums: []common.Checksum{ { Algorithm: "SHA1", Value: "3ab4e1c67a2d28fced849ee1bb76e7391b93f125", }, }, FileComment: "This file belongs to Jena", FileCopyrightText: "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP", FileContributors: []string{"Apache Software Foundation", "Hewlett Packard Inc."}, FileName: "./lib-source/jena-2.6.3-sources.jar", FileTypes: []string{"ARCHIVE"}, LicenseComments: "This license is used by Jena", LicenseConcluded: "LicenseRef-1", LicenseInfoInFiles: []string{"LicenseRef-1"}, }, { FileSPDXIdentifier: "File", Annotations: []spdx.Annotation{ { Annotator: common.Annotator{ Annotator: "File Commenter", AnnotatorType: "Person", }, AnnotationDate: "2011-01-29T18:30:22Z", AnnotationType: "OTHER", AnnotationComment: "File level annotation", }, }, Checksums: []common.Checksum{ { Algorithm: "SHA1", Value: "d6a770ba38583ed4bb4525bd96e50461655d2758", }, { Algorithm: "MD5", Value: "624c1abb3664f4b35547e7c73864ad24", }, }, FileComment: "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.", FileCopyrightText: "Copyright 2008-2010 John Smith", FileContributors: []string{"The Regents of the University of California", "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"}, FileName: "./package/foo.c", FileTypes: []string{"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: []string{"GPL-2.0-only", "LicenseRef-2"}, FileNotice: "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: []spdx.Snippet{ { SnippetSPDXIdentifier: "Snippet", SnippetFromFileSPDXIdentifier: "DoapSource", Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{ Offset: 310, FileSPDXIdentifier: "DoapSource", }, EndPointer: common.SnippetRangePointer{ Offset: 420, FileSPDXIdentifier: "DoapSource", }, }, { StartPointer: common.SnippetRangePointer{ LineNumber: 5, FileSPDXIdentifier: "DoapSource", }, EndPointer: common.SnippetRangePointer{ LineNumber: 23, FileSPDXIdentifier: "DoapSource", }, }, }, SnippetLicenseConcluded: "GPL-2.0-only", LicenseInfoInSnippet: []string{"GPL-2.0-only"}, SnippetLicenseComments: "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.", SnippetCopyrightText: "Copyright 2008-2010 John Smith", SnippetComment: "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.", SnippetName: "from linux kernel", // omitted: SnippetAttributionTexts: []string{"Snippet attribution"}, }, }, Relationships: []*spdx.Relationship{ { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "Package"), Relationship: "CONTAINS", RelationshipComment: "A relationship comment", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("spdx-tool-1.2", "ToolsElement"), Relationship: "COPY_OF", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "File"), Relationship: "DESCRIBES", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "Package"), Relationship: "DESCRIBES", }, { RefA: common.MakeDocElementID("", "Package"), RefB: common.MakeDocElementID("", "JenaLib"), Relationship: "CONTAINS", }, { RefA: common.MakeDocElementID("", "Package"), RefB: common.MakeDocElementID("", "Saxon"), Relationship: "DYNAMIC_LINK", }, { RefA: common.MakeDocElementID("", "CommonsLangSrc"), RefB: common.MakeDocElementSpecial("NOASSERTION"), Relationship: "GENERATED_FROM", }, { RefA: common.MakeDocElementID("", "JenaLib"), RefB: common.MakeDocElementID("", "Package"), Relationship: "CONTAINS", }, { RefA: common.MakeDocElementID("", "File"), RefB: common.MakeDocElementID("", "fromDoap-0"), Relationship: "GENERATED_FROM", }, }, // omitted: Reviews: []*spdx.Review{ // { // Reviewer: "joe@example.com", // ReviewerType: "Person", // ReviewDate: "2021-11-03T05:43:21Z", // ReviewComment: "This is a review comment", // }, //}, } tools-golang-0.5.5/spdx/v2/v2_3/file.go000066400000000000000000000065771463371440000174730ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // File is a File section of an SPDX Document type File struct { // 8.1: File Name // Cardinality: mandatory, one FileName string `json:"fileName"` // 8.2: File SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one FileSPDXIdentifier common.ElementID `json:"SPDXID"` // 8.3: File Types // Cardinality: optional, multiple FileTypes []string `json:"fileTypes,omitempty"` // 8.4: File Checksum: may have keys for SHA1, SHA256, MD5, SHA3-256, SHA3-384, SHA3-512, BLAKE2b-256, BLAKE2b-384, BLAKE2b-512, BLAKE3, ADLER32 // Cardinality: mandatory, one SHA1, others may be optionally provided Checksums []common.Checksum `json:"checksums"` // 8.5: Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one LicenseConcluded string `json:"licenseConcluded,omitempty"` // 8.6: License Information in File: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one or many LicenseInfoInFiles []string `json:"licenseInfoInFiles,omitempty"` // 8.7: Comments on License // Cardinality: optional, one LicenseComments string `json:"licenseComments,omitempty"` // 8.8: Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: mandatory, one FileCopyrightText string `json:"copyrightText"` // DEPRECATED in version 2.1 of spec // 8.9-8.11: Artifact of Project variables (defined below) // Cardinality: optional, one or many ArtifactOfProjects []*ArtifactOfProject `json:"artifactOfs,omitempty"` // 8.12: File Comment // Cardinality: optional, one FileComment string `json:"comment,omitempty"` // 8.13: File Notice // Cardinality: optional, one FileNotice string `json:"noticeText,omitempty"` // 8.14: File Contributor // Cardinality: optional, one or many FileContributors []string `json:"fileContributors,omitempty"` // 8.15: File Attribution Text // Cardinality: optional, one or many FileAttributionTexts []string `json:"attributionTexts,omitempty"` // DEPRECATED in version 2.0 of spec // 8.16: File Dependencies // Cardinality: optional, one or many FileDependencies []string `json:"fileDependencies,omitempty"` // Snippets contained in this File // Note that Snippets could be defined in a different Document! However, // the only ones that _THIS_ document can contain are this ones that are // defined here -- so this should just be an ElementID. Snippets map[common.ElementID]*Snippet `json:"-" yaml:"-"` Annotations []Annotation `json:"annotations,omitempty"` } // ArtifactOfProject is a DEPRECATED collection of data regarding // a Package, as defined in sections 8.9-8.11. // NOTE: the JSON schema does not define the structure of this object: // https://github.com/spdx/spdx-spec/blob/development/v2.3.1/schemas/spdx-schema.json#L480 type ArtifactOfProject struct { // DEPRECATED in version 2.1 of spec // 8.9: Artifact of Project Name // Cardinality: conditional, required if present, one per AOP Name string `json:"name"` // DEPRECATED in version 2.1 of spec // 8.10: Artifact of Project Homepage: URL or "UNKNOWN" // Cardinality: optional, one per AOP HomePage string `json:"homePage"` // DEPRECATED in version 2.1 of spec // 8.11: Artifact of Project Uniform Resource Identifier // Cardinality: optional, one per AOP URI string `json:"URI"` } tools-golang-0.5.5/spdx/v2/v2_3/json/000077500000000000000000000000001463371440000171575ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/json/json_test.go000066400000000000000000000301051463371440000215150ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package json import ( "bytes" jsonenc "encoding/json" "flag" "fmt" "os" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/require" "github.com/spdx/tools-golang/json" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" "github.com/spdx/tools-golang/spdx/v2/v2_3/example" ) var update = *flag.Bool("update-snapshots", false, "update the example snapshot") func Test_Read(t *testing.T) { fileName := "../../../../examples/sample-docs/json/SPDXJSONExample-v2.3.spdx.json" want := example.Copy() if update { w := &bytes.Buffer{} err := json.Write(want, w) if err != nil { t.Errorf("unable to serialize SPDX 2.3 example to JSON: %v", err) } err = os.WriteFile(fileName, w.Bytes(), 0644) if err != nil { t.Errorf("unable to write SPDX 2.3 example to JSON: %v", err) } } file, err := os.Open(fileName) if err != nil { panic(fmt.Errorf("error opening File: %s", err)) } var got spdx.Document err = json.ReadInto(file, &got) if err != nil { t.Errorf("json.parser.Load() error = %v", err) return } if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing JSON example: %s", diff) return } } func Test_Write(t *testing.T) { want := example.Copy() // we always output FilesAnalyzed, even though we handle reading files where it is omitted for _, p := range want.Packages { p.IsFilesAnalyzedTagPresent = true } w := &bytes.Buffer{} if err := json.Write(&want, w); err != nil { t.Errorf("Write() error = %v", err.Error()) return } // we should be able to parse what the writer wrote, and it should be identical to the original struct we wrote var got spdx.Document err := json.ReadInto(bytes.NewReader(w.Bytes()), &got) if err != nil { t.Errorf("failed to parse written document: %v", err.Error()) return } if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing JSON example: %s", diff) return } } func Test_nullRelationships(t *testing.T) { file, err := os.Open("testdata/spdx-null-relationships.json") if err != nil { panic(fmt.Errorf("error opening File: %s", err)) } var got spdx.Document err = json.ReadInto(file, &got) if err != nil { t.Errorf("json.parser.Load() error = %v", err) return } require.Len(t, got.Relationships, 2) for _, r := range got.Relationships { require.NotNil(t, r) } } func Test_ShorthandFields(t *testing.T) { contents := `{ "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "SPDX-Tools-v2.0", "documentDescribes": [ "SPDXRef-Container" ], "packages": [ { "name": "Container", "SPDXID": "SPDXRef-Container" }, { "name": "Package-1", "SPDXID": "SPDXRef-Package-1", "versionInfo": "1.1.1", "hasFiles": [ "SPDXRef-File-1", "SPDXRef-File-2" ] }, { "name": "Package-2", "SPDXID": "SPDXRef-Package-2", "versionInfo": "2.2.2" } ], "files": [ { "fileName": "./f1", "SPDXID": "SPDXRef-File-1" }, { "fileName": "./f2", "SPDXID": "SPDXRef-File-2" } ] }` doc := spdx.Document{} err := json.ReadInto(strings.NewReader(contents), &doc) require.NoError(t, err) id := func(s string) common.DocElementID { return common.DocElementID{ ElementRefID: common.ElementID(s), } } want := spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: "DOCUMENT", DocumentName: "SPDX-Tools-v2.0", Packages: []*spdx.Package{ { PackageName: "Container", PackageSPDXIdentifier: "Container", FilesAnalyzed: true, }, { PackageName: "Package-1", PackageSPDXIdentifier: "Package-1", PackageVersion: "1.1.1", FilesAnalyzed: true, }, { PackageName: "Package-2", PackageSPDXIdentifier: "Package-2", PackageVersion: "2.2.2", FilesAnalyzed: true, }, }, Files: []*spdx.File{ { FileName: "./f1", FileSPDXIdentifier: "File-1", }, { FileName: "./f2", FileSPDXIdentifier: "File-2", }, }, Relationships: []*spdx.Relationship{ { RefA: id("DOCUMENT"), RefB: id("Container"), Relationship: common.TypeRelationshipDescribe, }, { RefA: id("Package-1"), RefB: id("File-1"), Relationship: common.TypeRelationshipContains, }, { RefA: id("Package-1"), RefB: id("File-2"), Relationship: common.TypeRelationshipContains, }, }, } if diff := cmp.Diff(want, doc, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing JSON example: %s", diff) return } } func Test_ShorthandFieldsNoDuplicates(t *testing.T) { contents := `{ "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "SPDX-Tools-v2.0", "documentDescribes": [ "SPDXRef-Container" ], "packages": [ { "name": "Container", "SPDXID": "SPDXRef-Container" }, { "name": "Package-1", "SPDXID": "SPDXRef-Package-1", "versionInfo": "1.1.1", "hasFiles": [ "SPDXRef-File-1", "SPDXRef-File-2" ] }, { "name": "Package-2", "SPDXID": "SPDXRef-Package-2", "versionInfo": "2.2.2" } ], "files": [ { "fileName": "./f1", "SPDXID": "SPDXRef-File-1" }, { "fileName": "./f2", "SPDXID": "SPDXRef-File-2" } ], "relationships": [ { "spdxElementId": "SPDXRef-Package-1", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-File-1" }, { "spdxElementId": "SPDXRef-File-2", "relationshipType": "CONTAINED_BY", "relatedSpdxElement": "SPDXRef-Package-1" } ] }` doc := spdx.Document{} err := json.ReadInto(strings.NewReader(contents), &doc) require.NoError(t, err) id := func(s string) common.DocElementID { return common.DocElementID{ ElementRefID: common.ElementID(s), } } want := spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: "DOCUMENT", DocumentName: "SPDX-Tools-v2.0", Packages: []*spdx.Package{ { PackageName: "Container", PackageSPDXIdentifier: "Container", FilesAnalyzed: true, }, { PackageName: "Package-1", PackageSPDXIdentifier: "Package-1", PackageVersion: "1.1.1", FilesAnalyzed: true, }, { PackageName: "Package-2", PackageSPDXIdentifier: "Package-2", PackageVersion: "2.2.2", FilesAnalyzed: true, }, }, Files: []*spdx.File{ { FileName: "./f1", FileSPDXIdentifier: "File-1", }, { FileName: "./f2", FileSPDXIdentifier: "File-2", }, }, Relationships: []*spdx.Relationship{ { RefA: id("DOCUMENT"), RefB: id("Container"), Relationship: common.TypeRelationshipDescribe, }, { RefA: id("Package-1"), RefB: id("File-1"), Relationship: common.TypeRelationshipContains, }, { RefA: id("File-2"), RefB: id("Package-1"), Relationship: common.TypeRelationshipContainedBy, }, }, } if diff := cmp.Diff(want, doc, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing JSON example: %s", diff) return } } func Test_JsonEnums(t *testing.T) { contents := `{ "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "SPDX-Tools-v2.0", "documentDescribes": [ "SPDXRef-Container" ], "packages": [ { "name": "Container", "SPDXID": "SPDXRef-Container" }, { "name": "Package-1", "SPDXID": "SPDXRef-Package-1", "versionInfo": "1.1.1", "externalRefs": [{ "referenceCategory": "PACKAGE_MANAGER", "referenceLocator": "pkg:somepkg/ns/name1", "referenceType": "purl" }] }, { "name": "Package-2", "SPDXID": "SPDXRef-Package-2", "versionInfo": "2.2.2", "externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceLocator": "pkg:somepkg/ns/name2", "referenceType": "purl" }] }, { "name": "Package-3", "SPDXID": "SPDXRef-Package-3", "versionInfo": "3.3.3", "externalRefs": [{ "referenceCategory": "PERSISTENT_ID", "referenceLocator": "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", "referenceType": "gitoid" }] }, { "name": "Package-4", "SPDXID": "SPDXRef-Package-4", "versionInfo": "4.4.4", "externalRefs": [{ "referenceCategory": "PERSISTENT-ID", "referenceLocator": "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", "referenceType": "gitoid" }] } ] }` doc := spdx.Document{} err := json.ReadInto(strings.NewReader(contents), &doc) require.NoError(t, err) id := func(s string) common.DocElementID { return common.DocElementID{ ElementRefID: common.ElementID(s), } } require.Equal(t, spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: "DOCUMENT", DocumentName: "SPDX-Tools-v2.0", Packages: []*spdx.Package{ { PackageName: "Container", PackageSPDXIdentifier: "Container", FilesAnalyzed: true, }, { PackageName: "Package-1", PackageSPDXIdentifier: "Package-1", PackageVersion: "1.1.1", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: common.CategoryPackageManager, RefType: common.TypePackageManagerPURL, Locator: "pkg:somepkg/ns/name1", }, }, FilesAnalyzed: true, }, { PackageName: "Package-2", PackageSPDXIdentifier: "Package-2", PackageVersion: "2.2.2", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: common.CategoryPackageManager, RefType: common.TypePackageManagerPURL, Locator: "pkg:somepkg/ns/name2", }, }, FilesAnalyzed: true, }, { PackageName: "Package-3", PackageSPDXIdentifier: "Package-3", PackageVersion: "3.3.3", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: common.CategoryPersistentId, RefType: common.TypePersistentIdGitoid, Locator: "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", }, }, FilesAnalyzed: true, }, { PackageName: "Package-4", PackageSPDXIdentifier: "Package-4", PackageVersion: "4.4.4", PackageExternalReferences: []*spdx.PackageExternalReference{ { Category: common.CategoryPersistentId, RefType: common.TypePersistentIdGitoid, Locator: "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", }, }, FilesAnalyzed: true, }, }, Relationships: []*spdx.Relationship{ { RefA: id("DOCUMENT"), RefB: id("Container"), Relationship: common.TypeRelationshipDescribe, }, }, }, doc) } func relationshipLess(a, b *spdx.Relationship) bool { aStr, _ := jsonenc.Marshal(a) bStr, _ := jsonenc.Marshal(b) return string(aStr) < string(bStr) } tools-golang-0.5.5/spdx/v2/v2_3/json/testdata/000077500000000000000000000000001463371440000207705ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/json/testdata/spdx-null-relationships.json000066400000000000000000000031411463371440000264720ustar00rootroot00000000000000{ "files": [ { "fileName": "./Microsoft.CSharp.dll", "SPDXID": "SPDXRef-File--Microsoft.CSharp.dll-E226415EEA8ABBBA041A635582440F75E873395C", "checksums": [ { "algorithm": "SHA256", "checksumValue": "696b0b0d6ac06e620efd58db6f5f2e15fa2c9b91ddf8774ab8768c958d593254" }, { "algorithm": "SHA1", "checksumValue": "e226415eea8abbba041a635582440f75e873395c" } ], "licenseConcluded": "NOASSERTION", "licenseInfoInFile": [ "NOASSERTION" ], "copyrightText": "NOASSERTION" }], "packages": [ { "name": "read-pkg", "SPDXID": "SPDXRef-Package-read-pkg-1.1.0-30839A4052AC42B4E1CAB4B52EBC7DE7B94BB36D", "versionInfo": "1.1.0" }, { "name": "read-pkg", "SPDXID": "SPDXRef-Package-read-pkg-1.1.0-30839A4052AC42B4E1CAB4B52EBC7DE7B94BB36D", "versionInfo": "1.1.0" } ], "relationships": [ null, { }, null, { }, null ], "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "Coordinated Packages 229170", "documentNamespace": "https://sbom.microsoft/1:2QSF7qZlbE-F7QrUJlEo7g:pHp_nUFvDUijZ4LrJ4RhoQ/696:229170/F8kPc6dwY0WXD1Rkc2z6cg", "creationInfo": { "created": "2021-12-08T21:06:16Z", "creators": [ "Organization: Microsoft", "Tool: Microsoft.SBOMTool-2.0.88" ] } } tools-golang-0.5.5/spdx/v2/v2_3/other_license.go000066400000000000000000000020651463371440000213630ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 // OtherLicense is an Other License Information section of an SPDX Document type OtherLicense struct { // 10.1: License Identifier: "LicenseRef-[idstring]" // Cardinality: conditional (mandatory, one) if license is not // on SPDX License List LicenseIdentifier string `json:"licenseId"` // 10.2: Extracted Text // Cardinality: conditional (mandatory, one) if there is a // License Identifier assigned ExtractedText string `json:"extractedText"` // 10.3: License Name: single line of text or "NOASSERTION" // Cardinality: conditional (mandatory, one) if license is not // on SPDX License List LicenseName string `json:"name,omitempty"` // 10.4: License Cross Reference // Cardinality: conditional (optional, one or many) if license // is not on SPDX License List LicenseCrossReferences []string `json:"seeAlsos,omitempty"` // 10.5: License Comment // Cardinality: optional, one LicenseComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_3/package.go000066400000000000000000000161171463371440000201360ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 import ( "encoding/json" "strings" "github.com/spdx/tools-golang/json/marshal" "github.com/spdx/tools-golang/spdx/v2/common" ) // Package is a Package section of an SPDX Document type Package struct { // NOT PART OF SPEC // flag: does this "package" contain files that were in fact "unpackaged", // e.g. included directly in the Document without being in a Package? IsUnpackaged bool `json:"-" yaml:"-"` // 7.1: Package Name // Cardinality: mandatory, one PackageName string `json:"name"` // 7.2: Package SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one PackageSPDXIdentifier common.ElementID `json:"SPDXID"` // 7.3: Package Version // Cardinality: optional, one PackageVersion string `json:"versionInfo,omitempty"` // 7.4: Package File Name // Cardinality: optional, one PackageFileName string `json:"packageFileName,omitempty"` // 7.5: Package Supplier: may have single result for either Person or Organization, // or NOASSERTION // Cardinality: optional, one PackageSupplier *common.Supplier `json:"supplier,omitempty"` // 7.6: Package Originator: may have single result for either Person or Organization, // or NOASSERTION // Cardinality: optional, one PackageOriginator *common.Originator `json:"originator,omitempty"` // 7.7: Package Download Location // Cardinality: mandatory, one PackageDownloadLocation string `json:"downloadLocation"` // 7.8: FilesAnalyzed // Cardinality: optional, one; default value is "true" if omitted FilesAnalyzed bool `json:"filesAnalyzed"` // NOT PART OF SPEC: did FilesAnalyzed tag appear? IsFilesAnalyzedTagPresent bool `json:"-" yaml:"-"` // 7.9: Package Verification Code // Cardinality: if FilesAnalyzed == true must be present, if FilesAnalyzed == false must be omitted PackageVerificationCode *common.PackageVerificationCode `json:"packageVerificationCode,omitempty"` // 7.10: Package Checksum: may have keys for SHA1, SHA256, SHA512, MD5, SHA3-256, SHA3-384, SHA3-512, BLAKE2b-256, BLAKE2b-384, BLAKE2b-512, BLAKE3, ADLER32 // Cardinality: optional, one or many PackageChecksums []common.Checksum `json:"checksums,omitempty"` // 7.11: Package Home Page // Cardinality: optional, one PackageHomePage string `json:"homepage,omitempty"` // 7.12: Source Information // Cardinality: optional, one PackageSourceInfo string `json:"sourceInfo,omitempty"` // 7.13: Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one PackageLicenseConcluded string `json:"licenseConcluded,omitempty"` // 7.14: All Licenses Info from Files: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one or many if filesAnalyzed is true / omitted; // zero (must be omitted) if filesAnalyzed is false PackageLicenseInfoFromFiles []string `json:"licenseInfoFromFiles,omitempty"` // 7.15: Declared License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one PackageLicenseDeclared string `json:"licenseDeclared,omitempty"` // 7.16: Comments on License // Cardinality: optional, one PackageLicenseComments string `json:"licenseComments,omitempty"` // 7.17: Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: optional, zero or one PackageCopyrightText string `json:"copyrightText,omitempty"` // 7.18: Package Summary Description // Cardinality: optional, one PackageSummary string `json:"summary,omitempty"` // 7.19: Package Detailed Description // Cardinality: optional, one PackageDescription string `json:"description,omitempty"` // 7.20: Package Comment // Cardinality: optional, one PackageComment string `json:"comment,omitempty"` // 7.21: Package External Reference // Cardinality: optional, one or many PackageExternalReferences []*PackageExternalReference `json:"externalRefs,omitempty"` // 7.22: Package External Reference Comment // Cardinality: conditional (optional, one) for each External Reference // contained within PackageExternalReference struct, if present // 7.23: Package Attribution Text // Cardinality: optional, one or many PackageAttributionTexts []string `json:"attributionTexts,omitempty"` // 7.24: Primary Package Purpose // Cardinality: optional, one or many // Allowed values: APPLICATION, FRAMEWORK, LIBRARY, CONTAINER, OPERATING-SYSTEM, DEVICE, FIRMWARE, SOURCE, ARCHIVE, FILE, INSTALL, OTHER PrimaryPackagePurpose string `json:"primaryPackagePurpose,omitempty"` // 7.25: Release Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: optional, one ReleaseDate string `json:"releaseDate,omitempty"` // 7.26: Build Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: optional, one BuiltDate string `json:"builtDate,omitempty"` // 7.27: Valid Until Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: optional, one ValidUntilDate string `json:"validUntilDate,omitempty"` // Files contained in this Package Files []*File `json:"files,omitempty"` Annotations []Annotation `json:"annotations,omitempty"` // this field is only used when decoding JSON to translate the hasFiles // property to relationships hasFiles []common.DocElementID } func (p *Package) UnmarshalJSON(b []byte) error { type pkg Package type extras struct { HasFiles []common.DocElementID `json:"hasFiles"` FilesAnalyzed *bool `json:"filesAnalyzed"` } var p2 pkg if err := json.Unmarshal(b, &p2); err != nil { return err } var e extras if err := json.Unmarshal(b, &e); err != nil { return err } *p = Package(p2) p.hasFiles = e.HasFiles // FilesAnalyzed defaults to true if omitted if e.FilesAnalyzed == nil { p.FilesAnalyzed = true } else { p.IsFilesAnalyzedTagPresent = true } return nil } var _ json.Unmarshaler = (*Package)(nil) // PackageExternalReference is an External Reference to additional info // about a Package, as defined in section 7.21 type PackageExternalReference struct { // category is "SECURITY", "PACKAGE-MANAGER" or "OTHER" Category string `json:"referenceCategory"` // type is an [idstring] as defined in Appendix VI; // called RefType here due to "type" being a Golang keyword RefType string `json:"referenceType"` // locator is a unique string to access the package-specific // info, metadata or content within the target location Locator string `json:"referenceLocator"` // 7.22: Package External Reference Comment // Cardinality: conditional (optional, one) for each External Reference ExternalRefComment string `json:"comment,omitempty"` } var _ json.Unmarshaler = (*PackageExternalReference)(nil) func (r *PackageExternalReference) UnmarshalJSON(b []byte) error { type ref PackageExternalReference var rr ref if err := json.Unmarshal(b, &rr); err != nil { return err } rr.Category = strings.ReplaceAll(rr.Category, "_", "-") *r = PackageExternalReference(rr) return nil } var _ json.Marshaler = (*PackageExternalReference)(nil) func (r *PackageExternalReference) MarshalJSON() ([]byte, error) { type ref PackageExternalReference var rr ref rr = ref(*r) rr.Category = strings.ReplaceAll(rr.Category, "_", "-") return marshal.JSON(&rr) } tools-golang-0.5.5/spdx/v2/v2_3/rdf/000077500000000000000000000000001463371440000167615ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader.go000066400000000000000000000010251463371440000205500ustar00rootroot00000000000000package rdf import ( "io" "github.com/spdx/gordf/rdfloader" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" "github.com/spdx/tools-golang/spdx/v2/v2_3/rdf/reader" ) // Takes in a file Reader and returns the pertaining spdx document // or the error if any is encountered while setting the doc. func Read(content io.Reader) (*spdx.Document, error) { var rdfParserObj, err = rdfloader.LoadFromReaderObject(content) if err != nil { return nil, err } doc, err := reader.LoadFromGoRDFParser(rdfParserObj) return doc, err } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/000077500000000000000000000000001463371440000202235ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/constants.go000066400000000000000000000412501463371440000225700ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import "github.com/spdx/gordf/rdfloader/parser" var ( // NAMESPACES NS_SPDX = "http://spdx.org/rdf/terms#" NS_RDFS = "http://www.w3.org/2000/01/rdf-schema#" NS_RDF = parser.RDFNS NS_PTR = "http://www.w3.org/2009/pointers#" NS_DOAP = "http://usefulinc.com/ns/doap#" // SPDX properties SPDX_SPEC_VERSION = NS_SPDX + "specVersion" SPDX_DATA_LICENSE = NS_SPDX + "dataLicense" SPDX_NAME = NS_SPDX + "name" SPDX_EXTERNAL_DOCUMENT_REF = NS_SPDX + "externalDocumentRef" SPDX_LICENSE_LIST_VERSION = NS_SPDX + "licenseListVersion" SPDX_CREATOR = NS_SPDX + "creator" SPDX_CREATED = NS_SPDX + "created" SPDX_REVIEWED = NS_SPDX + "reviewed" SPDX_DESCRIBES_PACKAGE = NS_SPDX + "describesPackage" SPDX_HAS_EXTRACTED_LICENSING_INFO = NS_SPDX + "hasExtractedLicensingInfo" SPDX_RELATIONSHIP = NS_SPDX + "relationship" SPDX_ANNOTATION = NS_SPDX + "annotation" SPDX_COMMENT = NS_SPDX + "comment" SPDX_CREATION_INFO = NS_SPDX + "creationInfo" SPDX_CHECKSUM_ALGORITHM_SHA1 = NS_SPDX + "checksumAlgorithm_sha1" SPDX_CHECKSUM_ALGORITHM_SHA256 = NS_SPDX + "checksumAlgorithm_sha256" SPDX_CHECKSUM_ALGORITHM_MD5 = NS_SPDX + "checksumAlgorithm_md5" SPDX_EXTERNAL_DOCUMENT_ID = NS_SPDX + "externalDocumentId" SPDX_SPDX_DOCUMENT = NS_SPDX + "spdxDocument" SPDX_SPDX_DOCUMENT_CAPITALIZED = NS_SPDX + "SpdxDocument" SPDX_CHECKSUM = NS_SPDX + "checksum" SPDX_CHECKSUM_CAPITALIZED = NS_SPDX + "Checksum" SPDX_ANNOTATION_TYPE = NS_SPDX + "annotationType" SPDX_ANNOTATION_TYPE_OTHER = NS_SPDX + "annotationType_other" SPDX_ANNOTATION_TYPE_REVIEW = NS_SPDX + "annotationType_review" SPDX_LICENSE_INFO_IN_FILE = NS_SPDX + "licenseInfoInFile" SPDX_LICENSE_CONCLUDED = NS_SPDX + "licenseConcluded" SPDX_LICENSE_COMMENTS = NS_SPDX + "licenseComments" SPDX_COPYRIGHT_TEXT = NS_SPDX + "copyrightText" SPDX_ARTIFACT_OF = NS_SPDX + "artifactOf" SPDX_NOTICE_TEXT = NS_SPDX + "noticeText" SPDX_FILE_CONTRIBUTOR = NS_SPDX + "fileContributor" SPDX_FILE_DEPENDENCY = NS_SPDX + "fileDependency" SPDX_FILE_TYPE = NS_SPDX + "fileType" SPDX_FILE_NAME = NS_SPDX + "fileName" SPDX_EXTRACTED_TEXT = NS_SPDX + "extractedText" SPDX_LICENSE_ID = NS_SPDX + "licenseId" SPDX_FILE = NS_SPDX + "File" SPDX_PACKAGE = NS_SPDX + "Package" SPDX_SPDX_ELEMENT = NS_SPDX + "SpdxElement" SPDX_VERSION_INFO = NS_SPDX + "versionInfo" SPDX_PACKAGE_FILE_NAME = NS_SPDX + "packageFileName" SPDX_SUPPLIER = NS_SPDX + "supplier" SPDX_ORIGINATOR = NS_SPDX + "originator" SPDX_DOWNLOAD_LOCATION = NS_SPDX + "downloadLocation" SPDX_FILES_ANALYZED = NS_SPDX + "filesAnalyzed" SPDX_PACKAGE_VERIFICATION_CODE = NS_SPDX + "packageVerificationCode" SPDX_SOURCE_INFO = NS_SPDX + "sourceInfo" SPDX_LICENSE_INFO_FROM_FILES = NS_SPDX + "licenseInfoFromFiles" SPDX_LICENSE_DECLARED = NS_SPDX + "licenseDeclared" SPDX_SUMMARY = NS_SPDX + "summary" SPDX_DESCRIPTION = NS_SPDX + "description" SPDX_EXTERNAL_REF = NS_SPDX + "externalRef" SPDX_HAS_FILE = NS_SPDX + "hasFile" SPDX_PRIMARY_PACKAGE_PURPOSE = NS_SPDX + "primaryPackagePurpose" SPDX_RELEASE_DATE = NS_SPDX + "releaseDate" SPDX_BUILT_DATE = NS_SPDX + "builtDate" SPDX_VALID_UNTIL_DATE = NS_SPDX + "validUntilDate" SPDX_ATTRIBUTION_TEXT = NS_SPDX + "attributionText" SPDX_PACKAGE_VERIFICATION_CODE_VALUE = NS_SPDX + "packageVerificationCodeValue" SPDX_PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE = NS_SPDX + "packageVerificationCodeExcludedFile" SPDX_RELATED_SPDX_ELEMENT = NS_SPDX + "relatedSpdxElement" SPDX_RELATIONSHIP_TYPE = NS_SPDX + "relationshipType" SPDX_SNIPPET_FROM_FILE = NS_SPDX + "snippetFromFile" SPDX_LICENSE_INFO_IN_SNIPPET = NS_SPDX + "licenseInfoInSnippet" SPDX_RANGE = NS_SPDX + "range" SPDX_REVIEWER = NS_SPDX + "reviewer" SPDX_REVIEW_DATE = NS_SPDX + "reviewDate" SPDX_SNIPPET = NS_SPDX + "Snippet" SPDX_ALGORITHM = NS_SPDX + "algorithm" SPDX_CHECKSUM_VALUE = NS_SPDX + "checksumValue" SPDX_REFERENCE_CATEGORY = NS_SPDX + "referenceCategory" SPDX_REFERENCE_CATEGORY_PACKAGE_MANAGER = NS_SPDX + "referenceCategory_packageManager" SPDX_REFERENCE_CATEGORY_SECURITY = NS_SPDX + "referenceCategory_security" SPDX_REFERENCE_CATEGORY_OTHER = NS_SPDX + "referenceCategory_other" SPDX_REFERENCE_TYPE = NS_SPDX + "referenceType" SPDX_REFERENCE_LOCATOR = NS_SPDX + "referenceLocator" SPDX_ANNOTATION_DATE = NS_SPDX + "annotationDate" SPDX_ANNOTATOR = NS_SPDX + "annotator" SPDX_MEMBER = NS_SPDX + "member" SPDX_DISJUNCTIVE_LICENSE_SET = NS_SPDX + "DisjunctiveLicenseSet" SPDX_CONJUNCTIVE_LICENSE_SET = NS_SPDX + "ConjunctiveLicenseSet" SPDX_EXTRACTED_LICENSING_INFO = NS_SPDX + "ExtractedLicensingInfo" SPDX_SIMPLE_LICENSING_INFO = NS_SPDX + "SimpleLicensingInfo" SPDX_NONE_CAPS = NS_SPDX + "NONE" SPDX_NOASSERTION_CAPS = NS_SPDX + "NOASSERTION" SPDX_NONE_SMALL = NS_SPDX + "none" SPDX_NOASSERTION_SMALL = NS_SPDX + "noassertion" SPDX_LICENSE = NS_SPDX + "License" SPDX_LISTED_LICENSE = NS_SPDX + "ListedLicense" SPDX_EXAMPLE = NS_SPDX + "example" SPDX_IS_OSI_APPROVED = NS_SPDX + "isOsiApproved" SPDX_STANDARD_LICENSE_TEMPLATE = NS_SPDX + "standardLicenseTemplate" SPDX_IS_DEPRECATED_LICENSE_ID = NS_SPDX + "isDeprecatedLicenseId" SPDX_IS_FSF_LIBRE = NS_SPDX + "isFsfLibre" SPDX_LICENSE_TEXT = NS_SPDX + "licenseText" SPDX_STANDARD_LICENSE_HEADER = NS_SPDX + "standardLicenseHeader" SPDX_LICENSE_EXCEPTION_ID = NS_SPDX + "licenseExceptionId" SPDX_LICENSE_EXCEPTION_TEXT = NS_SPDX + "licenseExceptionText" SPDX_LICENSE_EXCEPTION = NS_SPDX + "licenseException" SPDX_WITH_EXCEPTION_OPERATOR = NS_SPDX + "WithExceptionOperator" SPDX_OR_LATER_OPERATOR = NS_SPDX + "OrLaterOperator" SPDX_STANDARD_LICENSE_HEADER_TEMPLATE = NS_SPDX + "standardLicenseHeaderTemplate" // RDFS properties RDFS_COMMENT = NS_RDFS + "comment" RDFS_SEE_ALSO = NS_RDFS + "seeAlso" // RDF properties RDF_TYPE = NS_RDF + "type" // DOAP properties DOAP_HOMEPAGE = NS_DOAP + "homepage" DOAP_NAME = NS_DOAP + "name" // PTR properties PTR_START_END_POINTER = NS_PTR + "StartEndPointer" PTR_START_POINTER = NS_PTR + "startPointer" PTR_BYTE_OFFSET_POINTER = NS_PTR + "ByteOffsetPointer" PTR_LINE_CHAR_POINTER = NS_PTR + "LineCharPointer" PTR_REFERENCE = NS_PTR + "reference" PTR_OFFSET = NS_PTR + "offset" PTR_LINE_NUMBER = NS_PTR + "lineNumber" PTR_END_POINTER = NS_PTR + "endPointer" // prefixes PREFIX_RELATIONSHIP_TYPE = "relationshipType_" ) func AllRelationshipTypes() []string { return []string{ "amendment", "ancestorOf", "buildDependencyOf", "buildToolOf", "containedBy", "contains", "copyOf", "dataFile", "dataFileOf", "dependencyManifestOf", "dependencyOf", "dependsOn", "descendantOf", "describedBy", "describes", "devDependencyOf", "devToolOf", "distributionArtifact", "documentation", "dynamicLink", "exampleOf", "expandedFromArchive", "fileAdded", "fileDeleted", "fileModified", "generatedFrom", "generates", "hasPrerequisite", "metafileOf", "optionalComponentOf", "optionalDependencyOf", "other", "packageOf", "patchApplied", "patchFor", "prerequisiteFor", "providedDependencyOf", "runtimeDependencyOf", "staticLink", "testDependencyOf", "testOf", "testToolOf", "testcaseOf", "variantOf", } } func AllStandardLicenseIDS() []string { return []string{ "0BSD", "389-exception", "AAL", "Abstyles", "Adobe-2006", "Adobe-Glyph", "ADSL", "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0", "Afmparse", "AGPL-1.0-only", "AGPL-1.0-or-later", "AGPL-1.0", "AGPL-3.0-only", "AGPL-3.0-or-later", "AGPL-3.0", "Aladdin", "AMDPLPA", "AML", "AMPAS", "ANTLR-PD", "Apache-1.0", "Apache-1.1", "Apache-2.0", "APAFML", "APL-1.0", "APSL-1.0", "APSL-1.1", "APSL-1.2", "APSL-2.0", "Artistic-1.0-cl8", "Artistic-1.0-Perl", "Artistic-1.0", "Artistic-2.0", "", "Autoconf-exception-2.0", "Autoconf-exception-3.0", "Bahyph", "Barr", "Beerware", "Bison-exception-2.2", "BitTorrent-1.0", "BitTorrent-1.1", "blessing", "BlueOak-1.0.0", "Bootloader-exception", "Borceux", "BSD-1-Clause", "BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", "BSD-2-Clause-Patent", "BSD-2-Clause-Views", "BSD-2-Clause", "BSD-3-Clause-Attribution", "BSD-3-Clause-Clear", "BSD-3-Clause-LBNL", "BSD-3-Clause-No-Nuclear-License-2014", "BSD-3-Clause-No-Nuclear-License", "BSD-3-Clause-No-Nuclear-Warranty", "BSD-3-Clause-Open-MPI", "BSD-3-Clause", "BSD-4-Clause-UC", "BSD-4-Clause", "BSD-Protection", "BSD-Source-Code", "BSL-1.0", "bzip2-1.0.5", "bzip2-1.0.6", "CAL-1.0-Combined-Work-Exception", "CAL-1.0", "Caldera", "CATOSL-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5", "CC-BY-3.0-AT", "CC-BY-3.0", "CC-BY-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5", "CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0", "CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0-IGO", "CC-BY-NC-ND-3.0", "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0", "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0", "CC-BY-ND-4.0", "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0-AT", "CC-BY-SA-3.0", "CC-BY-SA-4.0", "CC-PDDC", "CC0-1.0", "CDDL-1.0", "CDDL-1.1", "CDLA-Permissive-1.0", "CDLA-Sharing-1.0", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-2.1", "CECILL-B", "CECILL-C", "CERN-OHL-1.1", "CERN-OHL-1.2", "CERN-OHL-P-2.0", "CERN-OHL-S-2.0", "CERN-OHL-W-2.0", "ClArtistic", "Classpath-exception-2.0", "CLISP-exception-2.0", "CNRI-Jython", "CNRI-Python-GPL-Compatible", "CNRI-Python", "Condor-1.1", "copyleft-next-0.3.0", "copyleft-next-0.3.1", "CPAL-1.0", "CPL-1.0", "CPOL-1.02", "Crossword", "CrystalStacker", "CUA-OPL-1.0", "Cube", "curl", "D-FSL-1.0", "diffmark", "DigiRule-FOSS-exception", "DOC", "Dotseqn", "DSDP", "dvipdfm", "ECL-1.0", "ECL-2.0", "eCos-2.0", "eCos-exception-2.0", "EFL-1.0", "EFL-2.0", "eGenix", "Entessa", "EPICS", "EPL-1.0", "EPL-2.0", "ErlPL-1.1", "etalab-2.0", "EUDatagrid", "EUPL-1.0", "EUPL-1.1", "EUPL-1.2", "Eurosym", "Fair", "Fawkes-Runtime-exception", "FLTK-exception", "Font-exception-2.0", "Frameworx-1.0", "FreeImage", "freertos-exception-2.0", "FSFAP", "FSFUL", "FSFULLR", "FTL", "GCC-exception-2.0", "GCC-exception-3.1", "GFDL-1.1-invariants-only", "GFDL-1.1-invariants-or-later", "GFDL-1.1-no-invariants-only", "GFDL-1.1-no-invariants-or-later", "GFDL-1.1-only", "GFDL-1.1-or-later", "GFDL-1.1", "GFDL-1.2-invariants-only", "GFDL-1.2-invariants-or-later", "GFDL-1.2-no-invariants-only", "GFDL-1.2-no-invariants-or-later", "GFDL-1.2-only", "GFDL-1.2-or-later", "GFDL-1.2", "GFDL-1.3-invariants-only", "GFDL-1.3-invariants-or-later", "GFDL-1.3-no-invariants-only", "GFDL-1.3-no-invariants-or-later", "GFDL-1.3-only", "GFDL-1.3-or-later", "GFDL-1.3", "Giftware", "GL2PS", "Glide", "Glulxe", "GLWTPL", "gnu-javamail-exception", "gnuplot", "GPL-1.0+", "GPL-1.0-only", "GPL-1.0-or-later", "GPL-1.0", "GPL-2.0+", "GPL-2.0-only", "GPL-2.0-or-later", "GPL-2.0-with-autoconf-exception", "GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception", "GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-2.0", "GPL-3.0+", "GPL-3.0-linking-exception", "GPL-3.0-linking-source-exception", "GPL-3.0-only", "GPL-3.0-or-later", "GPL-3.0-with-autoconf-exception", "GPL-3.0-with-GCC-exception", "GPL-3.0", "GPL-CC-1.0", "gSOAP-1.3b", "HaskellReport", "Hippocratic-2.1", "HPND-sell-variant", "HPND", "i2p-gpl-java-exception", "IBM-pibs", "ICU", "IJG", "ImageMagick", "iMatix", "Imlib2", "Info-ZIP", "Intel-ACPI", "Intel", "Interbase-1.0", "IPA", "IPL-1.0", "ISC", "JasPer-2.0", "JPNIC", "JSON", "LAL-1.2", "LAL-1.3", "Latex2e", "Leptonica", "LGPL-2.0+", "LGPL-2.0-only", "LGPL-2.0-or-later", "LGPL-2.0", "LGPL-2.1+", "LGPL-2.1-only", "LGPL-2.1-or-later", "LGPL-2.1", "LGPL-3.0+", "LGPL-3.0-linking-exception", "LGPL-3.0-only", "LGPL-3.0-or-later", "LGPL-3.0", "LGPLLR", "libpng-2.0", "Libpng", "libselinux-1.0", "libtiff", "Libtool-exception", "licenses", "LiLiQ-P-1.1", "LiLiQ-R-1.1", "LiLiQ-Rplus-1.1", "Linux-OpenIB", "Linux-syscall-note", "LLVM-exception", "LPL-1.0", "LPL-1.02", "LPPL-1.0", "LPPL-1.1", "LPPL-1.2", "LPPL-1.3a", "LPPL-1.3c", "LZMA-exception", "MakeIndex", "mif-exception", "MirOS", "MIT-0", "MIT-advertising", "MIT-CMU", "MIT-enna", "MIT-feh", "MIT", "MITNFA", "Motosoto", "mpich2", "MPL-1.0", "MPL-1.1", "MPL-2.0-no-copyleft-exception", "MPL-2.0", "MS-PL", "MS-RL", "MTLL", "MulanPSL-1.0", "MulanPSL-2.0", "Multics", "Mup", "NASA-1.3", "Naumen", "NBPL-1.0", "NCGL-UK-2.0", "NCSA", "Net-SNMP", "NetCDF", "Newsletr", "NGPL", "NIST-PD-fallback", "NIST-PD", "NLOD-1.0", "NLPL", "Nokia-Qt-exception-1.1", "Nokia", "NOSL", "Noweb", "NPL-1.0", "NPL-1.1", "NPOSL-3.0", "NRL", "NTP-0", "NTP", "Nunit", "O-UDA-1.0", "OCaml-LGPL-linking-exception", "OCCT-exception-1.0", "OCCT-PL", "OCLC-2.0", "ODbL-1.0", "ODC-By-1.0", "OFL-1.0-no-RFN", "OFL-1.0-RFN", "OFL-1.0", "OFL-1.1-no-RFN", "OFL-1.1-RFN", "OFL-1.1", "OGC-1.0", "OGL-Canada-2.0", "OGL-UK-1.0", "OGL-UK-2.0", "OGL-UK-3.0", "OGTSL", "OLDAP-1.1", "OLDAP-1.2", "OLDAP-1.3", "OLDAP-1.4", "OLDAP-2.0.1", "OLDAP-2.0", "OLDAP-2.1", "OLDAP-2.2.1", "OLDAP-2.2.2", "OLDAP-2.2", "OLDAP-2.3", "OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OLDAP-2.8", "OML", "", "OpenJDK-assembly-exception-1.0", "OpenSSL", "openvpn-openssl-exception", "OPL-1.0", "OSET-PL-2.1", "OSL-1.0", "OSL-1.1", "OSL-2.0", "OSL-2.1", "OSL-3.0", "Parity-6.0.0", "Parity-7.0.0", "PDDL-1.0", "PHP-3.0", "PHP-3.01", "Plexus", "PolyForm-Noncommercial-1.0.0", "PolyForm-Small-Business-1.0.0", "PostgreSQL", "PS-or-PDF-font-exception-20170817", "PSF-2.0", "psfrag", "psutils", "Python-2.0", "Qhull", "QPL-1.0", "Qt-GPL-exception-1.0", "Qt-LGPL-exception-1.1", "Qwt-exception-1.0", "Rdisc", "RHeCos-1.1", "RPL-1.1", "RPL-1.5", "RPSL-1.0", "RSA-MD", "RSCPL", "Ruby", "SAX-PD", "Saxpath", "SCEA", "Sendmail-8.23", "Sendmail", "SGI-B-1.0", "SGI-B-1.1", "SGI-B-2.0", "SHL-0.5", "SHL-0.51", "SHL-2.0", "SHL-2.1", "SimPL-2.0", "SISSL-1.2", "SISSL", "Sleepycat", "SMLNJ", "SMPPL", "SNIA", "Spencer-86", "Spencer-94", "Spencer-99", "SPL-1.0", "SSH-OpenSSH", "SSH-short", "SSPL-1.0", "StandardML-NJ", "SugarCRM-1.1.3", "Swift-exception", "SWL", "TAPR-OHL-1.0", "TCL", "TCP-wrappers", "TMate", "TORQUE-1.1", "TOSL", "TU-Berlin-1.0", "TU-Berlin-2.0", "u-boot-exception-2.0", "UCL-1.0", "Unicode-DFS-2015", "Unicode-DFS-2016", "Unicode-TOU", "Universal-FOSS-exception-1.0", "Unlicense", "UPL-1.0", "Vim", "VOSTROM", "VSL-1.0", "W3C-19980720", "W3C-20150513", "W3C", "Watcom-1.0", "Wsuipa", "WTFPL", "WxWindows-exception-3.1", "wxWindows", "X11", "Xerox", "XFree86-1.1", "xinetd", "Xnet", "xpp", "XSkat", "YPL-1.0", "YPL-1.1", "Zed", "Zend-2.0", "Zimbra-1.3", "Zimbra-1.4", "zlib-acknowledgement", "Zlib", "ZPL-1.1", "ZPL-2.0", "ZPL-2.1", } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/license_utils.go000066400000000000000000000071401463371440000234160ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" ) /* util methods for licenses and checksums below:*/ // Given the license URI, returns the name of the license defined // in the last part of the uri. // This function is susceptible to false-positives. func getLicenseStringFromURI(uri string) string { licenseEnd := strings.TrimSpace(getLastPartOfURI(uri)) lower := strings.ToLower(licenseEnd) if lower == "none" || lower == "noassertion" { return strings.ToUpper(licenseEnd) } return licenseEnd } // returns the checksum algorithm and it's value // In the newer versions, these two strings will be bound to a single checksum struct // whose pointer will be returned. func (parser *rdfParser2_3) getChecksumFromNode(checksumNode *gordfParser.Node) (algorithm common.ChecksumAlgorithm, value string, err error) { var checksumValue, checksumAlgorithm string for _, checksumTriple := range parser.nodeToTriples(checksumNode) { switch checksumTriple.Predicate.ID { case RDF_TYPE: continue case SPDX_CHECKSUM_VALUE: // cardinality: exactly 1 checksumValue = strings.TrimSpace(checksumTriple.Object.ID) case SPDX_ALGORITHM: // cardinality: exactly 1 checksumAlgorithm, err = getAlgorithmFromURI(checksumTriple.Object.ID) if err != nil { return } default: err = fmt.Errorf("unknown predicate '%s' while parsing checksum node", checksumTriple.Predicate.ID) return } } return common.ChecksumAlgorithm(checksumAlgorithm), checksumValue, nil } func getAlgorithmFromURI(algorithmURI string) (checksumAlgorithm string, err error) { fragment := getLastPartOfURI(algorithmURI) if !strings.HasPrefix(fragment, "checksumAlgorithm_") { return "", fmt.Errorf("checksum algorithm uri must begin with checksumAlgorithm_. found %s", fragment) } algorithm := strings.TrimPrefix(fragment, "checksumAlgorithm_") algorithm = strings.ToLower(strings.TrimSpace(algorithm)) switch algorithm { case "md2", "md4", "md5", "md6": checksumAlgorithm = strings.ToUpper(algorithm) case "sha1", "sha224", "sha256", "sha384", "sha512": checksumAlgorithm = strings.ToUpper(algorithm) default: return "", fmt.Errorf("unknown checksum algorithm %s", algorithm) } return } // from a list of licenses, it returns a // list of string representation of those licenses. func mapLicensesToStrings(licences []AnyLicenseInfo) []string { res := make([]string, len(licences)) for i, lic := range licences { res[i] = lic.ToLicenseString() } return res } /****** Type Functions ******/ // TODO: should probably add brackets while linearizing a nested license. func (lic ConjunctiveLicenseSet) ToLicenseString() string { return strings.Join(mapLicensesToStrings(lic.members), " AND ") } // TODO: should probably add brackets while linearizing a nested license. func (lic DisjunctiveLicenseSet) ToLicenseString() string { return strings.Join(mapLicensesToStrings(lic.members), " OR ") } func (lic ExtractedLicensingInfo) ToLicenseString() string { return lic.licenseID } func (operator OrLaterOperator) ToLicenseString() string { return operator.member.ToLicenseString() } func (lic License) ToLicenseString() string { return lic.licenseID } func (lic ListedLicense) ToLicenseString() string { return lic.licenseID } func (lic WithExceptionOperator) ToLicenseString() string { return lic.member.ToLicenseString() } func (lic SpecialLicense) ToLicenseString() string { return string(lic.value) } func (lic SimpleLicensingInfo) ToLicenseString() string { return lic.licenseID } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/license_utils_test.go000066400000000000000000000226241463371440000244610ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" ) func Test_getLicenseStringFromURI(t *testing.T) { // TestCase 1: NONE license input := SPDX_NONE_CAPS output := getLicenseStringFromURI(input) expectedOutput := "NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", expectedOutput, output) } // TestCase 2: NOASSERTION license input = SPDX_NOASSERTION_SMALL output = getLicenseStringFromURI(input) expectedOutput = "NOASSERTION" if output != expectedOutput { t.Errorf("expected: %s, found %s", expectedOutput, output) } // TestCase 3: Other license input = NS_SPDX + "LicenseRef-1" output = getLicenseStringFromURI(input) expectedOutput = "LicenseRef-1" if output != expectedOutput { t.Errorf("expected: %s, found %s", expectedOutput, output) } } func Test_rdfParser2_3_getChecksumFromNode(t *testing.T) { var parser *rdfParser2_3 var err error // TestCase 1: invalid checksum algorithm parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) checksumNode := parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getChecksumFromNode(checksumNode) if err == nil { t.Errorf("expected an error saying invalid checksum algorithm") } // TestCase 2: invalid predicate parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) checksumNode = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getChecksumFromNode(checksumNode) if err == nil { t.Errorf("expected an error saying invalid predicate") } // TestCase 3: valid input parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) checksumNode = parser.gordfParserObj.Triples[0].Subject algorithm, value, err := parser.getChecksumFromNode(checksumNode) if err != nil { t.Errorf("unexpected error: %v", err) } if algorithm != "SHA1" { t.Errorf("expected checksum algorithm to be sha1, found %s", algorithm) } expectedValue := "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" if value != expectedValue { t.Errorf("expected checksumValue to be %s, found %s", expectedValue, value) } } func Test_rdfParser2_3_getAlgorithmFromURI(t *testing.T) { var algorithmURI string var err error // TestCase 1: checksumAlgorithm uri doesn't start with checksumAlgorithm_ algorithmURI = NS_SPDX + "sha1" _, err = getAlgorithmFromURI(algorithmURI) if err == nil { t.Errorf("should've raised an error for algorithmURI that doesn't start with checksumAlgorithm_") } // TestCase 2: unknown checksum algorithm algorithmURI = NS_SPDX + "checksumAlgorithm_sha999" _, err = getAlgorithmFromURI(algorithmURI) if err == nil { t.Errorf("should've raised an error for invalid algorithm") } // TestCase 3: valid input algorithmURI = NS_SPDX + "checksumAlgorithm_sha256" algorithm, err := getAlgorithmFromURI(algorithmURI) if err != nil { t.Errorf("unexpected error: %v", err) } if algorithm != "SHA256" { t.Errorf("expected: SHA256, found: %s", algorithm) } } func Test_mapLicensesToStrings(t *testing.T) { // nothing much to test here. // just a dummy dry run. licenses := []AnyLicenseInfo{ SpecialLicense{ value: NONE, }, SpecialLicense{ value: NOASSERTION, }, } licenseStrings := mapLicensesToStrings(licenses) expectedLicenseStrings := []string{"NONE", "NOASSERTION"} if !reflect.DeepEqual(licenseStrings, expectedLicenseStrings) { t.Errorf("expected: %+v\nfound %+v", expectedLicenseStrings, licenseStrings) } } func TestConjunctiveLicenseSet_ToLicenseString(t *testing.T) { var lic ConjunctiveLicenseSet var output, expectedOutput string // TestCase 1: no license in the set lic = ConjunctiveLicenseSet{ members: nil, } output = lic.ToLicenseString() expectedOutput = "" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 2: single license in the set lic = ConjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 3: more than one license in the set. lic = ConjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, SpecialLicense{value: NONE}, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION AND NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 4: nested conjunctive license. lic = ConjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, ConjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: "LicenseRef-1"}, SpecialLicense{value: NONE}, }, }, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION AND LicenseRef-1 AND NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } } func TestDisjunctiveLicenseSet_ToLicenseString(t *testing.T) { var lic DisjunctiveLicenseSet var output, expectedOutput string // TestCase 1: no license in the set lic = DisjunctiveLicenseSet{ members: nil, } output = lic.ToLicenseString() expectedOutput = "" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 2: single license in the set lic = DisjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 3: more than one license in the set. lic = DisjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, SpecialLicense{value: NONE}, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION OR NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } // TestCase 4: nested conjunctive license. lic = DisjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: NOASSERTION}, DisjunctiveLicenseSet{ members: []AnyLicenseInfo{ SpecialLicense{value: "LicenseRef-1"}, SpecialLicense{value: NONE}, }, }, }, } output = lic.ToLicenseString() expectedOutput = "NOASSERTION OR LicenseRef-1 OR NONE" if output != expectedOutput { t.Errorf("expected: %s, found %s", output, expectedOutput) } } func TestExtractedLicensingInfo_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) extractedLicense := ExtractedLicensingInfo{ SimpleLicensingInfo: SimpleLicensingInfo{ licenseID: "license", }, extractedText: "extracted Text", } expectedOutput := "license" output := extractedLicense.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestOrLaterOperator_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) orLater := OrLaterOperator{ member: SimpleLicensingInfo{ licenseID: "license", }, } expectedOutput := "license" output := orLater.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestLicense_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) license := License{ SimpleLicensingInfo: SimpleLicensingInfo{ licenseID: "license", }, } expectedOutput := "license" output := license.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestListedLicense_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) ll := ListedLicense{License{ SimpleLicensingInfo: SimpleLicensingInfo{ licenseID: "license", }, }, } expectedOutput := "license" output := ll.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestWithExceptionOperator_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) withException := WithExceptionOperator{ member: SimpleLicensingInfo{ licenseID: "license", }, licenseException: LicenseException{}, } expectedOutput := "license" output := withException.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestSpecialLicense_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) specialLicense := SpecialLicense{ value: "license", } expectedOutput := "license" output := specialLicense.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } func TestSimpleLicensingInfo_ToLicenseString(t *testing.T) { // nothing to test (just a dry run) sli := SimpleLicensingInfo{ licenseID: "license", } expectedOutput := "license" output := sli.ToLicenseString() if output != expectedOutput { t.Errorf("expected: %s, found: %s", expectedOutput, output) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_annotation.go000066400000000000000000000046001463371440000241160ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "errors" "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // creates a new instance of annotation and sets the annotation attributes // associated with the given node. // The newly created annotation is appended to the doc. func (parser *rdfParser2_3) parseAnnotationFromNode(node *gordfParser.Node) (err error) { ann := &spdx.Annotation{} for _, subTriple := range parser.nodeToTriples(node) { switch subTriple.Predicate.ID { case SPDX_ANNOTATOR: // cardinality: exactly 1 err = setAnnotatorFromString(subTriple.Object.ID, ann) case SPDX_ANNOTATION_DATE: // cardinality: exactly 1 ann.AnnotationDate = subTriple.Object.ID case RDFS_COMMENT: // cardinality: exactly 1 ann.AnnotationComment = subTriple.Object.ID case SPDX_ANNOTATION_TYPE: // cardinality: exactly 1 err = setAnnotationType(subTriple.Object.ID, ann) case RDF_TYPE: // cardinality: exactly 1 continue default: err = fmt.Errorf("unknown predicate %s while parsing annotation", subTriple.Predicate.ID) } if err != nil { return err } } return setAnnotationToParser(parser, ann) } func setAnnotationToParser(parser *rdfParser2_3, annotation *spdx.Annotation) error { if parser.doc == nil { return errors.New("uninitialized spdx document") } if parser.doc.Annotations == nil { parser.doc.Annotations = []*spdx.Annotation{} } parser.doc.Annotations = append(parser.doc.Annotations, annotation) return nil } // annotator is of type [Person|Organization|Tool]:String func setAnnotatorFromString(annotatorString string, ann *spdx.Annotation) error { subkey, subvalue, err := ExtractSubs(annotatorString, ":") if err != nil { return err } if subkey == "Person" || subkey == "Organization" || subkey == "Tool" { ann.Annotator.AnnotatorType = subkey ann.Annotator.Annotator = subvalue return nil } return fmt.Errorf("unrecognized Annotator type %v while parsing annotation", subkey) } // it can be NS_SPDX+annotationType_[review|other] func setAnnotationType(annType string, ann *spdx.Annotation) error { switch annType { case SPDX_ANNOTATION_TYPE_OTHER: ann.AnnotationType = "OTHER" case SPDX_ANNOTATION_TYPE_REVIEW: ann.AnnotationType = "REVIEW" default: return fmt.Errorf("unknown annotation type %s", annType) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_annotation_test.go000066400000000000000000000140621463371440000251600ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func Test_setAnnotatorFromString(t *testing.T) { // TestCase 1: Empty String must raise an error ann := &spdx.Annotation{} input := "" err := setAnnotatorFromString(input, ann) if err == nil { t.Error("should've raised an error for an empty string") } // TestCase 2: Invalid annotator type ann = &spdx.Annotation{} input = "Company: some_company" err = setAnnotatorFromString(input, ann) if err == nil { t.Errorf("should've raised an error for an unknown annotator type") } // TestCase 3: Valid annotator ann = &spdx.Annotation{} input = "Person: Rishabh" err = setAnnotatorFromString(input, ann) if err != nil { t.Errorf("unexpected error for a valid annotator") } if ann.Annotator.AnnotatorType != "Person" { t.Errorf("wrnog annotator type: expected: %s, found: %s", "Person", ann.Annotator) } if ann.Annotator.Annotator != "Rishabh" { t.Errorf("wrong annotator: expected: %s, found: %s", "Rishabh", ann.Annotator) } } func Test_setAnnotationType(t *testing.T) { ann := &spdx.Annotation{} // TestCase 1: invalid input (empty annotationType) err := setAnnotationType("", ann) if err == nil { t.Errorf("expected an error for empty input") } // TestCase 2: invalid input (unknown annotation type) err = setAnnotationType(NS_SPDX+"annotationType_unknown", ann) if err == nil { t.Errorf("expected an error for invalid annotationType") } // TestCase 3: valid input (annotationType_other) err = setAnnotationType(SPDX_ANNOTATION_TYPE_OTHER, ann) if err != nil { t.Errorf("unexpected error: %v", err) } if ann.AnnotationType != "OTHER" { t.Errorf("expected: OTHER, found: %s", ann.AnnotationType) } // TestCase 4: valid input (annotationType_review) err = setAnnotationType(SPDX_ANNOTATION_TYPE_REVIEW, ann) if err != nil { t.Errorf("unexpected error: %v", err) } if ann.AnnotationType != "REVIEW" { t.Errorf("expected: REVIEW, found: %s", ann.AnnotationType) } } func Test_setAnnotationToParser(t *testing.T) { // TestCase 1: doc is nil (must raise an error) parser, _ := parserFromBodyContent(``) parser.doc = nil err := setAnnotationToParser(parser, &spdx.Annotation{}) if err == nil { t.Errorf("empty doc should've raised an error") } // TestCase 2: empty annotations should create a new annotations // list and append the input to it. parser, _ = parserFromBodyContent(``) parser.doc.Annotations = nil err = setAnnotationToParser(parser, &spdx.Annotation{}) if err != nil { t.Errorf("unexpected error: %v", err) } if len(parser.doc.Annotations) != 1 { t.Errorf("expected doc to have 1 annotation, found %d", len(parser.doc.Annotations)) } } func Test_rdfParser2_3_parseAnnotationFromNode(t *testing.T) { // TestCase 1: invalid annotator must raise an error parser, _ := parserFromBodyContent(` 2010-01-29T18:30:22Z Document level annotation Company: some company `) node := parser.gordfParserObj.Triples[0].Subject err := parser.parseAnnotationFromNode(node) if err == nil { t.Errorf("wrong annotator type should've raised an error") } // TestCase 2: wrong annotation type should raise an error parser, _ = parserFromBodyContent(` 2010-01-29T18:30:22Z Document level annotation Person: Jane Doe `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseAnnotationFromNode(node) if err == nil { t.Errorf("wrong annotation type should've raised an error") } // TestCase 3: unknown predicate should also raise an error parser, _ = parserFromBodyContent(` 2010-01-29T18:30:22Z Document level annotation Person: Jane Doe `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseAnnotationFromNode(node) if err == nil { t.Errorf("unknown predicate must raise an error") } // TestCase 4: completely valid annotation parser, _ = parserFromBodyContent(` 2010-01-29T18:30:22Z Document level annotation Person: Jane Doe `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseAnnotationFromNode(node) if err != nil { t.Errorf("error parsing valid a annotation") } if n := len(parser.doc.Annotations); n != 1 { t.Errorf("document should've had only one annotation, found %d", n) } ann := parser.doc.Annotations[0] // validating all the attributes of the annotations expectedComment := "Document level annotation" if ann.AnnotationComment != expectedComment { t.Errorf(`expected: "%s", found "%s"`, expectedComment, ann.AnnotationComment) } expectedDate := "2010-01-29T18:30:22Z" if expectedDate != ann.AnnotationDate { t.Errorf(`expected: "%s", found "%s"`, expectedDate, ann.AnnotationDate) } expectedAnnotator := "Jane Doe" if expectedAnnotator != ann.Annotator.Annotator { t.Errorf(`expected: "%s", found "%s"`, expectedAnnotator, ann.Annotator) } if ann.Annotator.AnnotatorType != "Person" { t.Errorf(`expected: "%s", found "%s"`, "Person", ann.Annotator.AnnotatorType) } expectedAnnotationType := "OTHER" if expectedAnnotationType != ann.AnnotationType { t.Errorf(`expected: "%s", found "%s"`, expectedAnnotationType, ann.AnnotationType) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_creation_info.go000066400000000000000000000030031463371440000245570ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // Cardinality: Mandatory, one. func (parser *rdfParser2_3) parseCreationInfoFromNode(ci *spdx.CreationInfo, node *gordfParser.Node) error { for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case SPDX_LICENSE_LIST_VERSION: // 2.7 // cardinality: max 1 ci.LicenseListVersion = triple.Object.ID case SPDX_CREATOR: // 2.8 // cardinality: min 1 err := setCreator(triple.Object.ID, ci) if err != nil { return err } case SPDX_CREATED: // 2.9 // cardinality: exactly 1 ci.Created = triple.Object.ID case RDFS_COMMENT: // 2.10 ci.CreatorComment = triple.Object.ID case RDF_TYPE: continue default: return fmt.Errorf("unknown predicate %v while parsing a creation info", triple.Predicate) } } return nil } func setCreator(creatorStr string, ci *spdx.CreationInfo) error { entityType, entity, err := ExtractSubs(creatorStr, ":") if err != nil { return fmt.Errorf("error setting creator of a creation info: %s", err) } creator := common.Creator{Creator: entity} switch entityType { case "Person", "Organization", "Tool": creator.CreatorType = entityType default: return fmt.Errorf("unknown creatorType %v in a creation info", entityType) } ci.Creators = append(ci.Creators, creator) return nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_creation_info_test.go000066400000000000000000000064141463371440000256270ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func Test_setCreator(t *testing.T) { // TestCase 1: invalid creator (empty) input := "" err := setCreator(input, &spdx.CreationInfo{}) if err == nil { t.Errorf("shoud've raised an error due to invalid input") } // TestCase 2: invalid entity type input = "Company: some company" err = setCreator(input, &spdx.CreationInfo{}) if err == nil { t.Errorf("shoud've raised an error due to unknown entity type") } // TestCase 3: valid input input = "Person: Jane Doe" ci := &spdx.CreationInfo{} err = setCreator(input, ci) if err != nil { t.Errorf("error parsing a valid input: %v", err) } if len(ci.Creators) != 1 { t.Errorf("creationInfo should've had 1 creatorPersons, found %d", len(ci.Creators)) } expectedPerson := "Jane Doe" if ci.Creators[0].Creator != expectedPerson { t.Errorf("expected %s, found %s", expectedPerson, ci.Creators[0]) } } func Test_rdfParser2_3_parseCreationInfoFromNode(t *testing.T) { // TestCase 1: invalid creator must raise an error parser, _ := parserFromBodyContent(` 2.6 Person Unknown 2018-08-24T19:55:34Z `) ciNode := parser.gordfParserObj.Triples[0].Subject err := parser.parseCreationInfoFromNode(&spdx.CreationInfo{}, ciNode) if err == nil { t.Errorf("invalid creator must raise an error") } // TestCase 2: unknown predicate must also raise an error parser, _ = parserFromBodyContent(` 2.6 Person: fossy (y) Organization: Tool: spdx2 2018-08-24T19:55:34Z `) ciNode = parser.gordfParserObj.Triples[0].Subject err = parser.parseCreationInfoFromNode(&spdx.CreationInfo{}, ciNode) if err == nil { t.Errorf("unknown predicate must raise an error") } // TestCase 2: unknown predicate must also raise an error parser, _ = parserFromBodyContent(` 2.6 Person: fossy 2018-08-24T19:55:34Z comment `) ciNode = parser.gordfParserObj.Triples[0].Subject ci := &spdx.CreationInfo{} err = parser.parseCreationInfoFromNode(ci, ciNode) if err != nil { t.Errorf("unexpected error: %v", err) } if ci.LicenseListVersion != "2.6" { t.Errorf(`expected %s, found %s`, "2.6", ci.LicenseListVersion) } n := len(ci.Creators) if n != 1 { t.Errorf("expected 1 creatorPersons, found %d", n) } if ci.Creators[0].Creator != "fossy" { t.Errorf("expected %s, found %s", "fossy", ci.Creators[0].Creator) } expectedCreated := "2018-08-24T19:55:34Z" if ci.Created != expectedCreated { t.Errorf("expected %s, found %s", expectedCreated, ci.Created) } expectedComment := "comment" if ci.CreatorComment != expectedComment { t.Errorf("expected %s, found %s", expectedComment, ci.CreatorComment) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_file.go000066400000000000000000000164521463371440000226730ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // returns a file instance and the error if any encountered. func (parser *rdfParser2_3) getFileFromNode(fileNode *gordfParser.Node) (file *spdx.File, err error) { file = &spdx.File{} currState := parser.cache[fileNode.ID] if currState == nil { // this is the first time we are seeing this node. parser.cache[fileNode.ID] = &nodeState{ object: file, Color: WHITE, } } else if currState.Color == GREY { // we have already started parsing this file node and we needn't parse it again. return currState.object.(*spdx.File), nil } // setting color to grey to indicate that we've started parsing this node. parser.cache[fileNode.ID].Color = GREY // setting color to black just before function returns to the caller to // indicate that parsing current node is complete. defer func() { parser.cache[fileNode.ID].Color = BLACK }() err = setFileIdentifier(fileNode.ID, file) // 4.2 if err != nil { return nil, err } if existingFile := parser.files[file.FileSPDXIdentifier]; existingFile != nil { file = existingFile } for _, subTriple := range parser.nodeToTriples(fileNode) { switch subTriple.Predicate.ID { case SPDX_FILE_NAME: // 4.1 // cardinality: exactly 1 file.FileName = subTriple.Object.ID case SPDX_NAME: // cardinality: exactly 1 // TODO: check where it will be set in the golang-tools spdx-data-model case RDF_TYPE: // cardinality: exactly 1 case SPDX_FILE_TYPE: // 4.3 // cardinality: min 0 fileType := "" fileType, err = parser.getFileTypeFromUri(subTriple.Object.ID) file.FileTypes = append(file.FileTypes, fileType) case SPDX_CHECKSUM: // 4.4 // cardinality: min 1 err = parser.setFileChecksumFromNode(file, subTriple.Object) case SPDX_LICENSE_CONCLUDED: // 4.5 // cardinality: (exactly 1 anyLicenseInfo) or (None) or (Noassertion) anyLicense, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing licenseConcluded: %v", err) } file.LicenseConcluded = anyLicense.ToLicenseString() case SPDX_LICENSE_INFO_IN_FILE: // 4.6 // cardinality: min 1 lic, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing licenseInfoInFile: %v", err) } file.LicenseInfoInFiles = append(file.LicenseInfoInFiles, lic.ToLicenseString()) case SPDX_LICENSE_COMMENTS: // 4.7 // cardinality: max 1 file.LicenseComments = subTriple.Object.ID // TODO: allow copyright text to be of type NOASSERTION case SPDX_COPYRIGHT_TEXT: // 4.8 // cardinality: exactly 1 file.FileCopyrightText = subTriple.Object.ID case SPDX_LICENSE_INFO_FROM_FILES: // TODO: implement it. It is not defined in the tools-golang model. // deprecated artifactOf (see sections 4.9, 4.10, 4.11) case SPDX_ARTIFACT_OF: // cardinality: min 0 var artifactOf *spdx.ArtifactOfProject artifactOf, err = parser.getArtifactFromNode(subTriple.Object) file.ArtifactOfProjects = append(file.ArtifactOfProjects, artifactOf) case RDFS_COMMENT: // 4.12 // cardinality: max 1 file.FileComment = subTriple.Object.ID case SPDX_NOTICE_TEXT: // 4.13 // cardinality: max 1 file.FileNotice = getNoticeTextFromNode(subTriple.Object) case SPDX_FILE_CONTRIBUTOR: // 4.14 // cardinality: min 0 file.FileContributors = append(file.FileContributors, subTriple.Object.ID) case SPDX_FILE_DEPENDENCY: // cardinality: min 0 newFile, err := parser.getFileFromNode(subTriple.Object) if err != nil { return nil, fmt.Errorf("error setting a file dependency in a file: %v", err) } file.FileDependencies = append(file.FileDependencies, string(newFile.FileSPDXIdentifier)) case SPDX_ATTRIBUTION_TEXT: // cardinality: min 0 file.FileAttributionTexts = append(file.FileAttributionTexts, subTriple.Object.ID) case SPDX_ANNOTATION: // cardinality: min 0 err = parser.parseAnnotationFromNode(subTriple.Object) case SPDX_RELATIONSHIP: // cardinality: min 0 err = parser.parseRelationship(subTriple) default: return nil, fmt.Errorf("unknown triple predicate id %s", subTriple.Predicate.ID) } if err != nil { return nil, err } } parser.files[file.FileSPDXIdentifier] = file return file, nil } func (parser *rdfParser2_3) setFileChecksumFromNode(file *spdx.File, checksumNode *gordfParser.Node) error { checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(checksumNode) if err != nil { return fmt.Errorf("error parsing checksumNode of a file: %v", err) } if file.Checksums == nil { file.Checksums = []common.Checksum{} } switch checksumAlgorithm { case common.SHA1, common.SHA224, common.SHA256, common.SHA384, common.SHA512, common.MD2, common.MD4, common.MD5, common.MD6, common.SHA3_256, common.SHA3_384, common.SHA3_512, common.BLAKE2b_256, common.BLAKE2b_384, common.BLAKE2b_512, common.BLAKE3, common.ADLER32: file.Checksums = append(file.Checksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue}) case "": return fmt.Errorf("empty checksum algorithm and value") default: return fmt.Errorf("unknown checksumAlgorithm %s for a file", checksumAlgorithm) } return nil } func (parser *rdfParser2_3) getArtifactFromNode(node *gordfParser.Node) (*spdx.ArtifactOfProject, error) { artifactOf := &spdx.ArtifactOfProject{} // setting artifactOfProjectURI attribute (which is optional) if node.NodeType == gordfParser.IRI { artifactOf.URI = node.ID } // parsing rest triples and attributes of the artifact. for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case RDF_TYPE: case DOAP_HOMEPAGE: artifactOf.HomePage = triple.Object.ID case DOAP_NAME: artifactOf.Name = triple.Object.ID default: return nil, fmt.Errorf("error parsing artifactOf predicate %s", triple.Predicate.ID) } } return artifactOf, nil } // TODO: check if the filetype is valid. func (parser *rdfParser2_3) getFileTypeFromUri(uri string) (string, error) { // fileType is given as a uri. for example: http://spdx.org/rdf/terms#fileType_text lastPart := getLastPartOfURI(uri) if !strings.HasPrefix(lastPart, "fileType_") { return "", fmt.Errorf("fileType Uri must begin with fileTYpe_. found: %s", lastPart) } return strings.TrimPrefix(lastPart, "fileType_"), nil } // populates parser.doc.Files by a list of files which are not // associated with a package by the hasFile attribute // assumes: all the packages are already parsed. func (parser *rdfParser2_3) setUnpackagedFiles() { for fileID := range parser.files { if !parser.assocWithPackage[fileID] { parser.doc.Files = append(parser.doc.Files, parser.files[fileID]) } } } func setFileIdentifier(idURI string, file *spdx.File) (err error) { idURI = strings.TrimSpace(idURI) uriFragment := getLastPartOfURI(idURI) file.FileSPDXIdentifier, err = ExtractElementID(uriFragment) if err != nil { return fmt.Errorf("error setting file identifier: %s", err) } return nil } func getNoticeTextFromNode(node *gordfParser.Node) string { switch node.ID { case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL: return "NOASSERTION" default: return node.ID } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_file_test.go000066400000000000000000000713011463371440000237240ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "bufio" "strings" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" rdfloader2 "github.com/spdx/gordf/rdfloader/xmlreader" gordfWriter "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // content is the tags within the rdf:RDF tag // pads the content with the enclosing rdf:RDF tag func wrapIntoTemplate(content string) string { header := `` footer := `` return header + content + footer } func parserFromBodyContent(content string) (*rdfParser2_3, error) { rdfContent := wrapIntoTemplate(content) xmlreader := rdfloader2.XMLReaderFromFileObject(bufio.NewReader(strings.NewReader(rdfContent))) rootBlock, err := xmlreader.Read() if err != nil { return nil, err } parser := gordfParser.New() err = parser.Parse(rootBlock) if err != nil { return nil, err } nodeToTriples := gordfWriter.GetNodeToTriples(parser.Triples) rdfParser := NewParser2_3(parser, nodeToTriples) return rdfParser, err } func Test_rdfParser2_3_getArtifactFromNode(t *testing.T) { // TestCase 1: artifactOf without project URI rdfParser, err := parserFromBodyContent( ` http://www.openjena.org/ Jena `) if err != nil { t.Errorf("unexpected error while parsing a valid example: %v", err) } artifactOfNode := gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object artifact, err := rdfParser.getArtifactFromNode(artifactOfNode) if err != nil { t.Errorf("error parsing a valid artifactOf node: %v", err) } if artifact.Name != "Jena" { t.Errorf("expected name of artifact: %s, found: %s", "Jena", artifact.Name) } expectedHomePage := "http://www.openjena.org/" if artifact.HomePage != expectedHomePage { t.Errorf("wrong artifact homepage. Expected: %s, found: %s", expectedHomePage, artifact.HomePage) } if artifact.URI != "" { t.Errorf("wrong artifact URI. Expected: %s, found: %s", "", artifact.URI) } // TestCase 2: artifactOf with a Project URI rdfParser, err = parserFromBodyContent( ` http://www.openjena.org/ Jena `) if err != nil { t.Errorf("unexpected error while parsing a valid example: %v", err) } artifactOfNode = gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object artifact, err = rdfParser.getArtifactFromNode(artifactOfNode) if err != nil { t.Errorf("error parsing a valid artifactOf node: %v", err) } expectedURI := "http://subversion.apache.org/doap.rdf" if artifact.URI != expectedURI { t.Errorf("wrong artifact URI. Expected: %s, found: %s", expectedURI, artifact.URI) } // TestCase 3: artifactOf with unknown predicate rdfParser, err = parserFromBodyContent( ` http://www.openjena.org/ Jena `) if err != nil { t.Errorf("unexpected error while parsing a valid example: %v", err) } artifactOfNode = gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object _, err = rdfParser.getArtifactFromNode(artifactOfNode) if err == nil { t.Errorf("must've raised an error for an invalid predicate") } } func Test_rdfParser2_3_getFileTypeFromUri(t *testing.T) { rdfParser, _ := parserFromBodyContent(``) // TestCase 1: Valid fileType URI: fileTypeURI := "http://spdx.org/rdf/terms#fileType_source" fileType, err := rdfParser.getFileTypeFromUri(fileTypeURI) if err != nil { t.Errorf("error in a valid example: %v", err) } if fileType != "source" { t.Errorf("wrong fileType. expected: %s, found: %s", "source", fileType) } // TestCase 2: Invalid fileType URI format. fileTypeURI = "http://spdx.org/rdf/terms#source" fileType, err = rdfParser.getFileTypeFromUri(fileTypeURI) if err == nil { t.Error("should've raised an error for invalid fileType") } } func Test_rdfParser2_3_setUnpackagedFiles(t *testing.T) { // unpackaged files are the files which are not associated with any package // file associated with a package sets parser.assocWithPackage[fileID] to true. rdfParser, _ := parserFromBodyContent(``) file1 := &spdx.File{FileSPDXIdentifier: common.ElementID("file1")} file2 := &spdx.File{FileSPDXIdentifier: common.ElementID("file2")} file3 := &spdx.File{FileSPDXIdentifier: common.ElementID("file3")} // setting files to the document as if it were to be set when it was parsed using triples. rdfParser.files[file1.FileSPDXIdentifier] = file1 rdfParser.files[file2.FileSPDXIdentifier] = file2 rdfParser.files[file3.FileSPDXIdentifier] = file3 // assuming file1 is associated with a package rdfParser.assocWithPackage[file1.FileSPDXIdentifier] = true rdfParser.setUnpackagedFiles() // after setting unpackaged files, parser.doc.Files must've file2 and file3 if n := len(rdfParser.doc.Files); n != 2 { t.Errorf("unpackage files should've had 2 files, found %d files", n) } // checking if the unpackagedFiles contain only file2 & file3. for _, file := range rdfParser.doc.Files { switch string(file.FileSPDXIdentifier) { case "file2", "file3": continue default: t.Errorf("unexpected file with id %s found in unpackaged files", file.FileSPDXIdentifier) } } } func Test_setFileIdentifier(t *testing.T) { file := &spdx.File{} // TestCase 1: valid example err := setFileIdentifier("http://spdx.org/documents/spdx-toolsv2.1.7-SNAPSHOT#SPDXRef-129", file) if err != nil { t.Errorf("unexpected error: %v", err) } if file.FileSPDXIdentifier != "129" { t.Errorf("expected %s, found: %s", "129", file.FileSPDXIdentifier) } // TestCase 2: invalid example err = setFileIdentifier("http://spdx.org/documents/spdx-toolsv2.1.7-SNAPSHOT#129", file) if err == nil { t.Errorf("should've raised an error for an invalid example") } } func Test_rdfParser2_3_setFileChecksumFromNode(t *testing.T) { // TestCase 1: md5 checksum parser, _ := parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file := &spdx.File{} err := parser.setFileChecksumFromNode(file, checksumNode) if err != nil { t.Errorf("error parsing a valid checksum node") } checksumValue := "d2356e0fe1c0b85285d83c6b2ad51b5f" for _, checksum := range file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != "" { t.Errorf("incorrectly set sha1, should've been empty") } case common.SHA256: if checksum.Value != "" { t.Errorf("incorrectly set sha256, should've been empty") } case common.MD5: if checksum.Value != checksumValue { t.Errorf("wrong checksum value for md5. Expected: %s, found: %s", checksumValue, checksum.Value) } } } // TestCase 2: valid sha1 checksum parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &spdx.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err != nil { t.Errorf("error parsing a valid checksum node") } for _, checksum := range file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != checksumValue { t.Errorf("wrong checksum value for sha1. Expected: %s, found: %s", checksumValue, checksum.Value) } case common.SHA256: if checksum.Value != "" { t.Errorf("incorrectly set sha256, should've been empty") } case common.MD5: if checksum.Value != checksumValue { t.Errorf("incorrectly set md5, should've been empty") } } } // TestCase 3: valid sha256 checksum parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &spdx.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err != nil { t.Errorf("error parsing a valid checksum node") } for _, checksum := range file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != checksumValue { t.Errorf("incorrectly set sha1, should've been empty") } case common.SHA256: if checksum.Value != checksumValue { t.Errorf("wrong checksum value for sha256. Expected: %s, found: %s", checksumValue, checksum.Value) } case common.MD5: if checksum.Value != checksumValue { t.Errorf("incorrectly set md5, should've been empty") } } } // TestCase 4: checksum node without one of the mandatory attributes parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &spdx.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err == nil { t.Errorf("should've raised an error parsing an invalid checksum node") } // TestCase 5: invalid checksum algorithm parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &spdx.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err == nil { t.Errorf("should've raised an error parsing an invalid checksum node") } // TestCase 6: valid checksum algorithm which is invalid for file (like md4, md6, sha384, etc.) parser, _ = parserFromBodyContent(` d2356e0fe1c0b85285d83c6b2ad51b5f `) checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject file = &spdx.File{} err = parser.setFileChecksumFromNode(file, checksumNode) if err == nil { t.Errorf("should've raised an error parsing an invalid checksum algorithm for a file") } } func Test_rdfParser2_3_getFileFromNode(t *testing.T) { // TestCase 1: file with invalid id parser, _ := parserFromBodyContent(` `) fileNode := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err := parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid file ID") } // TestCase 2: invalid fileType parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid fileType") } // TestCase 3: invalid file checksum parser, _ = parserFromBodyContent(` 0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid checksum") } // TestCase 4: invalid license concluded parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid license Concluded") } // TestCase 5: invalid artifactOf attribute parser, _ = parserFromBodyContent(` Jena `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid artifactOf predicate") } // TestCase 6: invalid file dependency parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid fileDependency") } // TestCase 7: invalid annotation with unknown predicate parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid annotation predicate") } // TestCase 8: invalid relationship parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Errorf("should've raised an error stating invalid relationship Type") } // TestCase 8: unknown predicate parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Error("should've raised an error stating invalid predicate for a file") } // TestCase 9: invalid licenseInfoInFile. parser, _ = parserFromBodyContent(` `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject _, err = parser.getFileFromNode(fileNode) if err == nil { t.Error("should've raised an error stating invalid licenseInfoInFile for a file") } // TestCase 10: Splitting of File definition into parents of different tags mustn't create new file objects. fileDefinitions := []string{ ` time-1.9/ChangeLog `, ` `, } parser, _ = parserFromBodyContent(strings.Join(fileDefinitions, "")) var file *spdx.File packageTypeTriples := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE) for _, typeTriple := range packageTypeTriples { pkg, err := parser.getPackageFromNode(typeTriple.Subject) if err != nil { t.Errorf("unexpected error parsing a valid package: %v", err) } if n := len(pkg.Files); n != 1 { t.Errorf("expected package to contain exactly 1 file. Found %d files", n) } for _, file = range pkg.Files { } } // checking if all the attributes that spanned over a several tags are set in the same variable. expectedFileName := "time-1.9/ChangeLog" if file.FileName != expectedFileName { t.Errorf("expected %s, found %s", expectedFileName, file.FileName) } expectedLicenseConcluded := "NOASSERTION" if file.LicenseConcluded != expectedLicenseConcluded { t.Errorf("expected %s, found %s", expectedLicenseConcluded, file.LicenseConcluded) } expectedFileType := "source" if file.FileTypes[0] != expectedFileType { t.Errorf("expected %s, found %s", expectedFileType, file.FileTypes) } expectedLicenseInfoInFile := "NOASSERTION" if file.LicenseInfoInFiles[0] != expectedLicenseInfoInFile { t.Errorf("expected %s, found %s", expectedLicenseInfoInFile, file.LicenseInfoInFiles[0]) } // TestCase 12: checking if recursive dependencies are resolved. parser, _ = parserFromBodyContent(` ParentFile `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject file, err = parser.getFileFromNode(fileNode) // TestCase 11: all valid attribute and it's values. parser, _ = parserFromBodyContent(` time-1.9/ChangeLog 0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd no comments from spdx file http://www.openjena.org/ Jena no comments Some Organization attribution text 2011-01-29T18:30:22Z File level annotation copied from a spdx document Person: File Commenter `) fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject file, err = parser.getFileFromNode(fileNode) if err != nil { t.Errorf("unexpected error parsing a valid file: %v", err) } // checking each and every attribute of the obtained file. expectedFileName = "time-1.9/ChangeLog" if file.FileName != expectedFileName { t.Errorf("expected %s, found %s", expectedFileName, file.FileName) } if len(file.FileTypes) != 1 { t.Errorf("given file should have 1 fileType attribute. found %d", len(file.FileTypes)) } expectedFileType = "source" if file.FileTypes[0] != expectedFileType { t.Errorf("expected %s, found %s", expectedFileType, file.FileTypes) } expectedChecksum := "0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd" for _, checksum := range file.Checksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != expectedChecksum { t.Errorf("expected %s, found %s", expectedChecksum, checksum.Value) } } } expectedLicenseConcluded = "NOASSERTION" if file.LicenseConcluded != expectedLicenseConcluded { t.Errorf("expected %s, found %s", expectedLicenseConcluded, file.LicenseConcluded) } if len(file.LicenseInfoInFiles) != 1 { t.Errorf("given file should have 1 licenseInfoInFile attribute. found %d", len(file.LicenseInfoInFiles)) } expectedLicenseInfoInFile = "NOASSERTION" if file.LicenseInfoInFiles[0] != expectedLicenseInfoInFile { t.Errorf("expected %s, found %s", expectedLicenseInfoInFile, file.LicenseInfoInFiles[0]) } expectedLicenseComments := "no comments" if file.LicenseComments != expectedLicenseComments { t.Errorf("expected %s, found %s", expectedLicenseComments, file.LicenseComments) } expectedCopyrightText := "from spdx file" if file.FileCopyrightText != expectedCopyrightText { t.Errorf("expected %s, found %s", expectedCopyrightText, file.FileCopyrightText) } if n := len(file.ArtifactOfProjects); n != 1 { t.Errorf("given file should have 1 artifactOfProjects attribute. found %d", n) } artifactOf := file.ArtifactOfProjects[0] expectedHomePage := "http://www.openjena.org/" if artifactOf.HomePage != expectedHomePage { t.Errorf("expected %s, found %s", expectedHomePage, artifactOf.HomePage) } if artifactOf.Name != "Jena" { t.Errorf("expected %s, found %s", "Jena", artifactOf.Name) } if artifactOf.URI != "" { t.Errorf("expected artifactOf uri to be empty, found %s", artifactOf.URI) } expectedFileComment := "no comments" if file.FileComment != expectedFileComment { t.Errorf("expected %s, found %s", expectedFileName, file.FileComment) } expectedNoticeText := "NOASSERTION" if file.FileNotice != expectedNoticeText { t.Errorf("expected %s, found %s", expectedNoticeText, file.FileNotice) } if n := len(file.FileContributors); n != 1 { t.Errorf("given file should have 1 fileContributor. found %d", n) } expectedFileContributor := "Some Organization" if file.FileContributors[0] != expectedFileContributor { t.Errorf("expected %s, found %s", expectedFileContributor, file.FileContributors) } if n := len(file.FileDependencies); n != 1 { t.Errorf("given file should have 1 fileDependencies. found %d", n) } expectedFileDependency := "CommonsLangSrc" if file.FileDependencies[0] != expectedFileDependency { t.Errorf("expected %s, found %s", expectedFileDependency, file.FileDependencies[0]) } if n := len(file.FileAttributionTexts); n != 1 { t.Errorf("given file should have 1 attributionText. found %d", n) } expectedAttributionText := "attribution text" if file.FileAttributionTexts[0] != expectedAttributionText { t.Errorf("expected %s, found %s", expectedAttributionText, file.FileAttributionTexts[0]) } if n := len(parser.doc.Annotations); n != 1 { t.Errorf("doc should've had 1 annotation. found %d", n) } ann := parser.doc.Annotations[0] expectedAnnDate := "2011-01-29T18:30:22Z" if ann.AnnotationDate != expectedAnnDate { t.Errorf("expected %s, found %s", expectedAnnDate, ann.AnnotationDate) } expectedAnnComment := "File level annotation copied from a spdx document" if ann.AnnotationComment != expectedAnnComment { t.Errorf("expected %s, found %s", expectedAnnComment, ann.AnnotationComment) } expectedAnnotationType := "OTHER" if ann.AnnotationType != expectedAnnotationType { t.Errorf("expected %s, found %s", expectedAnnotationType, ann.AnnotationType) } expectedAnnotator := "File Commenter" if ann.Annotator.Annotator != expectedAnnotator { t.Errorf("expected %s, found %s", expectedAnnotator, ann.Annotator) } expectedAnnotatorType := "Person" if ann.AnnotationType != expectedAnnotationType { t.Errorf("expected %s, found %s", expectedAnnotatorType, ann.Annotator.AnnotatorType) } if n := len(parser.doc.Relationships); n != 1 { t.Errorf("doc should've had 1 relation. found %d", n) } reln := parser.doc.Relationships[0] expectedRefAEID := "item177" if reln.RefA.DocumentRefID != "" { t.Errorf("expected refA.DocumentRefID to be empty, found %s", reln.RefA.DocumentRefID) } if string(reln.RefA.ElementRefID) != expectedRefAEID { t.Errorf("expected %s, found %s", expectedRefAEID, reln.RefA.ElementRefID) } expectedRefBEID := "Package" if reln.RefB.DocumentRefID != "" { t.Errorf("expected refB.DocumentRefID to be empty, found %s", reln.RefB.DocumentRefID) } if string(reln.RefB.ElementRefID) != expectedRefBEID { t.Errorf("expected %s, found %s", expectedRefBEID, reln.RefB.ElementRefID) } expectedRelationType := "contains" if reln.Relationship != expectedRelationType { t.Errorf("expected %s, found %s", expectedRefBEID, reln.RefB.ElementRefID) } if reln.RelationshipComment != "" { t.Errorf("expected relationship comment to be empty, found %s", reln.RelationshipComment) } } func Test_getNoticeTextFromNode(t *testing.T) { // TestCase 1: SPDX_NOASSERTION_SMALL must return NOASSERTION output := getNoticeTextFromNode(&gordfParser.Node{ NodeType: gordfParser.IRI, ID: SPDX_NOASSERTION_SMALL, }) if strings.ToUpper(output) != "NOASSERTION" { t.Errorf("expected NOASSERTION, found %s", strings.ToUpper(output)) } // TestCase 2: SPDX_NOASSERTION_CAPS must return NOASSERTION output = getNoticeTextFromNode(&gordfParser.Node{ NodeType: gordfParser.IRI, ID: SPDX_NOASSERTION_CAPS, }) if strings.ToUpper(output) != "NOASSERTION" { t.Errorf("expected NOASSERTION, found %s", strings.ToUpper(output)) } // TestCase 3: not a NOASSERTION must return the field verbatim // TestCase 1: SPDX_NOASSERTION_SMALL must return NOASSERTION output = getNoticeTextFromNode(&gordfParser.Node{ NodeType: gordfParser.IRI, ID: "text", }) if output != "text" { t.Errorf("expected text, found %s", output) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_license.go000066400000000000000000000242571463371440000234000ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "errors" "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" ) // AnyLicense is a baseClass for all the licenses // All the types of licenses is a sub-type of AnyLicense, // either directly or indirectly. // This function acts as a mux for all the licenses. Based on the input, it // decides which type of license it is and passes control to that type of // license parser to parse the given input. func (parser *rdfParser2_3) getAnyLicenseFromNode(node *gordfParser.Node) (AnyLicenseInfo, error) { currState := parser.cache[node.ID] if currState == nil { // there is no entry about the state of current package node. // this is the first time we're seeing this node. parser.cache[node.ID] = &nodeState{ object: nil, // not storing the object as we won't retrieve it later. Color: WHITE, } } else if currState.Color == GREY { // we have already started parsing this license node. // We have a cyclic dependency! return nil, errors.New("Couldn't parse license: found a cyclic dependency on " + node.ID) } // setting color of the state to grey to indicate that we've started to // parse this node once. parser.cache[node.ID].Color = GREY // setting state color to black when we're done parsing this node. defer func() { parser.cache[node.ID].Color = BLACK }() associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) if len(associatedTriples) == 0 { // just a license uri string was found. return parser.getSpecialLicenseFromNode(node) } // we have some attributes associated with the license node. nodeType, err := getNodeTypeFromTriples(associatedTriples, node) if err != nil { return nil, fmt.Errorf("error parsing license triple: %v", err) } switch nodeType { case SPDX_DISJUNCTIVE_LICENSE_SET: return parser.getDisjunctiveLicenseSetFromNode(node) case SPDX_CONJUNCTIVE_LICENSE_SET: return parser.getConjunctiveLicenseSetFromNode(node) case SPDX_EXTRACTED_LICENSING_INFO: return parser.getExtractedLicensingInfoFromNode(node) case SPDX_LISTED_LICENSE, SPDX_LICENSE: return parser.getLicenseFromNode(node) case SPDX_WITH_EXCEPTION_OPERATOR: return parser.getWithExceptionOperatorFromNode(node) case SPDX_OR_LATER_OPERATOR: return parser.getOrLaterOperatorFromNode(node) case SPDX_SIMPLE_LICENSING_INFO: return parser.getSimpleLicensingInfoFromNode(node) } return nil, fmt.Errorf("Unknown subTag (%s) found while parsing AnyLicense", nodeType) } func (parser *rdfParser2_3) getLicenseExceptionFromNode(node *gordfParser.Node) (exception LicenseException, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) for _, triple := range associatedTriples { value := triple.Object.ID switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_LICENSE_EXCEPTION_ID: exception.licenseExceptionId = value case SPDX_LICENSE_EXCEPTION_TEXT: exception.licenseExceptionText = value case RDFS_SEE_ALSO: if !isUriValid(value) { return exception, fmt.Errorf("invalid uri (%s) for seeAlso attribute of LicenseException", value) } exception.seeAlso = value case SPDX_NAME: exception.name = value case SPDX_EXAMPLE: exception.example = value case RDFS_COMMENT: exception.comment = value default: return exception, fmt.Errorf("invalid predicate(%s) for LicenseException", triple.Predicate) } } return exception, nil } func (parser *rdfParser2_3) getSimpleLicensingInfoFromNode(node *gordfParser.Node) (SimpleLicensingInfo, error) { simpleLicensingTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) return parser.getSimpleLicensingInfoFromTriples(simpleLicensingTriples) } func (parser *rdfParser2_3) getWithExceptionOperatorFromNode(node *gordfParser.Node) (operator WithExceptionOperator, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) var memberFound bool for _, triple := range associatedTriples { switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_MEMBER: if memberFound { return operator, fmt.Errorf("more than one member found in the WithExceptionOperator (expected only 1)") } memberFound = true member, err := parser.getSimpleLicensingInfoFromNode(triple.Object) if err != nil { return operator, fmt.Errorf("error parsing member of a WithExceptionOperator: %v", err) } operator.member = member case SPDX_LICENSE_EXCEPTION: operator.licenseException, err = parser.getLicenseExceptionFromNode(triple.Object) if err != nil { return operator, fmt.Errorf("error parsing licenseException of WithExceptionOperator: %v", err) } default: return operator, fmt.Errorf("unknown predicate (%s) for a WithExceptionOperator", triple.Predicate.ID) } } return operator, nil } func (parser *rdfParser2_3) getOrLaterOperatorFromNode(node *gordfParser.Node) (operator OrLaterOperator, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) n := len(associatedTriples) if n != 2 { return operator, fmt.Errorf("orLaterOperator must be associated with exactly one tag. found %v triples", n-1) } for _, triple := range associatedTriples { switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_MEMBER: operator.member, err = parser.getSimpleLicensingInfoFromNode(triple.Object) if err != nil { return operator, fmt.Errorf("error parsing simpleLicensingInfo of OrLaterOperator: %v", err) } default: return operator, fmt.Errorf("unknown predicate %s", triple.Predicate.ID) } } return operator, nil } // SpecialLicense is a type of license which is not defined in any of the // spdx documents, it is a type of license defined for the sake of brevity. // It can be [NONE|NOASSERTION|LicenseRef-] func (parser *rdfParser2_3) getSpecialLicenseFromNode(node *gordfParser.Node) (lic SpecialLicense, err error) { uri := strings.TrimSpace(node.ID) switch uri { case SPDX_NONE_CAPS, SPDX_NONE_SMALL: return SpecialLicense{ value: NONE, }, nil case SPDX_NOASSERTION_SMALL, SPDX_NOASSERTION_CAPS: return SpecialLicense{ value: NOASSERTION, }, nil } // the license is neither NONE nor NOASSERTION // checking if the license is among the standardLicenses licenseAbbreviation := getLastPartOfURI(uri) for _, stdLicense := range AllStandardLicenseIDS() { if licenseAbbreviation == stdLicense { return SpecialLicense{ value: SpecialLicenseValue(stdLicense), }, nil } } return lic, fmt.Errorf("found a custom license uri (%s) without any associated fields", uri) } func (parser *rdfParser2_3) getDisjunctiveLicenseSetFromNode(node *gordfParser.Node) (DisjunctiveLicenseSet, error) { licenseSet := DisjunctiveLicenseSet{ members: []AnyLicenseInfo{}, } for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_MEMBER: member, err := parser.getAnyLicenseFromNode(triple.Object) if err != nil { return licenseSet, fmt.Errorf("error parsing disjunctive license set: %v", err) } licenseSet.members = append(licenseSet.members, member) } } return licenseSet, nil } func (parser *rdfParser2_3) getConjunctiveLicenseSetFromNode(node *gordfParser.Node) (ConjunctiveLicenseSet, error) { licenseSet := ConjunctiveLicenseSet{ members: []AnyLicenseInfo{}, } for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case RDF_TYPE: continue case SPDX_MEMBER: member, err := parser.getAnyLicenseFromNode(triple.Object) if err != nil { return licenseSet, fmt.Errorf("error parsing conjunctive license set: %v", err) } licenseSet.members = append(licenseSet.members, member) default: return licenseSet, fmt.Errorf("unknown subTag for ConjunctiveLicenseSet: %s", triple.Predicate.ID) } } return licenseSet, nil } func (parser *rdfParser2_3) getSimpleLicensingInfoFromTriples(triples []*gordfParser.Triple) (lic SimpleLicensingInfo, err error) { for _, triple := range triples { switch triple.Predicate.ID { case RDFS_COMMENT: lic.comment = triple.Object.ID case SPDX_LICENSE_ID: lic.licenseID = triple.Object.ID case SPDX_NAME: lic.name = triple.Object.ID case RDFS_SEE_ALSO: if !isUriValid(triple.Object.ID) { return lic, fmt.Errorf("%s is not a valid uri for seeAlso attribute of a License", triple.Object.ID) } lic.seeAlso = append(lic.seeAlso, triple.Object.ID) case SPDX_EXAMPLE: lic.example = triple.Object.ID case RDF_TYPE: continue default: return lic, fmt.Errorf("unknown predicate(%s) for simple licensing info", triple.Predicate) } } return lic, nil } func (parser *rdfParser2_3) getLicenseFromNode(node *gordfParser.Node) (lic License, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) var restTriples []*gordfParser.Triple for _, triple := range associatedTriples { value := triple.Object.ID switch triple.Predicate.ID { case SPDX_IS_OSI_APPROVED: lic.isOsiApproved, err = boolFromString(value) if err != nil { return lic, fmt.Errorf("error parsing isOsiApproved attribute of a License: %v", err) } case SPDX_LICENSE_TEXT: lic.licenseText = value case SPDX_STANDARD_LICENSE_HEADER: lic.standardLicenseHeader = value case SPDX_STANDARD_LICENSE_TEMPLATE: lic.standardLicenseTemplate = value case SPDX_STANDARD_LICENSE_HEADER_TEMPLATE: lic.standardLicenseHeaderTemplate = value case SPDX_IS_DEPRECATED_LICENSE_ID: lic.isDeprecatedLicenseID, err = boolFromString(value) if err != nil { return lic, fmt.Errorf("error parsing isDeprecatedLicenseId attribute of a License: %v", err) } case SPDX_IS_FSF_LIBRE: lic.isFsfLibre, err = boolFromString(value) if err != nil { return lic, fmt.Errorf("error parsing isFsfLibre attribute of a License: %v", err) } default: restTriples = append(restTriples, triple) } } lic.SimpleLicensingInfo, err = parser.getSimpleLicensingInfoFromTriples(restTriples) if err != nil { return lic, fmt.Errorf("error setting simple licensing information of a License: %s", err) } return lic, nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_license_test.go000066400000000000000000001160521463371440000244320ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "sort" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" ) func Test_rdfParser2_3_getAnyLicenseFromNode(t *testing.T) { // since this function is a mux, we just have to make sure that with each // type of input, it is able to redirect the request to an appropriate // license getter. // TestCase 1: input node is just a node string without any associated // triple (either a NONE|NOASSERTION) because for other case, // the license should've been associated with other triples parser, _ := parserFromBodyContent(``) inputNode := &gordfParser.Node{ NodeType: gordfParser.IRI, ID: NS_SPDX + "NONE", } lic, err := parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a SpecialLicense switch lic.(type) { case SpecialLicense: default: t.Errorf("expected license to be of type SpecialLicense, found %v", reflect.TypeOf(lic)) } // TestCase 2: DisjunctiveLicenseSet: parser, _ = parserFromBodyContent(` `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a DisjunctiveLicenseSet switch lic.(type) { case DisjunctiveLicenseSet: default: t.Errorf("expected license to be of type DisjunctiveLicenseSet, found %v", reflect.TypeOf(lic)) } // TestCase 3: ConjunctiveLicenseSet: parser, _ = parserFromBodyContent(` `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a ConjunctiveLicenseSet switch lic.(type) { case ConjunctiveLicenseSet: default: t.Errorf("expected license to be of type ConjunctiveLicenseSet, found %v", reflect.TypeOf(lic)) } // TestCase 4: ExtractedLicensingInfo parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a ExtractedLicensingInfo switch lic.(type) { case ExtractedLicensingInfo: default: t.Errorf("expected license to be of type ExtractedLicensingInfo, found %v", reflect.TypeOf(lic)) } // TestCase 4: ExtractedLicensingInfo parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a ExtractedLicensingInfo switch lic.(type) { case ExtractedLicensingInfo: default: t.Errorf("expected license to be of type ExtractedLicensingInfo, found %v", reflect.TypeOf(lic)) } // TestCase 5: License parser, _ = parserFromBodyContent(` <> Apache License Version 2.0, January 2004 http://www.apache.org/licenses/<><> TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION<> <> Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. <> Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. <> Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. <> Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: <> You must give any other recipients of the Work or Derivative Works a copy of this License; and <> You must cause any modified files to carry prominent notices stating that You changed the files; and <> You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and <> If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. <> Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. <> Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. <> Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. <> Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. <> Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.<> END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright <> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.<> http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 Apache-2.0 true http://www.opensource.org/licenses/Apache-2.0 ... ... `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a License switch lic.(type) { case License: default: t.Errorf("expected license to be of type License, found %v", reflect.TypeOf(lic)) } // TestCase 5: WithExceptionOperator parser, _ = parserFromBodyContent(` Libtool-exception `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a WithExceptionOperator switch lic.(type) { case WithExceptionOperator: default: t.Errorf("expected license to be of type WithExceptionOperator, found %v", reflect.TypeOf(lic)) } // TestCase 6: OrLaterOperator parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) inputNode = parser.gordfParserObj.Triples[0].Subject lic, err = parser.getAnyLicenseFromNode(inputNode) if err != nil { t.Errorf("error parsing a valid license input: %v", err) } // checking if the return type is a OrLaterOperator switch lic.(type) { case OrLaterOperator: default: t.Errorf("expected license to be of type OrLaterOperator, found %v", reflect.TypeOf(lic)) } // TestCase 7: checking if an unknown license raises an error. parser, _ = parserFromBodyContent(` `) node := parser.gordfParserObj.Triples[0].Subject _, err = parser.getAnyLicenseFromNode(node) t.Log(err) if err == nil { t.Errorf("should've raised an error for invalid input") } // TestCase 8: cyclic dependent license must raise an error. parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getAnyLicenseFromNode(node) if err == nil { t.Errorf("expected an error due to cyclic dependent license. found %v", err) } } func Test_rdfParser2_3_getConjunctiveLicenseSetFromNode(t *testing.T) { var parser *rdfParser2_3 var err error var licenseNode *gordfParser.Node var license ConjunctiveLicenseSet // TestCase 1: invalid license member parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject _, err = parser.getConjunctiveLicenseSetFromNode(licenseNode) if err == nil { t.Errorf("expected an error saying invalid license member, found ") } // TestCase 2: invalid predicate in the licenseSet. parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject _, err = parser.getConjunctiveLicenseSetFromNode(licenseNode) if err == nil { t.Errorf("expected an error saying invalid predicate found") } // TestCase 3: valid example. parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject license, err = parser.getConjunctiveLicenseSetFromNode(licenseNode) if err != nil { t.Errorf("unexpected error parsing licenseSet: %v", err) } nMembers := len(license.members) if nMembers != 2 { t.Errorf("expected licenseSet to have 2 members, found %d", nMembers) } licenseMembers := mapLicensesToStrings(license.members) expectedLicenseMembers := []string{"LGPL-2.0", "Nokia"} sort.Strings(licenseMembers) if !reflect.DeepEqual(licenseMembers, expectedLicenseMembers) { t.Errorf("expected %v, found %v", expectedLicenseMembers, licenseMembers) } } func Test_rdfParser2_3_getDisjunctiveLicenseSetFromNode(t *testing.T) { var parser *rdfParser2_3 var err error var licenseNode *gordfParser.Node var license DisjunctiveLicenseSet // TestCase 1: invalid license member parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject _, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode) if err == nil { t.Errorf("expected an error saying invalid license member, found ") } // TestCase 2: invalid predicate in the licenseSet. parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject _, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode) if err == nil { t.Errorf("expected an error saying invalid predicate found") } // TestCase 3: valid example. parser, _ = parserFromBodyContent(` `) licenseNode = parser.gordfParserObj.Triples[0].Subject license, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode) if err != nil { t.Errorf("unexpected error parsing licenseSet: %v", err) } nMembers := len(license.members) if nMembers != 2 { t.Errorf("expected licenseSet to have 2 members, found %d", nMembers) } licenseMembers := mapLicensesToStrings(license.members) expectedLicenseMembers := []string{"LGPL-2.0", "Nokia"} sort.Strings(licenseMembers) if !reflect.DeepEqual(licenseMembers, expectedLicenseMembers) { t.Errorf("expected %v, found %v", expectedLicenseMembers, licenseMembers) } } func Test_rdfParser2_3_getLicenseExceptionFromNode(t *testing.T) { var licenseException LicenseException var err error var node *gordfParser.Node var parser *rdfParser2_3 // TestCase 1: invalid value for rdf:seeAlso parser, _ = parserFromBodyContent(` see-also Libtool-exception `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getLicenseExceptionFromNode(node) if err == nil { t.Errorf("should've raised an error due to invalid uri for rdfs:seeAlso") } // TestCase 2: invalid predicate for licenseException // TestCase 1: invalid value for rdf:seeAlso parser, _ = parserFromBodyContent(` Libtool-exception `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getLicenseExceptionFromNode(node) if err == nil { t.Errorf("should've raised an error due to invalid predicate") } // TestCase 3: everything valid // TestCase 1: invalid value for rdf:seeAlso parser, _ = parserFromBodyContent(` no example Libtool-exception no comments text name `) node = parser.gordfParserObj.Triples[0].Subject licenseException, err = parser.getLicenseExceptionFromNode(node) if err != nil { t.Fatalf("unexpected error while parsing a valid licenseException") } expectedCrossReference := "http://www.opensource.org/licenses/GPL-3.0" if licenseException.seeAlso != expectedCrossReference { t.Errorf("expected: %s, found: %s", expectedCrossReference, licenseException.seeAlso) } expectedExample := "no example" if licenseException.example != expectedExample { t.Errorf("expected: %s, got: %s", expectedExample, licenseException.example) } if licenseException.licenseExceptionId != "Libtool-exception" { t.Errorf("expected: %s, got: %s", "Libtool-exception", licenseException.licenseExceptionId) } if licenseException.comment != "no comments" { t.Errorf("expected: %s, got: %s", "no comments", licenseException.comment) } if licenseException.licenseExceptionText != "text" { t.Errorf("expected: '%s', got: '%s'", "text", licenseException.licenseExceptionText) } if licenseException.name != "name" { t.Errorf("expected: '%s', got: '%s'", "name", licenseException.name) } } func Test_rdfParser2_3_getLicenseFromNode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var license License var err error // TestCase 1: isOsiApproved is not a valid boolean parser, _ = parserFromBodyContent(` no `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("expected function to raise an error stating isOsiApproved should be a valid boolean type") } // TestCase 2: rdf:seeAlso not a valid uri must raise an error parser, _ = parserFromBodyContent(` uri `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("expected function to raise an error stating invalid uri for rdfs:seeAlso") } // TestCase 3: isDeprecatedLicenseId is not a valid boolean parser, _ = parserFromBodyContent(` yes `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("expected function to raise an error stating isDeprecatedLicenseId should be a valid boolean type") } // TestCase 4: isFsfLibre is not a valid boolean parser, _ = parserFromBodyContent(` no `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("expected function to raise an error stating isFsfLibre should be a valid boolean type") } // TestCase 5: invalid triple for License: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err == nil { t.Errorf("invalid predicate should've raised an error, got ") } // TestCase 5: everything valid: parser, _ = parserFromBodyContent(` http://www.opensource.org/licenses/GPL-3.0 true GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 GNU General Public License v3.0 or later ... GPL-3.0-or-later This license was released: 29 June 2007 true ... .... `) node = parser.gordfParserObj.Triples[0].Subject license, err = parser.getLicenseFromNode(node) if err != nil { t.Errorf("error parsing a valid input: %v", err) } expectedSeeAlso := "http://www.opensource.org/licenses/GPL-3.0" if len(license.seeAlso) != 1 { t.Fatalf("expected seeAlso to have 1 element, got %d", len(license.seeAlso)) } if license.seeAlso[len(license.seeAlso)-1] != expectedSeeAlso { t.Errorf("expected %s, got %s", expectedSeeAlso, license.seeAlso) } if license.isOsiApproved != true { t.Errorf("expected %t, got %t", true, license.isOsiApproved) } expectedLicenseText := "GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007" if license.licenseText != expectedLicenseText { t.Errorf("expected %s, got %s", expectedSeeAlso, license.licenseText) } expectedName := "GNU General Public License v3.0 or later" if license.name != expectedName { t.Errorf("expected %s, got %s", expectedName, license.name) } expectedstdLicHeader := "..." if license.standardLicenseHeader != expectedstdLicHeader { t.Errorf("expected %s, got %s", expectedstdLicHeader, license.standardLicenseHeader) } expectedLicenseId := "GPL-3.0-or-later" if expectedLicenseId != license.licenseID { t.Errorf("expected %s, got %s", expectedLicenseId, license.licenseID) } expectedLicenseComment := "This license was released: 29 June 2007" if expectedLicenseComment != license.comment { t.Errorf("expected %s, got %s", expectedLicenseComment, license.comment) } expectedstdLicTemplate := "..." if license.standardLicenseHeader != expectedstdLicTemplate { t.Errorf("expected %s, got %s", expectedstdLicTemplate, license.standardLicenseTemplate) } expectedstdLicHeaderTemplate := "..." if license.standardLicenseHeaderTemplate != expectedstdLicHeaderTemplate { t.Errorf("expected %s, got %s", expectedstdLicHeaderTemplate, license.standardLicenseHeaderTemplate) } if license.isFsfLibre != true { t.Errorf("expected %t, got %t", true, license.isFsfLibre) } } func Test_rdfParser2_3_getOrLaterOperatorFromNode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var err error // TestCase 1: more than one member in the OrLaterOperator tag must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getOrLaterOperatorFromNode(node) if err == nil { t.Error("expected an error due to more than one members, got ") } // TestCase 2: Invalid predicate must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getOrLaterOperatorFromNode(node) if err == nil { t.Error("expected an error due to invalid predicate, got ") } // TestCase 5: invalid member parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getOrLaterOperatorFromNode(node) if err == nil { t.Errorf("expected an error parsing invalid license member, got %v", err) } // TestCase 4: valid input parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getOrLaterOperatorFromNode(node) if err != nil { t.Errorf("unexpected error parsing a valid input: %v", err) } } func Test_rdfParser2_3_getSimpleLicensingInfoFromNode(t *testing.T) { // nothing to test. The just provides an interface to call function that // uses triples to render a SimpleLicensingInfo. parser, _ := parserFromBodyContent(` LicenseRef-Freeware freeware `) node := parser.gordfParserObj.Triples[0].Subject _, err := parser.getSimpleLicensingInfoFromNode(node) if err != nil { t.Errorf("error parsing a valid input: %v", err) } } func Test_rdfParser2_3_getSimpleLicensingInfoFromTriples(t *testing.T) { var parser *rdfParser2_3 var err error var license SimpleLicensingInfo // TestCase 1: invalid rdf:seeAlso attribute parser, _ = parserFromBodyContent(` an invalid uri `) _, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples) if err == nil { t.Error("expected an error reporting invalid uri for rdf:seeAlso, got ") } // TestCase 2: invalid predicate must raise an error parser, _ = parserFromBodyContent(` `) _, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples) if err == nil { t.Error("expected an error reporting invalid predicate, got ") } // TestCase 3: valid example parser, _ = parserFromBodyContent(` comment lid name https://opensource.org/licenses/MPL-1.0 example `) license, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedComment := "comment" expectedLicenseId := "lid" expectedName := "name" expectedSeeAlso := "https://opensource.org/licenses/MPL-1.0" expectedExample := "example" if expectedComment != license.comment { t.Errorf("expected %v, got %v", expectedComment, license.comment) } if expectedLicenseId != license.licenseID { t.Errorf("expected %v, got %v", expectedLicenseId, license.licenseID) } if expectedName != license.name { t.Errorf("expected %v, got %v", expectedName, license.name) } if len(license.seeAlso) != 1 { t.Fatalf("expected seeAlso to have 1 element, found %d", len(license.seeAlso)) } if license.seeAlso[0] != expectedSeeAlso { t.Errorf("expected %v, got %v", expectedSeeAlso, license.seeAlso[0]) } if license.example != expectedExample { t.Errorf("expected %v, got %v", expectedExample, license.example) } } func Test_rdfParser2_3_getSpecialLicenseFromNode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var license SpecialLicense // TestCase 1: NONE parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: NS_SPDX + "NONE", } license, err := parser.getSpecialLicenseFromNode(node) if err != nil { t.Errorf("error parsing a valid node: %v", err) } if license.value != "NONE" { t.Errorf("expected %s, got %s", "NONE", license.value) } // TestCase 2: NOASSERTION parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: NS_SPDX + "NOASSERTION", } license, err = parser.getSpecialLicenseFromNode(node) if err != nil { t.Errorf("error parsing a valid node: %v", err) } if license.value != "NOASSERTION" { t.Errorf("expected %s, got %s", "NOASSERTION", license.value) } // TestCase 4: undefined standard license parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "https://opensource.org/licenses/unknown", } _, err = parser.getSpecialLicenseFromNode(node) if err == nil { t.Errorf("expected an error saying invalid license") } // TestCase 4: valid standard license parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "https://opensource.org/licenses/MPL-1.0", } license, err = parser.getSpecialLicenseFromNode(node) if err != nil { t.Errorf("error parsing a valid node: %v", err) } if license.value != "MPL-1.0" { t.Errorf("expected %s, got %s", "MPL-1.0", license.value) } } func Test_rdfParser2_3_getWithExceptionOperatorFromNode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var err error // TestCase 1: more than one member in the OrLaterOperator tag must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err == nil { t.Error("expected an error due to more than one members, got ") } // TestCase 2: Invalid predicate must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err == nil { t.Error("expected an error due to invalid predicate, got ") } // TestCase 3: Invalid member parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err == nil { t.Error("expected an error due to error parsing a member, got ") } // TestCase 4: Invalid licenseException parser, _ = parserFromBodyContent(` example Libtool-exception comment `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err == nil { t.Error("expected an error due to invalid licenseException, got ") } // TestCase 5: valid input parser, _ = parserFromBodyContent(` example Libtool-exception comment `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getWithExceptionOperatorFromNode(node) if err != nil { t.Errorf("error parsing a valid input: %v", err) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_other_license_info.go000066400000000000000000000024371463371440000256100ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *rdfParser2_3) getExtractedLicensingInfoFromNode(node *gordfParser.Node) (lic ExtractedLicensingInfo, err error) { associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) var restTriples []*gordfParser.Triple for _, triple := range associatedTriples { switch triple.Predicate.ID { case SPDX_EXTRACTED_TEXT: lic.extractedText = triple.Object.ID default: restTriples = append(restTriples, triple) } } lic.SimpleLicensingInfo, err = parser.getSimpleLicensingInfoFromTriples(restTriples) if err != nil { return lic, fmt.Errorf("error setting simple licensing information of extracted licensing info: %s", err) } return lic, nil } func (parser *rdfParser2_3) extractedLicenseToOtherLicense(extLicense ExtractedLicensingInfo) (othLic spdx.OtherLicense) { othLic.LicenseIdentifier = extLicense.licenseID othLic.ExtractedText = extLicense.extractedText othLic.LicenseComment = extLicense.comment othLic.LicenseCrossReferences = extLicense.seeAlso othLic.LicenseName = extLicense.name return othLic } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_other_license_info_test.go000066400000000000000000000147631463371440000266540ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" ) func Test_rdfParser2_3_getExtractedLicensingInfoFromNode(t *testing.T) { var parser *rdfParser2_3 var err error var node *gordfParser.Node // TestCase 1: invalid predicate must raise an error parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExtractedLicensingInfoFromNode(node) if err == nil { t.Errorf("expected an error saying invalid predicate, got ") } // TestCase 2: valid input parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExtractedLicensingInfoFromNode(node) if err != nil { t.Errorf("unexpected error: %v", err) } } func Test_rdfParser2_3_extractedLicenseToOtherLicense(t *testing.T) { // nothing to test for this function. parser, _ := parserFromBodyContent(` LicenseRef-Freeware freeware `) node := parser.gordfParserObj.Triples[0].Subject extLicense, _ := parser.getExtractedLicensingInfoFromNode(node) othLic := parser.extractedLicenseToOtherLicense(extLicense) if othLic.LicenseIdentifier != extLicense.licenseID { t.Errorf("expected %v, got %v", othLic.LicenseIdentifier, extLicense.licenseID) } if othLic.ExtractedText != extLicense.extractedText { t.Errorf("expected %v, got %v", othLic.ExtractedText, extLicense.extractedText) } if othLic.LicenseComment != extLicense.comment { t.Errorf("expected %v, got %v", othLic.LicenseComment, extLicense.comment) } if !reflect.DeepEqual(othLic.LicenseCrossReferences, extLicense.seeAlso) { t.Errorf("expected %v, got %v", othLic.LicenseCrossReferences, extLicense.seeAlso) } if othLic.LicenseName != extLicense.name { t.Errorf("expected %v, got %v", othLic.LicenseName, extLicense.name) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_package.go000066400000000000000000000307071463371440000233460ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *rdfParser2_3) getPackageFromNode(packageNode *gordfParser.Node) (pkg *spdx.Package, err error) { pkg = &spdx.Package{} // new package which will be returned currState := parser.cache[packageNode.ID] if currState == nil { // there is no entry about the state of current package node. // this is the first time we're seeing this node. parser.cache[packageNode.ID] = &nodeState{ object: pkg, Color: WHITE, } } else if currState.Color == GREY { // we have already started parsing this package node and we needn't parse it again. return currState.object.(*spdx.Package), nil } // setting color of the state to grey to indicate that we've started to // parse this node once. parser.cache[packageNode.ID].Color = GREY // setting state color to black to indicate when we're done parsing this node. defer func() { parser.cache[packageNode.ID].Color = BLACK }() // setting the SPDXIdentifier for the package. eId, err := ExtractElementID(getLastPartOfURI(packageNode.ID)) if err != nil { return nil, fmt.Errorf("error extracting elementID of a package identifier: %v", err) } pkg.PackageSPDXIdentifier = eId // 3.2 // check if we already have a package initialized for this ID existingPackageIndex := -1 for ii, existingPkg := range parser.doc.Packages { if existingPkg != nil && existingPkg.PackageSPDXIdentifier == eId { existingPackageIndex = ii pkg = existingPkg break } } // iterate over all the triples associated with the provided package packageNode. for _, subTriple := range parser.nodeToTriples(packageNode) { switch subTriple.Predicate.ID { case RDF_TYPE: // cardinality: exactly 1 continue case SPDX_NAME: // 7.1 // cardinality: exactly 1 pkg.PackageName = subTriple.Object.ID case SPDX_VERSION_INFO: // 7.3 // cardinality: max 1 pkg.PackageVersion = subTriple.Object.ID case SPDX_PACKAGE_FILE_NAME: // 7.4 // cardinality: max 1 pkg.PackageFileName = subTriple.Object.ID case SPDX_SUPPLIER: // 7.5 // cardinality: max 1 err = setPackageSupplier(pkg, subTriple.Object.ID) case SPDX_ORIGINATOR: // 7.6 // cardinality: max 1 err = setPackageOriginator(pkg, subTriple.Object.ID) case SPDX_DOWNLOAD_LOCATION: // 7.7 // cardinality: exactly 1 err = setDocumentLocationFromURI(pkg, subTriple.Object.ID) case SPDX_FILES_ANALYZED: // 7.8 // cardinality: max 1 err = setFilesAnalyzed(pkg, subTriple.Object.ID) case SPDX_PACKAGE_VERIFICATION_CODE: // 7.9 // cardinality: max 1 err = parser.setPackageVerificationCode(pkg, subTriple.Object) case SPDX_CHECKSUM: // 7.10 // cardinality: min 0 err = parser.setPackageChecksum(pkg, subTriple.Object) case DOAP_HOMEPAGE: // 7.11 // cardinality: max 1 // homepage must be a valid Uri if !isUriValid(subTriple.Object.ID) { return nil, fmt.Errorf("invalid uri %s while parsing doap_homepage in a package", subTriple.Object.ID) } pkg.PackageHomePage = subTriple.Object.ID case SPDX_SOURCE_INFO: // 7.12 // cardinality: max 1 pkg.PackageSourceInfo = subTriple.Object.ID case SPDX_LICENSE_CONCLUDED: // 7.13 // cardinality: exactly 1 anyLicenseInfo, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return nil, err } pkg.PackageLicenseConcluded = anyLicenseInfo.ToLicenseString() case SPDX_LICENSE_INFO_FROM_FILES: // 7.14 // cardinality: min 0 pkg.PackageLicenseInfoFromFiles = append(pkg.PackageLicenseInfoFromFiles, getLicenseStringFromURI(subTriple.Object.ID)) case SPDX_LICENSE_DECLARED: // 7.15 // cardinality: exactly 1 anyLicenseInfo, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return nil, err } pkg.PackageLicenseDeclared = anyLicenseInfo.ToLicenseString() case SPDX_LICENSE_COMMENTS: // 7.16 // cardinality: max 1 pkg.PackageLicenseComments = subTriple.Object.ID case SPDX_COPYRIGHT_TEXT: // 7.17 // cardinality: exactly 1 pkg.PackageCopyrightText = subTriple.Object.ID case SPDX_SUMMARY: // 7.18 // cardinality: max 1 pkg.PackageSummary = subTriple.Object.ID case SPDX_DESCRIPTION: // 7.19 // cardinality: max 1 pkg.PackageDescription = subTriple.Object.ID case RDFS_COMMENT: // 7.20 // cardinality: max 1 pkg.PackageComment = subTriple.Object.ID case SPDX_EXTERNAL_REF: // 7.21 // cardinality: min 0 externalDocRef, err := parser.getPackageExternalRef(subTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing externalRef of a package: %v", err) } pkg.PackageExternalReferences = append(pkg.PackageExternalReferences, externalDocRef) case SPDX_HAS_FILE: // 7.22 // cardinality: min 0 file, err := parser.getFileFromNode(subTriple.Object) if err != nil { return nil, fmt.Errorf("error setting file inside a package: %v", err) } parser.setFileToPackage(pkg, file) case SPDX_PRIMARY_PACKAGE_PURPOSE: // 7.24 // cardinality: exactly 1 pkg.PrimaryPackagePurpose = getPrimaryPackagePurpose(subTriple.Object.ID) case SPDX_RELEASE_DATE: // 7.25 // cardinality: exactly 1 pkg.ReleaseDate = subTriple.Object.ID case SPDX_BUILT_DATE: // 7.26 // cardinality: exactly 1 pkg.BuiltDate = subTriple.Object.ID case SPDX_VALID_UNTIL_DATE: // 7.27 // cardinality: exactly 1 pkg.ValidUntilDate = subTriple.Object.ID case SPDX_RELATIONSHIP: // cardinality: min 0 err = parser.parseRelationship(subTriple) case SPDX_ATTRIBUTION_TEXT: // cardinality: min 0 pkg.PackageAttributionTexts = append(pkg.PackageAttributionTexts, subTriple.Object.ID) case SPDX_ANNOTATION: // cardinality: min 0 err = parser.parseAnnotationFromNode(subTriple.Object) default: return nil, fmt.Errorf("unknown predicate id %s while parsing a package", subTriple.Predicate.ID) } if err != nil { return nil, err } } if existingPackageIndex != -1 { parser.doc.Packages[existingPackageIndex] = pkg } else { parser.doc.Packages = append(parser.doc.Packages, pkg) } return pkg, nil } // parses externalReference found in the package by the associated triple. func (parser *rdfParser2_3) getPackageExternalRef(node *gordfParser.Node) (externalDocRef *spdx.PackageExternalReference, err error) { externalDocRef = &spdx.PackageExternalReference{} for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case SPDX_REFERENCE_CATEGORY: // cardinality: exactly 1 switch triple.Object.ID { case SPDX_REFERENCE_CATEGORY_SECURITY: externalDocRef.Category = "SECURITY" case SPDX_REFERENCE_CATEGORY_PACKAGE_MANAGER: externalDocRef.Category = "PACKAGE-MANAGER" case SPDX_REFERENCE_CATEGORY_OTHER: externalDocRef.Category = "OTHER" default: return nil, fmt.Errorf("unknown packageManager uri %s", triple.Predicate.ID) } case RDF_TYPE: continue case SPDX_REFERENCE_TYPE: // assumes: the reference type is associated with just the uri and // other associated fields are ignored. // other fields include: // 1. contextualExample, // 2. documentation and, // 3. externalReferenceSite externalDocRef.RefType = triple.Object.ID case SPDX_REFERENCE_LOCATOR: // cardinality: exactly 1 externalDocRef.Locator = triple.Object.ID case RDFS_COMMENT: // cardinality: max 1 externalDocRef.ExternalRefComment = triple.Object.ID default: return nil, fmt.Errorf("unknown package external reference predicate id %s", triple.Predicate.ID) } } return } func getPrimaryPackagePurpose(purpose string) string { value := strings.ReplaceAll(purpose, "packagePurpose_", "") value = strings.ReplaceAll(value, "_", "-") value = strings.ToUpper(value) switch value { case "APPLICATION", "FRAMEWORK", "LIBRARY", "CONTAINER", "OPERATING-SYSTEM", "DEVICE", "FIRMWARE", "SOURCE", "ARCHIVE", "FILE", "INSTALL", "OTHER": return value } // invalid value return "" } func (parser *rdfParser2_3) setPackageVerificationCode(pkg *spdx.Package, node *gordfParser.Node) error { if pkg.PackageVerificationCode == nil { pkg.PackageVerificationCode = &common.PackageVerificationCode{} } for _, subTriple := range parser.nodeToTriples(node) { switch subTriple.Predicate.ID { case SPDX_PACKAGE_VERIFICATION_CODE_VALUE: // cardinality: exactly 1 pkg.PackageVerificationCode.Value = subTriple.Object.ID case SPDX_PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE: // cardinality: min 0 pkg.PackageVerificationCode.ExcludedFiles = append(pkg.PackageVerificationCode.ExcludedFiles, subTriple.Object.ID) case RDF_TYPE: // cardinality: exactly 1 continue default: return fmt.Errorf("unparsed predicate %s", subTriple.Predicate.ID) } } return nil } // appends the file to the package and also sets the assocWithPackage for the // file to indicate the file is associated with a package func (parser *rdfParser2_3) setFileToPackage(pkg *spdx.Package, file *spdx.File) { if pkg.Files == nil { pkg.Files = []*spdx.File{} } pkg.Files = append(pkg.Files, file) parser.assocWithPackage[file.FileSPDXIdentifier] = true } // given a supplierObject, sets the PackageSupplier attribute of the pkg. // Args: // // value: [NOASSERTION | [Person | Organization]: string] func setPackageSupplier(pkg *spdx.Package, value string) error { value = strings.TrimSpace(value) supplier := &common.Supplier{} if strings.ToUpper(value) == "NOASSERTION" { supplier.Supplier = "NOASSERTION" pkg.PackageSupplier = supplier return nil } subKey, subValue, err := ExtractSubs(value, ":") if err != nil { return fmt.Errorf("package supplier must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value) } switch subKey { case "Person", "Organization": supplier.Supplier = subValue supplier.SupplierType = subKey default: return fmt.Errorf("unknown supplier %s", subKey) } pkg.PackageSupplier = supplier return nil } // given a OriginatorObject, sets the PackageOriginator attribute of the pkg. // Args: // // value: [NOASSERTION | [Person | Organization]: string] func setPackageOriginator(pkg *spdx.Package, value string) error { value = strings.TrimSpace(value) originator := &common.Originator{} if strings.ToUpper(value) == "NOASSERTION" { originator.Originator = "NOASSERTION" pkg.PackageOriginator = originator return nil } subKey, subValue, err := ExtractSubs(value, ":") if err != nil { return fmt.Errorf("package Originator must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value) } switch subKey { case "Person", "Organization": originator.Originator = subValue originator.OriginatorType = subKey default: return fmt.Errorf("unknown Originator %s", subKey) } pkg.PackageOriginator = originator return nil } // validates the uri and sets the location if it is valid func setDocumentLocationFromURI(pkg *spdx.Package, locationURI string) error { switch locationURI { case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL: pkg.PackageDownloadLocation = "NOASSERTION" case SPDX_NONE_CAPS, SPDX_NONE_SMALL: pkg.PackageDownloadLocation = "NONE" default: if !isUriValid(locationURI) { return fmt.Errorf("%s is not a valid uri", locationURI) } pkg.PackageDownloadLocation = locationURI } return nil } // sets the FilesAnalyzed attribute to the given package // boolValue is a string of type "true" or "false" func setFilesAnalyzed(pkg *spdx.Package, boolValue string) (err error) { pkg.IsFilesAnalyzedTagPresent = true pkg.FilesAnalyzed, err = boolFromString(boolValue) return err } func (parser *rdfParser2_3) setPackageChecksum(pkg *spdx.Package, node *gordfParser.Node) error { checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(node) if err != nil { return fmt.Errorf("error getting checksum algorithm and value from %v", node) } if pkg.PackageChecksums == nil { pkg.PackageChecksums = make([]common.Checksum, 0, 1) } switch checksumAlgorithm { case common.SHA1, common.SHA224, common.SHA256, common.SHA384, common.SHA512, common.MD2, common.MD4, common.MD5, common.MD6, common.SHA3_256, common.SHA3_384, common.SHA3_512, common.BLAKE2b_256, common.BLAKE2b_384, common.BLAKE2b_512, common.BLAKE3, common.ADLER32: pkg.PackageChecksums = append(pkg.PackageChecksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue}) default: return fmt.Errorf("unknown checksumAlgorithm %s while parsing a package", checksumAlgorithm) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_package_test.go000066400000000000000000000702141463371440000244020ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func Test_setPackageSupplier(t *testing.T) { var err error // TestCase 1: no assertion must set PackageSupplierNOASSERTION field to true pkg := &spdx.Package{} err = setPackageSupplier(pkg, "NOASSERTION") if err != nil { t.Fatalf("unexpected error: %v", err) } if pkg.PackageSupplier.Supplier != "NOASSERTION" { t.Errorf("PackageSupplier must've been set to NOASSERTION") } // TestCase 2: lower-case noassertion must also set the // PackageSupplierNOASSERTION to true. pkg = &spdx.Package{} err = setPackageSupplier(pkg, "noassertion") if err != nil { t.Fatalf("unexpected error: %v", err) } if pkg.PackageSupplier.Supplier != "NOASSERTION" { t.Errorf("PackageSupplier must've been set to NOASSERTION") } // TestCase 3: invalid input without colon separator. must raise an error pkg = &spdx.Package{} input := "string without colon separator" err = setPackageSupplier(pkg, input) if err == nil { t.Errorf("invalid input \"%s\" didn't raise an error", input) } // TestCase 4: Valid Person pkg = &spdx.Package{} personName := "Rishabh Bhatnagar" input = "Person: " + personName err = setPackageSupplier(pkg, input) if err != nil { t.Errorf("unexpected error: %v", err) } if pkg.PackageSupplier.Supplier != personName { t.Errorf("PackageSupplierPerson should be %s. found %s", personName, pkg.PackageSupplier.Supplier) } // TestCase 5: Valid Organization pkg = &spdx.Package{} orgName := "SPDX" input = "Organization: " + orgName err = setPackageSupplier(pkg, input) if err != nil { t.Errorf("unexpected error: %v", err) } if pkg.PackageSupplier.Supplier != orgName { t.Errorf("PackageSupplierPerson should be %s. found %s", orgName, pkg.PackageSupplier.Supplier) } // TestCase 6: Invalid EntityType pkg = &spdx.Package{} input = "InvalidEntity: entity" err = setPackageSupplier(pkg, input) if err == nil { t.Errorf("invalid entity should've raised an error") } } func Test_setPackageOriginator(t *testing.T) { var err error // TestCase 1: no assertion must set PackageSupplierNOASSERTION field to true pkg := &spdx.Package{} err = setPackageOriginator(pkg, "NOASSERTION") if err != nil { t.Fatalf("unexpected error: %v", err) } if pkg.PackageOriginator.Originator != "NOASSERTION" { t.Errorf("PackageOriginator must've been set to NOASSERTION") } // TestCase 2: lower-case noassertion must also set the // PackageOriginatorNOASSERTION to true. pkg = &spdx.Package{} err = setPackageOriginator(pkg, "noassertion") if err != nil { t.Fatalf("unexpected error: %v", err) } if pkg.PackageOriginator.Originator != "NOASSERTION" { t.Errorf("PackageOriginator must've been set to NOASSERTION") } // TestCase 3: invalid input without colon separator. must raise an error pkg = &spdx.Package{} input := "string without colon separator" err = setPackageOriginator(pkg, input) if err == nil { t.Errorf("invalid input \"%s\" didn't raise an error", input) } // TestCase 4: Valid Person pkg = &spdx.Package{} personName := "Rishabh Bhatnagar" input = "Person: " + personName err = setPackageOriginator(pkg, input) if err != nil { t.Errorf("unexpected error: %v", err) } if pkg.PackageOriginator.Originator != personName { t.Errorf("PackageOriginatorPerson should be %s. found %s", personName, pkg.PackageOriginator.Originator) } // TestCase 5: Valid Organization pkg = &spdx.Package{} orgName := "SPDX" input = "Organization: " + orgName err = setPackageOriginator(pkg, input) if err != nil { t.Errorf("unexpected error: %v", err) } if pkg.PackageOriginator.Originator != orgName { t.Errorf("PackageOriginatorOrganization should be %s. found %s", orgName, pkg.PackageOriginator.Originator) } // TestCase 6: Invalid EntityType pkg = &spdx.Package{} input = "InvalidEntity: entity" err = setPackageOriginator(pkg, input) if err == nil { t.Errorf("invalid entity should've raised an error") } } func Test_rdfParser2_3_setPackageVerificationCode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var pkg *spdx.Package var err error // TestCase 1: invalid predicate must raise an error parser, _ = parserFromBodyContent(` cbceb8b5689b75a584efe35587b5d41bd48820ce ./package.spdx `) node = parser.gordfParserObj.Triples[0].Subject pkg = &spdx.Package{} err = parser.setPackageVerificationCode(pkg, node) if err == nil { t.Errorf("expected an error due to invalid predicate, got ") } // TestCase 2: valid input parser, _ = parserFromBodyContent(` cbceb8b5689b75a584efe35587b5d41bd48820ce ./package.spdx `) node = parser.gordfParserObj.Triples[0].Subject pkg = &spdx.Package{} err = parser.setPackageVerificationCode(pkg, node) if err != nil { t.Errorf("unexpected error: %v", err) } expectedValue := "cbceb8b5689b75a584efe35587b5d41bd48820ce" if pkg.PackageVerificationCode.Value != expectedValue { t.Errorf("expected %v, got %v", expectedValue, pkg.PackageVerificationCode) } expectedExcludedFile := "./package.spdx" if pkg.PackageVerificationCode.ExcludedFiles[0] != expectedExcludedFile { t.Errorf("expected %v, got %v", expectedExcludedFile, pkg.PackageVerificationCode.ExcludedFiles) } } func Test_rdfParser2_3_getPackageExternalRef(t *testing.T) { var extRef *spdx.PackageExternalReference var err error var parser *rdfParser2_3 var node *gordfParser.Node // TestCase 1: invalid reference category parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err == nil { t.Errorf("expected an error due to invalid referenceCategory, got ") } // TestCase 2: invalid predicate parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err == nil { t.Errorf("expected an error due to invalid referenceCategory, got ") } // TestCase 3: valid example (referenceCategory_security) parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* comment `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err != nil { t.Fatalf("unexpected error parsing a valid example: %v", err) } expectedExtRef := &spdx.PackageExternalReference{ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", RefType: "http://spdx.org/rdf/references/cpe23Type", Category: "SECURITY", ExternalRefComment: "comment", } if !reflect.DeepEqual(extRef, expectedExtRef) { t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef) } // TestCase 4: valid example (referenceCategory_packageManager) parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* comment `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err != nil { t.Fatalf("unexpected error parsing a valid example: %v", err) } expectedExtRef = &spdx.PackageExternalReference{ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", RefType: "http://spdx.org/rdf/references/cpe23Type", Category: "PACKAGE-MANAGER", ExternalRefComment: "comment", } if !reflect.DeepEqual(extRef, expectedExtRef) { t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef) } // TestCase 5: valid example (referenceCategory_other) parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* comment `) node = parser.gordfParserObj.Triples[0].Subject extRef, err = parser.getPackageExternalRef(node) if err != nil { t.Fatalf("unexpected error parsing a valid example: %v", err) } expectedExtRef = &spdx.PackageExternalReference{ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", RefType: "http://spdx.org/rdf/references/cpe23Type", Category: "OTHER", ExternalRefComment: "comment", } if !reflect.DeepEqual(extRef, expectedExtRef) { t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef) } } func Test_rdfParser2_3_getPrimaryPackagePurpose(t *testing.T) { // TestCase 1: basic purpose value := getPrimaryPackagePurpose("packagePurpose_container") if value != "CONTAINER" { t.Errorf("expected primary package purpose to be CONTAINER. got: '%s'", value) } // TestCase 2: purpose with underscore-to-dash value = getPrimaryPackagePurpose("packagePurpose_operating_system") if value != "OPERATING-SYSTEM" { t.Errorf("expected primary package purpose to be OPERATING-SYSTEM. got: '%s'", value) } // TestCase 3: invalid purpose value = getPrimaryPackagePurpose("packagePurpose_invalid") if value != "" { t.Errorf("expected invalid primary package purpose to be empty. got: '%s'", value) } } func Test_rdfParser2_3_getPackageFromNode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var err error // TestCase 1: invalid elementId parser, _ = parserFromBodyContent(` time-1.9.tar.gz `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(missing SPDXRef- prefix), found %v", err) } // TestCase 2: Invalid License Concluded must raise an error: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid license), found %v", err) } // TestCase 2: Invalid License Declared must raise an error: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid license), found %v", err) } // TestCase 3: Invalid ExternalRef parser, _ = parserFromBodyContent(` cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid externalRef), found %v", err) } // TestCase 4: invalid file must raise an error parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid file), found %v", err) } // TestCase 5: invalid predicate must raise an error parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid predicate), found %v", err) } // TestCase 6: invalid annotation must raise an error parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid annotation), found %v", err) } // TestCase 6: invalid homepage must raise an error parser, _ = parserFromBodyContent(` u r i `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err == nil { t.Errorf("expected an error(invalid homepage uri), found %v", err) } // TestCase 7: Package tag declared more than once should be parsed into a single object's definition parser, _ = parserFromBodyContent(` Test Package `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err != nil { t.Errorf("error parsing a valid package: %v", err) } yetAnotherPkgTriple := gordfParser.Triple{ Subject: node, Predicate: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: SPDX_PACKAGE_FILE_NAME, }, Object: &gordfParser.Node{ NodeType: gordfParser.LITERAL, ID: "packageFileName", }, } parser.nodeStringToTriples[node.String()] = append(parser.nodeStringToTriples[node.String()], &yetAnotherPkgTriple) pkg, err := parser.getPackageFromNode(node) if err != nil { t.Errorf("error parsing a valid package: %v", err) } // validating if all the attributes that spanned over two tags are included in the parsed package. expectedID := "upload2" if string(pkg.PackageSPDXIdentifier) != expectedID { t.Errorf("expected package id: %s, got %s", expectedID, pkg.PackageSPDXIdentifier) } expectedPkgFileName := "packageFileName" if expectedPkgFileName != pkg.PackageFileName { t.Errorf("expected package file name: %s, got %s", expectedPkgFileName, pkg.PackageFileName) } expectedName := "Test Package" if pkg.PackageName != expectedName { t.Errorf("expected package name: %s, got %s", expectedPkgFileName, pkg.PackageName) } // TestCase 8: Checking if packages can handle cyclic dependencies: // Simulating a smallest possible cycle: package related to itself. parser, _ = parserFromBodyContent(` Test Package 1.1.1 `) node = parser.gordfParserObj.Triples[0].Subject pkg, err = parser.getPackageFromNode(node) if err != nil { t.Errorf("error parsing a valid package: %v", err) } // checking if both the attributes of the packages are set. expectedVersionInfo := "1.1.1" expectedPackageName := "Test Package" if pkg.PackageVersion != expectedVersionInfo { t.Errorf("Expected %s, found %s", expectedVersionInfo, pkg.PackageVersion) } if pkg.PackageName != expectedPackageName { t.Errorf("Expected %s, found %s", expectedPackageName, pkg.PackageName) } // TestCase 9: everything valid parser, _ = parserFromBodyContent(` Test Package 1.1.1 time-1.9.tar.gz Person: Jane Doe (jane.doe@example.com) Organization: SPDX true cbceb8b5689b75a584efe35587b5d41bd48820ce ./package.spdx 75068c26abbed3ad3980685bae21d7202d288317 http://www.openjena.org/ uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. Other versions available for a commercial license Package for Testing Some tags are taken from other spdx autogenerated files no comments cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* attribution text 2011-01-29T18:30:22Z Package level annotation Person: Package Commenter `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getPackageFromNode(node) if err != nil { t.Errorf("error parsing a valid package: %v", err) } } func Test_rdfParser2_3_setFileToPackage(t *testing.T) { var pkg *spdx.Package var file *spdx.File var parser *rdfParser2_3 // TestCase 1: setting to a nil files attribute shouldn't panic. parser, _ = parserFromBodyContent(``) pkg = &spdx.Package{} file = &spdx.File{} parser.setFileToPackage(pkg, file) if len(pkg.Files) != 1 { t.Errorf("expected given package to have one file after setting, got %d", len(pkg.Files)) } if parser.assocWithPackage[file.FileSPDXIdentifier] != true { t.Errorf("given file should've been associated with a package, assocWithPackage is false") } } func Test_rdfParser2_3_setPackageChecksum(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var pkg *spdx.Package var expectedChecksumValue string var err error // TestCase 1: invalid checksum algorithm parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &spdx.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err == nil { t.Error("expected an error due to invalid checksum node, got ") } // TestCase 1: valid checksum algorithm which is invalid for package parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &spdx.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err == nil { t.Error("expected an error due to invalid checksum for package, got ") } // TestCase 2: valid checksum (sha1) parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &spdx.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err != nil { t.Errorf("unexpected error: %v", err) } expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" for _, checksum := range pkg.PackageChecksums { switch checksum.Algorithm { case common.SHA1: if checksum.Value != expectedChecksumValue { t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value) } } } // TestCase 3: valid checksum (sha256) parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &spdx.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err != nil { t.Errorf("unexpected error: %v", err) } expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" for _, checksum := range pkg.PackageChecksums { switch checksum.Algorithm { case common.SHA256: if checksum.Value != expectedChecksumValue { t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value) } } } // TestCase 4: valid checksum (md5) parser, _ = parserFromBodyContent(` 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 `) pkg = &spdx.Package{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setPackageChecksum(pkg, node) if err != nil { t.Errorf("unexpected error: %v", err) } expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" for _, checksum := range pkg.PackageChecksums { switch checksum.Algorithm { case common.MD5: if checksum.Value != expectedChecksumValue { t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value) } } } } func Test_setDocumentLocationFromURI(t *testing.T) { var pkg *spdx.Package var expectedDocumentLocation, gotDocumentLocation string var inputURI string var err error // TestCase 1: NOASSERTION inputURI = SPDX_NOASSERTION_SMALL pkg = &spdx.Package{} err = setDocumentLocationFromURI(pkg, inputURI) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedDocumentLocation = "NOASSERTION" gotDocumentLocation = pkg.PackageDownloadLocation if expectedDocumentLocation != gotDocumentLocation { t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation) } // TestCase 2: NONE inputURI = SPDX_NONE_CAPS pkg = &spdx.Package{} err = setDocumentLocationFromURI(pkg, inputURI) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedDocumentLocation = "NONE" gotDocumentLocation = pkg.PackageDownloadLocation if expectedDocumentLocation != gotDocumentLocation { t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation) } // TestCase 3: valid uri inputURI = "https://www.gnu.org/software/texinfo/" pkg = &spdx.Package{} err = setDocumentLocationFromURI(pkg, inputURI) if err != nil { t.Fatalf("unexpected error: %v", err) } expectedDocumentLocation = "https://www.gnu.org/software/texinfo/" gotDocumentLocation = pkg.PackageDownloadLocation if expectedDocumentLocation != gotDocumentLocation { t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation) } // TestCase 3: invalid uri inputURI = " " pkg = &spdx.Package{} err = setDocumentLocationFromURI(pkg, inputURI) if err == nil { t.Fatalf("expected an error due to invalid uri, got %v", err) } } func Test_setFilesAnalyzed(t *testing.T) { var pkg *spdx.Package var err error // TestCase 1: not a valid bool value: pkg = &spdx.Package{} err = setFilesAnalyzed(pkg, "no") if err == nil { t.Errorf("expected an error due to invalid bool input, got %v", err) } // TestCase 2: valid input pkg = &spdx.Package{} err = setFilesAnalyzed(pkg, "true") if err != nil { t.Fatalf("unexpected error: %v", err) } if !pkg.IsFilesAnalyzedTagPresent { t.Errorf("should've set IsFilesAnalyzedTagPresent, got: %t", pkg.IsFilesAnalyzedTagPresent) } if !pkg.FilesAnalyzed { t.Errorf("expected: %t, got: %t", true, pkg.FilesAnalyzed) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_relationship.go000066400000000000000000000115371463371440000244540ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // parsing the relationship that exists in the rdf document. // Relationship is of type RefA relationType RefB. // parsing the relationship appends the relationship to the current document's // Relationships Slice. func (parser *rdfParser2_3) parseRelationship(triple *gordfParser.Triple) (err error) { reln := spdx.Relationship{} reln.RefA, err = getReferenceFromURI(triple.Subject.ID) if err != nil { return err } currState := parser.cache[triple.Object.ID] if currState == nil { // there is no entry about the state of current package node. // this is the first time we're seeing this node. parser.cache[triple.Object.ID] = &nodeState{ object: reln, Color: WHITE, } } else if currState.Color == GREY { // we have already started parsing this relationship node and we needn't parse it again. return nil } // setting color of the state to grey to indicate that we've started to // parse this node once. parser.cache[triple.Object.ID].Color = GREY // setting state color to black to indicate when we're done parsing this node. defer func() { parser.cache[triple.Object.ID].Color = BLACK }() for _, subTriple := range parser.nodeToTriples(triple.Object) { switch subTriple.Predicate.ID { case SPDX_RELATIONSHIP_TYPE: // cardinality: exactly 1 reln.Relationship, err = getRelationshipTypeFromURI(subTriple.Object.ID) case RDF_TYPE: // cardinality: exactly 1 continue case SPDX_RELATED_SPDX_ELEMENT: // cardinality: exactly 1 // assumes: spdx-element is a uri reln.RefB, err = getReferenceFromURI(subTriple.Object.ID) if err != nil { return err } relatedSpdxElementTriples := parser.nodeToTriples(subTriple.Object) if len(relatedSpdxElementTriples) == 0 { continue } typeTriples := rdfwriter.FilterTriples(relatedSpdxElementTriples, &subTriple.Object.ID, &RDF_TYPE, nil) if len(typeTriples) != 1 { return fmt.Errorf("expected %s to have exactly one rdf:type triple. found %d triples", subTriple.Object, len(typeTriples)) } err = parser.parseRelatedElementFromTriple(&reln, typeTriples[0]) if err != nil { return err } case RDFS_COMMENT: // cardinality: max 1 reln.RelationshipComment = subTriple.Object.ID default: return fmt.Errorf("unexpected predicate id: %s", subTriple.Predicate.ID) } if err != nil { return err } } parser.doc.Relationships = append(parser.doc.Relationships, &reln) return nil } func (parser *rdfParser2_3) parseRelatedElementFromTriple(reln *spdx.Relationship, triple *gordfParser.Triple) error { // iterate over relatedElement Type and check which SpdxElement it is. var err error switch triple.Object.ID { case SPDX_FILE: file, err := parser.getFileFromNode(triple.Subject) if err != nil { return fmt.Errorf("error setting a file: %v", err) } reln.RefB = common.DocElementID{ DocumentRefID: "", ElementRefID: file.FileSPDXIdentifier, } case SPDX_PACKAGE: pkg, err := parser.getPackageFromNode(triple.Subject) if err != nil { return fmt.Errorf("error setting a package inside a relationship: %v", err) } reln.RefB = common.DocElementID{ DocumentRefID: "", ElementRefID: pkg.PackageSPDXIdentifier, } case SPDX_SPDX_ELEMENT: // it shouldn't be associated with any other triple. // it must be a uri reference. reln.RefB, err = ExtractDocElementID(getLastPartOfURI(triple.Subject.ID)) if err != nil { return err } default: return fmt.Errorf("undefined relatedElement %s found while parsing relationship", triple.Object.ID) } return nil } // references like RefA and RefB of any relationship func getReferenceFromURI(uri string) (common.DocElementID, error) { fragment := getLastPartOfURI(uri) switch strings.ToLower(strings.TrimSpace(fragment)) { case "noassertion", "none": return common.DocElementID{ DocumentRefID: "", ElementRefID: common.ElementID(strings.ToUpper(fragment)), }, nil } return ExtractDocElementID(fragment) } // note: relationshipType is case sensitive. func getRelationshipTypeFromURI(relnTypeURI string) (string, error) { relnTypeURI = strings.TrimSpace(relnTypeURI) lastPart := getLastPartOfURI(relnTypeURI) if !strings.HasPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE) { return "", fmt.Errorf("relationshipType must start with %s. found %s", PREFIX_RELATIONSHIP_TYPE, lastPart) } lastPart = strings.TrimPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE) lastPart = strings.TrimSpace(lastPart) for _, validRelationshipType := range AllRelationshipTypes() { if lastPart == validRelationshipType { return lastPart, nil } } return "", fmt.Errorf("unknown relationshipType: '%s'", lastPart) } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_relationship_test.go000066400000000000000000000345121463371440000255110ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func Test_getReferenceFromURI(t *testing.T) { // TestCase 1: noassertion uri ref, err := getReferenceFromURI(SPDX_NOASSERTION_CAPS) if err != nil { t.Errorf("unexpected error: %v", err) } if ref.DocumentRefID != "" { t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID) } if ref.ElementRefID != "NOASSERTION" { t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "NOASSERTION") } // TestCase 2: NONE uri ref, err = getReferenceFromURI(SPDX_NONE_CAPS) if err != nil { t.Errorf("unexpected error: %v", err) } if ref.DocumentRefID != "" { t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID) } if ref.ElementRefID != "NONE" { t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "NONE") } // TestCase 3: Valid URI ref, err = getReferenceFromURI(NS_SPDX + "SPDXRef-item1") if err != nil { t.Errorf("unexpected error: %v", err) } if ref.DocumentRefID != "" { t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID) } if ref.ElementRefID != "item1" { t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "item1") } // TestCase 3: Invalid URI _, err = getReferenceFromURI(NS_SPDX + "item1") if err == nil { t.Errorf("should've raised an error for invalid input") } } func Test_getRelationshipTypeFromURI(t *testing.T) { // TestCase 1: valid relationshipType relnType := "expandedFromArchive" op, err := getRelationshipTypeFromURI(NS_SPDX + "relationshipType_" + relnType) if err != nil { t.Errorf("error getting relationship type from a valid input") } if op != relnType { t.Errorf("expected %s, found %s", relnType, op) } // TestCase2: invalid relationshipType relnType = "invalidRelationship" _, err = getRelationshipTypeFromURI(NS_SPDX + "relationshipType_" + relnType) if err == nil { t.Errorf("should've raised an error for an invalid input(%s)", relnType) } } func Test_rdfParser2_3_parseRelatedElementFromTriple(t *testing.T) { // TestCase 1: Package as a related element parser, _ := parserFromBodyContent(` `) reln := &spdx.Relationship{} triple := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE)[0] err := parser.parseRelatedElementFromTriple(reln, triple) if err != nil { t.Errorf("error parsing a valid example") } expectedRefA := common.DocElementID{ DocumentRefID: "", ElementRefID: "", } if !reflect.DeepEqual(expectedRefA, reln.RefA) { t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA) } expectedRefB := common.DocElementID{ DocumentRefID: "", ElementRefID: "Saxon", } if !reflect.DeepEqual(expectedRefB, reln.RefB) { t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB) } // TestCase 3: invalid package as a relatedElement parser, _ = parserFromBodyContent(` `) reln = &spdx.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err == nil { t.Errorf("expected an error due to invalid Package id, got %v", err) } // TestCase 4: valid File as a related element parser, _ = parserFromBodyContent(` `) reln = &spdx.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err != nil { t.Errorf("error parsing a valid example") } expectedRefA = common.DocElementID{ DocumentRefID: "", ElementRefID: "", } if !reflect.DeepEqual(expectedRefA, reln.RefA) { t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA) } expectedRefB = common.DocElementID{ DocumentRefID: "", ElementRefID: "Saxon", } if !reflect.DeepEqual(expectedRefB, reln.RefB) { t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB) } // TestCase 5: invalid File as a relatedElement parser, _ = parserFromBodyContent(` `) reln = &spdx.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err == nil { t.Errorf("expected an error while parsing an invalid File, got %v", err) } // TestCase 6: valid SpdxElement as a related element parser, _ = parserFromBodyContent(` `) reln = &spdx.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_SPDX_ELEMENT)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err != nil { t.Errorf("error parsing a valid example") } expectedRefA = common.DocElementID{ DocumentRefID: "", ElementRefID: "", } if !reflect.DeepEqual(expectedRefA, reln.RefA) { t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA) } expectedRefB = common.DocElementID{ DocumentRefID: "", ElementRefID: "File", } if !reflect.DeepEqual(expectedRefB, reln.RefB) { t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB) } // TestCase 7: invalid SpdxElement as a related element parser, _ = parserFromBodyContent(` `) reln = &spdx.Relationship{} triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_SPDX_ELEMENT)[0] err = parser.parseRelatedElementFromTriple(reln, triple) if err == nil { t.Errorf("expected an error due to invalid documentId for SpdxElement, got %v", err) } } func Test_rdfParser2_3_parseRelationship(t *testing.T) { // TestCase 1: invalid RefA parser, _ := parserFromBodyContent(` `) triple := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err := parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to invalid RefA") } // TestCase 3: invalid RefB parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to invalid RefB") } // TestCase 3: more than one typeTriple for relatedElement parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to more than one type triples") } // TestCase 4: undefined relatedSpdxElement parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to unknown relatedElement, got %v", err) } // TestCase 6: relatedElement associated with more than one type parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("expected an error due to invalid relatedElement, got %v", err) } // TestCase 5: unknown predicate inside a relationship parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err == nil { t.Errorf("should've raised an error due to unknown predicate in a relationship") } // TestCase 8: Recursive relationships mustn't raise any error: parser, _ = parserFromBodyContent(` `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err != nil { t.Errorf("error parsing a valid example") } // TestCase 7: completely valid example: parser, _ = parserFromBodyContent(` comment `) triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0] err = parser.parseRelationship(triple) if err != nil { t.Errorf("unexpected error parsing a valid relationship: %v", err) } // validating parsed attributes if len(parser.doc.Relationships) != 1 { t.Errorf("after parsing a valid relationship, doc should've had 1 relationship, found %d", len(parser.doc.Relationships)) } reln := parser.doc.Relationships[0] expectedRelnType := "describes" if reln.Relationship != expectedRelnType { t.Errorf("expected %s, found %s", expectedRelnType, reln.Relationship) } expectedRefA := common.DocElementID{ DocumentRefID: "", ElementRefID: "File", } if !reflect.DeepEqual(expectedRefA, reln.RefA) { t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA) } expectedRefB := common.DocElementID{ DocumentRefID: "", ElementRefID: "Saxon", } if !reflect.DeepEqual(expectedRefB, reln.RefB) { t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB) } expectedComment := "comment" if reln.RelationshipComment != expectedComment { t.Errorf("expected %v, found %v", expectedComment, reln.RelationshipComment) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_review.go000066400000000000000000000020231463371440000232420ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *rdfParser2_3) setReviewFromNode(reviewedNode *gordfParser.Node) error { review := spdx.Review{} for _, triple := range parser.nodeToTriples(reviewedNode) { switch triple.Predicate.ID { case RDF_TYPE: // cardinality: exactly 1 continue case RDFS_COMMENT: // cardinality: max 1 review.ReviewComment = triple.Object.ID case SPDX_REVIEW_DATE: // cardinality: exactly 1 review.ReviewDate = triple.Object.ID case SPDX_REVIEWER: // cardinality: max 1 var err error review.ReviewerType, review.Reviewer, err = ExtractSubs(triple.Object.ID, ":") if err != nil { return fmt.Errorf("error parsing reviewer: %v", err) } default: return fmt.Errorf("unknown predicate %v for review triples", triple.Predicate) } } parser.doc.Reviews = append(parser.doc.Reviews, &review) return nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_review_test.go000066400000000000000000000044411463371440000243070ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" ) func Test_rdfParser2_3_setReviewFromNode(t *testing.T) { // TestCase 1: unknown predicate must raise an error parser, _ := parserFromBodyContent(` Another example reviewer. 2011-03-13T00:00:00Z Person: Suzanne Reviewer `) reviewNode := parser.gordfParserObj.Triples[0].Subject err := parser.setReviewFromNode(reviewNode) if err == nil { t.Errorf("unknown predicate should've elicit an error") } // TestCase 2: wrong reviewer format must raise an error parser, _ = parserFromBodyContent(` Another example reviewer. 2011-03-13T00:00:00Z Suzanne Reviewer `) reviewNode = parser.gordfParserObj.Triples[0].Subject err = parser.setReviewFromNode(reviewNode) if err == nil { t.Errorf("incorrect should've elicit an error") } // TestCase 3: valid input parser, _ = parserFromBodyContent(` Another example reviewer. 2011-03-13T00:00:00Z Person: Suzanne `) reviewNode = parser.gordfParserObj.Triples[0].Subject err = parser.setReviewFromNode(reviewNode) if err != nil { t.Errorf("error parsing a valid node") } n := len(parser.doc.Reviews) if n != 1 { t.Errorf("expected doc to have 1 review, found %d", n) } review := parser.doc.Reviews[0] expectedComment := "Another example reviewer." if review.ReviewComment != expectedComment { t.Errorf("expected: %v, found: %s", expectedComment, review.ReviewComment) } expectedDate := "2011-03-13T00:00:00Z" if review.ReviewDate != expectedDate { t.Errorf("expected %s, found %s", expectedDate, review.ReviewDate) } expectedReviewer := "Suzanne" if review.Reviewer != expectedReviewer { t.Errorf("expected %s, found %s", expectedReviewer, review.Reviewer) } expectedReviewerType := "Person" if review.ReviewerType != expectedReviewerType { t.Errorf("expected %s, found %s", expectedReviewerType, review.ReviewerType) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_snippet_info.go000066400000000000000000000155671463371440000244570ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strconv" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // Snippet Information // Cardinality: Optional, Many func (parser *rdfParser2_3) getSnippetInformationFromNode2_3(node *gordfParser.Node) (si *spdx.Snippet, err error) { si = &spdx.Snippet{} err = setSnippetID(node.ID, si) if err != nil { return nil, err } for _, siTriple := range parser.nodeToTriples(node) { switch siTriple.Predicate.ID { case RDF_TYPE: // cardinality: exactly 1 case SPDX_SNIPPET_FROM_FILE: // cardinality: exactly 1 // file which is associated with the snippet _, err := parser.getFileFromNode(siTriple.Object) if err != nil { return nil, err } docElemID, err := ExtractDocElementID(getLastPartOfURI(siTriple.Object.ID)) si.SnippetFromFileSPDXIdentifier = docElemID.ElementRefID case SPDX_RANGE: // cardinality: min 1 err = parser.setSnippetRangeFromNode(siTriple.Object, si) if err != nil { return nil, err } case SPDX_LICENSE_INFO_IN_SNIPPET: // license info in snippet can be NONE, NOASSERTION or SimpleLicensingInfo // using AnyLicenseInfo because it can redirect the request and // can handle NONE & NOASSERTION var anyLicense AnyLicenseInfo anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing license info in snippet: %v", err) } si.LicenseInfoInSnippet = append(si.LicenseInfoInSnippet, anyLicense.ToLicenseString()) case SPDX_NAME: si.SnippetName = siTriple.Object.ID case SPDX_COPYRIGHT_TEXT: si.SnippetCopyrightText = siTriple.Object.ID case SPDX_LICENSE_COMMENTS: si.SnippetLicenseComments = siTriple.Object.ID case RDFS_COMMENT: si.SnippetComment = siTriple.Object.ID case SPDX_LICENSE_CONCLUDED: var anyLicense AnyLicenseInfo anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object) if err != nil { return nil, fmt.Errorf("error parsing license info in snippet: %v", err) } si.SnippetLicenseConcluded = anyLicense.ToLicenseString() default: return nil, fmt.Errorf("unknown predicate %v", siTriple.Predicate.ID) } } return si, nil } // given is the id of the file, sets the snippet to the file in parser. func (parser *rdfParser2_3) setSnippetToFileWithID(snippet *spdx.Snippet, fileID common.ElementID) error { if parser.files[fileID] == nil { return fmt.Errorf("snippet refers to an undefined file with ID: %s", fileID) } // initializing snippet of the files if it is not defined already if parser.files[fileID].Snippets == nil { parser.files[fileID].Snippets = map[common.ElementID]*spdx.Snippet{} } // setting the snippet to the file. parser.files[fileID].Snippets[snippet.SnippetSPDXIdentifier] = snippet return nil } func (parser *rdfParser2_3) setSnippetRangeFromNode(node *gordfParser.Node, si *spdx.Snippet) error { // for a range object, we can have only 3 associated triples: // node -> RDF_TYPE -> Object // node -> startPointer -> Object // node -> endPointer -> Object associatedTriples := parser.nodeToTriples(node) if len(associatedTriples) != 3 { return fmt.Errorf("range should be associated with exactly 3 triples, got %d", len(associatedTriples)) } // Triple 1: Predicate=RDF_TYPE typeTriple := rdfwriter.FilterTriples(associatedTriples, &node.ID, &RDF_TYPE, nil) if len(typeTriple) != 1 { // we had 3 associated triples. out of which 2 is start and end pointer, // if we do not have the rdf:type triple as the third one, // we have either extra or undefined predicate. return fmt.Errorf("every object node must be associated with exactly one rdf:type triple, found: %d", len(typeTriple)) } // getting start pointer startPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_START_POINTER, nil) if len(startPointerTriples) != 1 { return fmt.Errorf("range object must be associated with exactly 1 startPointer, got %d", len(startPointerTriples)) } startRangeType, start, err := parser.getPointerFromNode(startPointerTriples[0].Object, si) if err != nil { return fmt.Errorf("error parsing startPointer: %v", err) } // getting end pointer endPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_END_POINTER, nil) if len(startPointerTriples) != 1 { return fmt.Errorf("range object must be associated with exactly 1 endPointer, got %d", len(endPointerTriples)) } endRangeType, end, err := parser.getPointerFromNode(endPointerTriples[0].Object, si) if err != nil { return fmt.Errorf("error parsing endPointer: %v", err) } // return error when start and end pointer type is not same. if startRangeType != endRangeType { return fmt.Errorf("start and end range type doesn't match") } si.Ranges = []common.SnippetRange{{ StartPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier}, EndPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier}, }} if startRangeType == LINE_RANGE { si.Ranges[0].StartPointer.LineNumber = start si.Ranges[0].EndPointer.LineNumber = end } else { si.Ranges[0].StartPointer.Offset = start si.Ranges[0].EndPointer.Offset = end } return nil } func (parser *rdfParser2_3) getPointerFromNode(node *gordfParser.Node, si *spdx.Snippet) (rt RangeType, number int, err error) { for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case RDF_TYPE: case PTR_REFERENCE: err = parser.parseRangeReference(triple.Object, si) case PTR_OFFSET: number, err = strconv.Atoi(triple.Object.ID) rt = BYTE_RANGE case PTR_LINE_NUMBER: number, err = strconv.Atoi(triple.Object.ID) rt = LINE_RANGE default: err = fmt.Errorf("undefined predicate (%s) for a pointer", triple.Predicate) } if err != nil { return } } if rt == "" { err = fmt.Errorf("range type not defined for a pointer") } return } func (parser *rdfParser2_3) parseRangeReference(node *gordfParser.Node, snippet *spdx.Snippet) error { // reference is supposed to be either a resource reference to an already // defined or a new file. Unfortunately, I didn't find field where this can be set in the tools-golang data model. // todo: set this reference to the snippet associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) if len(associatedTriples) == 0 { return nil } _, err := parser.getFileFromNode(node) if err != nil { return fmt.Errorf("error parsing a new file in a reference: %v", err) } return nil } func setSnippetID(uri string, si *spdx.Snippet) (err error) { fragment := getLastPartOfURI(uri) si.SnippetSPDXIdentifier, err = ExtractElementID(fragment) if err != nil { return fmt.Errorf("error setting snippet identifier: %v", uri) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_snippet_info_test.go000066400000000000000000000441051463371440000255040ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func Test_rdfParser2_3_getSnippetInformationFromTriple2_3(t *testing.T) { var err error var parser *rdfParser2_3 var node *gordfParser.Node // TestCase 1: invalid snippet id: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_3(node) if err == nil { t.Errorf("expected an error due to invalid, got %v", err) } // TestCase 2: Invalid LicenseInfoInSnippet parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_3(node) if err == nil { t.Errorf("expected an error due to invalid licenseInfoInSnippet, got %v", err) } // TestCase 3: Invalid range. parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_3(node) if err == nil { t.Errorf("expected an error due to invalid range, got %v", err) } // TestCase 3: invalid file in snippetFromFile parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_3(node) if err == nil { t.Errorf("expected an error due to invalid snippetFromFile, got %v", err) } // TestCase 4: unknown predicate parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_3(node) if err == nil { t.Errorf("expected an error due to invalid predicate, got %v", err) } // TestCase 5: invalid license concluded: parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_3(node) if err == nil { t.Errorf("expected an error due to invalid licenseConcluded, got %v", err) } // TestCase 6: everything valid: parser, _ = parserFromBodyContent(` 420 310 snippet test test comments comments `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getSnippetInformationFromNode2_3(node) if err != nil { t.Fatalf("error parsing a valid example: %v", err) } } func Test_setSnippetID(t *testing.T) { // TestCase 1: invalid input (empty) err := setSnippetID("", &spdx.Snippet{}) if err == nil { t.Errorf("should've raised an error for empty input") } // TestCase 2: valid input si := &spdx.Snippet{} err = setSnippetID("http://spdx.org/spdxdocs/spdx-example#SPDXRef-Snippet", si) if err != nil { t.Errorf("unexpected error: %v", err) } if si.SnippetSPDXIdentifier != "Snippet" { t.Errorf("expected: %s, found: %s", "Snippet", si.SnippetSPDXIdentifier) } } func Test_rdfParser2_3_parseRangeReference(t *testing.T) { var err error var node *gordfParser.Node var parser *rdfParser2_3 var si *spdx.Snippet // TestCase 1: ResourceLiteral node without a new file shouldn't raise any error. si = &spdx.Snippet{} parser, _ = parserFromBodyContent(``) node = &gordfParser.Node{ NodeType: gordfParser.RESOURCELITERAL, ID: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource", } err = parser.parseRangeReference(node, si) if err != nil { t.Errorf("error parsing a valid node: %v", err) } // TestCase 2: invalid file in the reference should raise an error si = &spdx.Snippet{} parser, _ = parserFromBodyContent(` test file `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseRangeReference(node, si) if err == nil { t.Errorf("expected an error due to invalid file in the range reference, got %v", err) } // TestCase 3: A valid reference must set the file to the files map of the parser. si = &spdx.Snippet{} parser, _ = parserFromBodyContent(` test file `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseRangeReference(node, si) if err != nil { t.Errorf("error parsing a valid input: %v", err) } if len(parser.files) != 1 { t.Errorf("expected parser.files to have 1 file, found %d", len(parser.files)) } } func Test_rdfParser2_3_getPointerFromNode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var si *spdx.Snippet var err error var rt RangeType var number int // TestCase 1: invalid number in the offset field must raise an error. parser, _ = parserFromBodyContent(` 3-10 `) node = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getPointerFromNode(node, si) if err == nil { t.Errorf("should've raised an error parsing invalid offset, got %v", err) } // TestCase 2: invalid number in the lineNumber field must raise an error. parser, _ = parserFromBodyContent(` 3-10 `) node = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getPointerFromNode(node, si) if err == nil { t.Errorf("should've raised an error parsing invalid offset, got %v", err) } // TestCase 3: invalid predicate in the pointer field parser, _ = parserFromBodyContent(` 3-10 `) node = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getPointerFromNode(node, si) if err == nil { t.Errorf("should've raised an error parsing invalid predicate, got %v", err) } // TestCase 4: No range type defined must also raise an error parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, _, err = parser.getPointerFromNode(node, si) if err == nil { t.Errorf("should've raised an error parsing invalid rangeType, got %v", err) } // TestCase 5: valid example parser, _ = parserFromBodyContent(` 310 `) node = parser.gordfParserObj.Triples[0].Subject rt, number, err = parser.getPointerFromNode(node, si) if err != nil { t.Fatalf("unexpected error parsing a valid node: %v", err) } if rt != BYTE_RANGE { t.Errorf("expected: %s, got: %s", BYTE_RANGE, rt) } if number != 310 { t.Errorf("expected: %d, got: %d", 310, number) } } func Test_rdfParser2_3_setSnippetRangeFromNode(t *testing.T) { var parser *rdfParser2_3 var err error var si *spdx.Snippet var node *gordfParser.Node // TestCase 1: range with less one pointer less must raise an error // (end-pointer missing in the range) parser, _ = parserFromBodyContent(` 310 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to missing end pointer, got %v", err) } // TestCase 2: triples with 0 or more than one type-triple parser, _ = parserFromBodyContent(` 420 310 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject dummyTriple := parser.gordfParserObj.Triples[0] // resetting the node to be associated with 3 triples which will have // rdf:type triple either thrice or 0 times. parser.nodeStringToTriples[node.String()] = []*gordfParser.Triple{ dummyTriple, dummyTriple, dummyTriple, } err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to invalid rdf:type triples, got %v", err) } // TestCase 3: triples with 0 startPointer parser, _ = parserFromBodyContent(` 420 310 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to missing start pointer, got %v", err) } // TestCase 4: triples with 0 endPointer parser, _ = parserFromBodyContent(` 420 310 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to missing end pointer, got %v", err) } // TestCase 5: error parsing start pointer must be propagated to the range parser, _ = parserFromBodyContent(` 42.0 310 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to invalid start pointer, got %v", err) } // TestCase 6: error parsing end pointer must be propagated to the range parser, _ = parserFromBodyContent(` 420 31+0 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to invalid end pointer, got %v", err) } // TestCase 7: mismatching start and end pointer must also raise an error. parser, _ = parserFromBodyContent(` 420 310 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err == nil { t.Errorf("expected an error due to mismatching start and end pointers, got %v", err) } // TestCase 8: everything valid(byte_range): parser, _ = parserFromBodyContent(` 420 310 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err != nil { t.Errorf("unexpected error: %v", err) } // TestCase 9: everything valid(line_range): parser, _ = parserFromBodyContent(` 420 310 `) si = &spdx.Snippet{} node = parser.gordfParserObj.Triples[0].Subject err = parser.setSnippetRangeFromNode(node, si) if err != nil { t.Errorf("unexpected error: %v", err) } } func Test_rdfParser2_3_setSnippetToFileWithID(t *testing.T) { var parser *rdfParser2_3 var fileId common.ElementID var si *spdx.Snippet var file *spdx.File var err error // TestCase 1: file id which is not associated with any file must raise an error. parser, _ = parserFromBodyContent("") si = &spdx.Snippet{} err = parser.setSnippetToFileWithID(si, fileId) if err == nil { t.Errorf("expected an error saying undefined file") } // TestCase 2: file exists, but snippet of the file doesn't ( it mustn't raise any error ) fileId = common.ElementID("File1") file = &spdx.File{ FileSPDXIdentifier: fileId, } parser.files[fileId] = file file.Snippets = nil // nil snippets err = parser.setSnippetToFileWithID(si, fileId) if err != nil { t.Errorf("unexpected error: %v", err) } if len(file.Snippets) != 1 { t.Errorf("expected file to have 1 snippet, got %d", len(file.Snippets)) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_spdx_document.go000066400000000000000000000101271463371440000246210ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *rdfParser2_3) parseSpdxDocumentNode(spdxDocNode *gordfParser.Node) (err error) { // shorthand for document's creation info. ci := parser.doc.CreationInfo // parse the document header information (SPDXID and document namespace) // the Subject.ID is of type baseURI#spdxID baseUri, offset, err := ExtractSubs(spdxDocNode.ID, "#") if err != nil { return err } parser.doc.DocumentNamespace = baseUri // 2.5 parser.doc.SPDXIdentifier = common.ElementID(offset) // 2.3 // parse other associated triples. for _, subTriple := range parser.nodeToTriples(spdxDocNode) { objectValue := subTriple.Object.ID switch subTriple.Predicate.ID { case RDF_TYPE: continue case SPDX_SPEC_VERSION: // 2.1: specVersion // cardinality: exactly 1 parser.doc.SPDXVersion = objectValue case SPDX_DATA_LICENSE: // 2.2: dataLicense // cardinality: exactly 1 dataLicense, err := parser.getAnyLicenseFromNode(subTriple.Object) if err != nil { return err } parser.doc.DataLicense = dataLicense.ToLicenseString() case SPDX_NAME: // 2.4: DocumentName // cardinality: exactly 1 parser.doc.DocumentName = objectValue case SPDX_EXTERNAL_DOCUMENT_REF: // 2.6: externalDocumentReferences // cardinality: min 0 var extRef spdx.ExternalDocumentRef extRef, err = parser.getExternalDocumentRefFromNode(subTriple.Object) if err != nil { return err } parser.doc.ExternalDocumentReferences = append(parser.doc.ExternalDocumentReferences, extRef) case SPDX_CREATION_INFO: // 2.7 - 2.10: // cardinality: exactly 1 err = parser.parseCreationInfoFromNode(ci, subTriple.Object) case RDFS_COMMENT: // 2.11: Document Comment // cardinality: max 1 parser.doc.DocumentComment = objectValue case SPDX_REVIEWED: // reviewed: // cardinality: min 0 err = parser.setReviewFromNode(subTriple.Object) case SPDX_DESCRIBES_PACKAGE: // describes Package // cardinality: min 0 var pkg *spdx.Package pkg, err = parser.getPackageFromNode(subTriple.Object) if err != nil { return err } parser.doc.Packages = append(parser.doc.Packages, pkg) case SPDX_HAS_EXTRACTED_LICENSING_INFO: // hasExtractedLicensingInfo // cardinality: min 0 extractedLicensingInfo, err := parser.getExtractedLicensingInfoFromNode(subTriple.Object) if err != nil { return fmt.Errorf("error setting extractedLicensingInfo in spdxDocument: %v", err) } othLicense := parser.extractedLicenseToOtherLicense(extractedLicensingInfo) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, &othLicense) case SPDX_RELATIONSHIP: // relationship // cardinality: min 0 err = parser.parseRelationship(subTriple) case SPDX_ANNOTATION: // annotations // cardinality: min 0 err = parser.parseAnnotationFromNode(subTriple.Object) default: return fmt.Errorf("invalid predicate while parsing SpdxDocument: %v", subTriple.Predicate) } if err != nil { return err } } return nil } func (parser *rdfParser2_3) getExternalDocumentRefFromNode(node *gordfParser.Node) (edr spdx.ExternalDocumentRef, err error) { for _, triple := range parser.nodeToTriples(node) { switch triple.Predicate.ID { case SPDX_EXTERNAL_DOCUMENT_ID: // cardinality: exactly 1 edr.DocumentRefID = triple.Object.ID case SPDX_SPDX_DOCUMENT: // cardinality: exactly 1 // assumption: "spdxDocument" property of an external document // reference is just a uri which doesn't follow a spdxDocument definition edr.URI = triple.Object.ID case SPDX_CHECKSUM: // cardinality: exactly 1 alg, checksum, err := parser.getChecksumFromNode(triple.Object) if err != nil { return edr, err } edr.Checksum.Value = checksum edr.Checksum.Algorithm = alg case RDF_TYPE: continue default: return edr, fmt.Errorf("unknown predicate ID (%s) while parsing externalDocumentReference", triple.Predicate.ID) } } return edr, nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parse_spdx_document_test.go000066400000000000000000000234071463371440000256650ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" ) func Test_rdfParser2_3_getExternalDocumentRefFromNode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var err error // TestCase 1: invalid checksum parser, _ = parserFromBodyContent(` DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExternalDocumentRefFromNode(node) if err == nil { t.Errorf("expected an error due to invalid checksum, found %v", err) } // TestCase 2: unknown predicate parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExternalDocumentRefFromNode(node) if err == nil { t.Errorf("expected an error due to invalid predicate, found %v", err) } // TestCase 3: valid example parser, _ = parserFromBodyContent(` DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 `) node = parser.gordfParserObj.Triples[0].Subject _, err = parser.getExternalDocumentRefFromNode(node) if err != nil { t.Errorf("unexpected error: %v", err) } } func Test_rdfParser2_3_parseSpdxDocumentNode(t *testing.T) { var parser *rdfParser2_3 var node *gordfParser.Node var err error // TestCase 1: invalid spdx id of the document parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid document id, got %v", err) } // TestCase 2: erroneous dataLicense parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid dataLicense, got %v", err) } // TestCase 3: invalid external document ref parser, _ = parserFromBodyContent(` DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid externalDocumentRef, got %v", err) } // TestCase 4: invalid package parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid externalDocumentRef, got %v", err) } // TestCase 5: error in extractedLicensingInfo parser, _ = parserFromBodyContent(` LicenseRef-Freeware freeware `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid extractedLicensingInfo, got %v", err) } // TestCase 6: error in annotation parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to invalid extractedLicensingInfo, got %v", err) } // TestCase 7: invalid predicate parser, _ = parserFromBodyContent(` `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err == nil { t.Errorf("expected an error due to unknown predicate, got %v", err) } // TestCase 7: everything valid parser, _ = parserFromBodyContent(` SPDX-2.1 /test/example DocumentRef-spdx-tool-1.2 d6a770ba38583ed4bb4525bd96e50461655d2759 2.6 Person: spdx (y) Organization: Tool: spdx2 2018-08-24T19:55:34Z test Another example reviewer. 2011-03-13T00:00:00Z Person: Suzanne Reviewer 2011-01-29T18:30:22Z test annotation Person: Rishabh Bhatnagar `) node = parser.gordfParserObj.Triples[0].Subject err = parser.parseSpdxDocumentNode(node) if err != nil { t.Errorf("unexpected error: %v", err) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parser.go000066400000000000000000000124251463371440000220520ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "errors" "fmt" gordfParser "github.com/spdx/gordf/rdfloader/parser" gordfWriter "github.com/spdx/gordf/rdfwriter" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // returns a new instance of rdfParser2_3 given the gordf object and nodeToTriples mapping func NewParser2_3(gordfParserObj *gordfParser.Parser, nodeToTriples map[string][]*gordfParser.Triple) *rdfParser2_3 { parser := rdfParser2_3{ gordfParserObj: gordfParserObj, nodeStringToTriples: nodeToTriples, doc: &spdx.Document{ ExternalDocumentReferences: []spdx.ExternalDocumentRef{}, CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{}, Files: []*spdx.File{}, OtherLicenses: []*spdx.OtherLicense{}, Relationships: []*spdx.Relationship{}, Annotations: []*spdx.Annotation{}, Reviews: []*spdx.Review{}, }, files: map[common.ElementID]*spdx.File{}, assocWithPackage: map[common.ElementID]bool{}, cache: map[string]*nodeState{}, } return &parser } // main function which takes in a gordfParser and returns // a spdxDocument model or the error encountered while parsing it func LoadFromGoRDFParser(gordfParserObj *gordfParser.Parser) (*spdx.Document, error) { // nodeToTriples is a mapping from a node to list of triples. // for every node in the set of subjects of all the triples, // it provides a list of triples that are associated with that subject node. nodeToTriples := gordfWriter.GetNodeToTriples(gordfParserObj.Triples) parser := NewParser2_3(gordfParserObj, nodeToTriples) spdxDocumentNode, err := parser.getSpdxDocNode() if err != nil { return nil, err } err = parser.parseSpdxDocumentNode(spdxDocumentNode) if err != nil { return nil, err } // parsing other root elements for _, rootNode := range gordfWriter.GetRootNodes(parser.gordfParserObj.Triples) { typeTriples := gordfWriter.FilterTriples(gordfParserObj.Triples, &rootNode.ID, &RDF_TYPE, nil) if len(typeTriples) != 1 { return nil, fmt.Errorf("every node must be associated with exactly 1 type Triple. found %d type triples", len(typeTriples)) } switch typeTriples[0].Object.ID { case SPDX_SPDX_DOCUMENT_CAPITALIZED: continue // it is already parsed. case SPDX_SNIPPET: snippet, err := parser.getSnippetInformationFromNode2_3(typeTriples[0].Subject) if err != nil { return nil, fmt.Errorf("error parsing a snippet: %v", err) } err = parser.setSnippetToFileWithID(snippet, snippet.SnippetFromFileSPDXIdentifier) if err != nil { return nil, err } // todo: check other root node attributes. default: continue // because in rdf it is quite possible that the root node is an // element that has been used in the some other element as a child } } // parsing packages and files sets the files to a files variable which is // associated with the parser and not the document. following method is // necessary to transfer the files which are not set in the packages to the // Files attribute of the document // WARNING: do not relocate following function call. It must be at the end of the function parser.setUnpackagedFiles() return parser.doc, nil } // from the given parser object, returns the SpdxDocument Node defined in the root elements. // returns error if the document is associated with no SpdxDocument or // associated with more than one SpdxDocument node. func (parser *rdfParser2_3) getSpdxDocNode() (node *gordfParser.Node, err error) { /* Possible Questions: 1. why are you traversing the root nodes only? why not directly filter out all the triples with rdf:type=spdx:SpdxDocument? Ans: It is quite possible that the relatedElement or any other attribute to have dependency of another SpdxDocument. In that case, that element will reference the dependency using SpdxDocument tag which will cause false positives when direct filtering is done. */ // iterate over root nodes and find the node which has a property of rdf:type=spdx:SpdxDocument var spdxDocNode *gordfParser.Node for _, rootNode := range gordfWriter.GetRootNodes(parser.gordfParserObj.Triples) { typeTriples := gordfWriter.FilterTriples( parser.nodeToTriples(rootNode), // triples &rootNode.ID, // Subject &RDF_TYPE, // Predicate nil, // Object ) if typeTriples[0].Object.ID == SPDX_SPDX_DOCUMENT_CAPITALIZED { // we found a SpdxDocument Node // must be associated with exactly one rdf:type. if len(typeTriples) != 1 { return nil, fmt.Errorf("rootNode (%v) must be associated with exactly one"+ " triple of predicate rdf:type, found %d triples", rootNode, len(typeTriples)) } // checking if we've already found a node and it is not same as the current one. if spdxDocNode != nil && spdxDocNode.ID != typeTriples[0].Subject.ID { return nil, fmt.Errorf("found more than one SpdxDocument Node (%v and %v)", spdxDocNode, typeTriples[0].Subject) } spdxDocNode = typeTriples[0].Subject } } if spdxDocNode == nil { return nil, errors.New("RDF files must be associated with a SpdxDocument tag. No tag found") } return spdxDocNode, nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/parser_test.go000066400000000000000000000140101463371440000231010ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" ) func TestNewParser2_3(t *testing.T) { // testing if the attributes are initialised well and no top-level is left uninitialized. // primarily, checking if all the maps are initialized because // uninitialized slices are by default slices of length 0 p, _ := parserFromBodyContent(``) parser := NewParser2_3(p.gordfParserObj, p.nodeStringToTriples) if parser.files == nil { t.Errorf("files should've been initialised, got %v", parser.files) } if parser.assocWithPackage == nil { t.Errorf("assocWithPackage should've been initialised, got %v", parser.assocWithPackage) } if parser.doc.CreationInfo == nil { t.Errorf("doc.CreationInfo should've been initialised, got %v", parser.doc.CreationInfo) } if parser.doc.Packages == nil { t.Errorf("doc.Packages should've been initialised, got %v", parser.doc.Packages) } if parser.doc.Files == nil { t.Errorf("doc.Files should've been initialised, got %v", parser.doc.Files) } } func TestLoadFromGoRDFParser(t *testing.T) { var parser *rdfParser2_3 var err error // TestCase 1: gordfparser without a SpdxDocument node triple: parser, _ = parserFromBodyContent("") _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error because of absence of SpdxDocument node, got %v", err) } // TestCase 2: invalid SpdxDocumentNode parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error because of absence of SpdxDocument node, got %v", err) } // TestCase 3: >1 type triples for subnode of a SpdxDocument: parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error due to more than one type triples, got %v", err) } // TestCase 4: invalid snippet must raise an error. parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error due to invalid Snippet, got %v", err) } // TestCase 5: invalid snippet not associated with any File must raise an error. parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err == nil { t.Errorf("expected an error due to invalid Snippet File, got %v", err) } // TestCase 6: other Tag alongwith the SpdxDocument node mustn't raise any error. parser, _ = parserFromBodyContent(` `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err != nil { t.Errorf("unexpected error: %v", err) } // TestCase 5: everything valid: parser, _ = parserFromBodyContent(` from linux kernel Copyright 2008-2010 John Smith 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. Copyright 2010, 2011 Source Auditor Inc. Open Logic Inc. ./src/org/spdx/parser/DOAPProject.java Black Duck Software In.c `) _, err = LoadFromGoRDFParser(parser.gordfParserObj) if err != nil { t.Errorf("error parsing a valid example: %v", err) } } func Test_rdfParser2_3_getSpdxDocNode(t *testing.T) { var parser *rdfParser2_3 var err error // TestCase 1: more than one association type for a single node. parser, _ = parserFromBodyContent(` `) _, err = parser.getSpdxDocNode() t.Log(err) if err == nil { t.Errorf("expected and error due to more than one type triples for the SpdxDocument Node, got %v", err) } // TestCase 2: must be associated with exactly one rdf:type. parser, _ = parserFromBodyContent(` `) _, err = parser.getSpdxDocNode() t.Log(err) if err == nil { t.Errorf("rootNode must be associated with exactly one triple of predicate rdf:type, got %v", err) } // TestCase 3: two different spdx nodes found in a single document. parser, _ = parserFromBodyContent(` `) _, err = parser.getSpdxDocNode() if err == nil { t.Errorf("expected and error due to more than one type SpdxDocument Node, got %v", err) } // TestCase 4: no spdx document parser, _ = parserFromBodyContent(``) _, err = parser.getSpdxDocNode() if err == nil { t.Errorf("expected and error due to no SpdxDocument Node, got %v", err) } // TestCase 5: valid spdxDocument node parser, _ = parserFromBodyContent(` `) _, err = parser.getSpdxDocNode() if err != nil { t.Errorf("unexpected error: %v", err) } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/types.go000066400000000000000000000055541463371440000217270ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later // copied from tvloader/parser2v3/types.go package reader import ( gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) type rdfParser2_3 struct { // fields associated with gordf project which // will be required by rdfloader gordfParserObj *gordfParser.Parser nodeStringToTriples map[string][]*gordfParser.Triple // document into which data is being parsed doc *spdx.Document // map of packages and files. files map[common.ElementID]*spdx.File assocWithPackage map[common.ElementID]bool // mapping of nodeStrings to parsed object to save double computation. cache map[string]*nodeState } type Color int const ( GREY Color = iota // represents that the node is being visited WHITE // unvisited node BLACK // visited node ) type nodeState struct { // object will be pointer to the parsed or element being parsed. object interface{} // color of a state represents if the node is visited/unvisited/being-visited. Color Color } type AnyLicenseInfo interface { // ToLicenseString returns the representation of license about how it will // be stored in the tools-golang data model ToLicenseString() string } type SimpleLicensingInfo struct { AnyLicenseInfo comment string licenseID string name string seeAlso []string example string } type ExtractedLicensingInfo struct { SimpleLicensingInfo extractedText string } type OrLaterOperator struct { AnyLicenseInfo member SimpleLicensingInfo } type ConjunctiveLicenseSet struct { AnyLicenseInfo members []AnyLicenseInfo } type DisjunctiveLicenseSet struct { AnyLicenseInfo members []AnyLicenseInfo } type License struct { SimpleLicensingInfo isOsiApproved bool licenseText string standardLicenseHeader string standardLicenseTemplate string standardLicenseHeaderTemplate string isDeprecatedLicenseID bool isFsfLibre bool } type ListedLicense struct { License } type LicenseException struct { licenseExceptionId string licenseExceptionText string seeAlso string // must be a valid uri name string example string comment string } type WithExceptionOperator struct { AnyLicenseInfo member SimpleLicensingInfo licenseException LicenseException } // custom LicenseType to provide support for licences of // type Noassertion, None and customLicenses type SpecialLicense struct { AnyLicenseInfo value SpecialLicenseValue } type SpecialLicenseValue string const ( NONE SpecialLicenseValue = "NONE" NOASSERTION SpecialLicenseValue = "NOASSERTION" ) type RangeType string const ( BYTE_RANGE RangeType = "byteRange" LINE_RANGE RangeType = "lineRange" ) tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/utils.go000066400000000000000000000120031463371440000217060ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "errors" "fmt" "strings" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/rdfwriter" urilib "github.com/spdx/gordf/uri" "github.com/spdx/tools-golang/spdx/v2/common" ) // a uri is of type baseURI#fragment or baseFragment/subFragment // returns fragment or subFragment when given as an input. func getLastPartOfURI(uri string) string { if strings.Contains(uri, "#") { parts := strings.Split(uri, "#") return parts[len(parts)-1] } parts := strings.Split(uri, "/") return parts[len(parts)-1] } func isUriValid(uri string) bool { _, err := urilib.NewURIRef(uri) return err == nil } func getNodeTypeFromTriples(triples []*gordfParser.Triple, node *gordfParser.Node) (string, error) { if node == nil { return "", errors.New("empty node passed to find node type") } typeTriples := rdfwriter.FilterTriples(triples, &node.ID, &RDF_TYPE, nil) switch len(typeTriples) { case 0: return "", fmt.Errorf("node{%v} not associated with any type triple", node) case 1: return typeTriples[0].Object.ID, nil default: return "", fmt.Errorf("node{%v} is associated with more than one type triples", node) } } func (parser *rdfParser2_3) nodeToTriples(node *gordfParser.Node) []*gordfParser.Triple { if node == nil { return []*gordfParser.Triple{} } return parser.nodeStringToTriples[node.String()] } // returns which boolean was given as an input // string(bool) is the only possible input for which it will not raise any error. func boolFromString(boolString string) (bool, error) { switch strings.ToLower(boolString) { case "true": return true, nil case "false": return false, nil default: return false, fmt.Errorf("boolean string can be either true/false") } } /* Function Below this line is taken from the tvloader/parser2v3/utils.go */ // used to extract DocumentRef and SPDXRef values from an SPDX Identifier // which can point either to this document or to a different one func ExtractDocElementID(value string) (common.DocElementID, error) { docRefID := "" idStr := value // check prefix to see if it's a DocumentRef ID if strings.HasPrefix(idStr, "DocumentRef-") { // extract the part that comes between "DocumentRef-" and ":" strs := strings.Split(idStr, ":") // should be exactly two, part before and part after if len(strs) < 2 { return common.DocElementID{}, fmt.Errorf("no colon found although DocumentRef- prefix present") } if len(strs) > 2 { return common.DocElementID{}, fmt.Errorf("more than one colon found") } // trim the prefix and confirm non-empty docRefID = strings.TrimPrefix(strs[0], "DocumentRef-") if docRefID == "" { return common.DocElementID{}, fmt.Errorf("document identifier has nothing after prefix") } // and use remainder for element ID parsing idStr = strs[1] } // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(idStr, "SPDXRef-") { return common.DocElementID{}, fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(idStr, ":") { // we know this means there was no DocumentRef- prefix, because // we would have handled multiple colons above if it was return common.DocElementID{}, fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(idStr, "SPDXRef-") if eltRefID == "" { return common.DocElementID{}, fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.DocElementID{DocumentRefID: docRefID, ElementRefID: common.ElementID(eltRefID)}, nil } // used to extract SPDXRef values only from an SPDX Identifier which can point // to this document only. Use extractDocElementID for parsing IDs that can // refer either to this document or a different one. func ExtractElementID(value string) (common.ElementID, error) { // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(value, "SPDXRef-") { return common.ElementID(""), fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(value, ":") { return common.ElementID(""), fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(value, "SPDXRef-") if eltRefID == "" { return common.ElementID(""), fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.ElementID(eltRefID), nil } // used to extract key / value from embedded substrings // returns subkey, subvalue, nil if no error, or "", "", error otherwise func ExtractSubs(value string, sep string) (string, string, error) { // parse the value to see if it's a valid subvalue format sp := strings.SplitN(value, sep, 2) if len(sp) == 1 { return "", "", fmt.Errorf("invalid subvalue format for %s (no %s found)", value, sep) } subkey := strings.TrimSpace(sp[0]) subvalue := strings.TrimSpace(sp[1]) return subkey, subvalue, nil } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader/utils_test.go000066400000000000000000000230541463371440000227550ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "reflect" "testing" gordfParser "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/tools-golang/spdx/v2/common" ) func Test_getLastPartOfURI(t *testing.T) { // uri of type baseFragment#fragment input := "baseFragment#fragment" expectedOutput := "fragment" output := getLastPartOfURI(input) if output != expectedOutput { t.Errorf("expected %s, found %s", expectedOutput, output) } // uri of type baseFragment/subFragment input = "baseFragment/subFragment" expectedOutput = "subFragment" output = getLastPartOfURI(input) if output != expectedOutput { t.Errorf("expected %s, found %s", expectedOutput, output) } // neither of the case mustn't raise any error. input = "www.github.com" expectedOutput = input output = getLastPartOfURI(input) if output != expectedOutput { t.Errorf("expected %s, found %s", expectedOutput, output) } } func Test_isUriValid(t *testing.T) { // TestCase 1: Valid Input URI input := "https://www.github.com" isValid := isUriValid(input) if !isValid { t.Errorf("valid input(%s) detected as invalid.", input) } // TestCase 2: Invalid Input URI input = `http\:www.github.com` isValid = isUriValid(input) if isValid { t.Errorf("invalid input(%s) detected as valid", input) } } func Test_rdfParser2_3_nodeToTriples(t *testing.T) { var parser *rdfParser2_3 var output, expectedOutput []*gordfParser.Triple // TestCase 1: a nil node shouldn't raise any error or panic. parser, _ = parserFromBodyContent(``) output = parser.nodeToTriples(nil) if output == nil { t.Errorf("nil input should return an empty slice and not nil") } expectedOutput = []*gordfParser.Triple{} if !reflect.DeepEqual(output, expectedOutput) { t.Errorf("expected %+v, got %+v", expectedOutput, output) } // TestCase 2: node should be addressable based on the node content and not the pointer. // It should allow new nodes same as the older ones to retrieve the associated triples. parser, _ = parserFromBodyContent(` 75068c26abbed3ad3980685bae21d7202d288317 `) newNode := &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#checksum", } output = parser.nodeToTriples(newNode) // The output must have 3 triples: // 1. newNode rdf:type Checksum // 2. newNode spdx:algorithm http://spdx.org/rdf/terms#checksumAlgorithm_sha1 // 3. newNode spdx:checksumValue 75068c26abbed3ad3980685bae21d7202d288317 if len(output) != 3 { t.Errorf("expected output to have 3 triples, got %d", len(output)) } } func Test_boolFromString(t *testing.T) { // TestCase 1: Valid Input: "true" // mustn't raise any error input := "true" val, err := boolFromString(input) if err != nil { t.Errorf("function raised an error for a valid input(%s): %s", input, err) } if val != true { t.Errorf("invalid output. Expected %v, found %v", true, val) } // TestCase 2: Valid Input: "true" // mustn't raise any error input = "false" val, err = boolFromString(input) if err != nil { t.Errorf("function raised an error for a valid input(%s): %s", input, err) } if val != false { t.Errorf("invalid output. Expected %v, found %v", false, val) } // TestCase 3: invalid input: "" // it must raise an error input = "" val, err = boolFromString(input) if err == nil { t.Errorf("invalid input should've raised an error") } } func Test_getNodeTypeFromTriples(t *testing.T) { var err error var node *gordfParser.Node var triples []*gordfParser.Triple var nodeType, expectedNodeType string // TestCase 1: nil node must raise an error because, // nil nodes cannot be associated with any rdf:type attribute. _, err = getNodeTypeFromTriples(triples, nil) if err == nil { t.Errorf("expected an error due to nil node, got %v", err) } // TestCase 2: none of the triples give information about the rdf:type of a node. node = &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "N0", } _, err = getNodeTypeFromTriples(triples, node) if err == nil { t.Errorf("expected an error saying no rdf:type found, got %v", err) } // TestCase 3: node is associated with exactly one rdf:type triples typeTriple := &gordfParser.Triple{ Subject: node, Predicate: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: RDF_TYPE, }, Object: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "http://spdx.org/rdf/terms#Checksum", }, } triples = append(triples, typeTriple) expectedNodeType = "http://spdx.org/rdf/terms#Checksum" nodeType, err = getNodeTypeFromTriples(triples, node) if err != nil { t.Fatalf("unexpected error: %v", err) } if nodeType != expectedNodeType { t.Errorf("expected: %v, got: %v", nodeType, expectedNodeType) } // TestCase 4: node associated with more than one rdf:type triples must raise an error. typeTriple = &gordfParser.Triple{ Subject: node, Predicate: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: RDF_TYPE, }, Object: &gordfParser.Node{ NodeType: gordfParser.IRI, ID: "http://spdx.org/rdf/terms#Snippet", }, } triples = append(triples, typeTriple) _, err = getNodeTypeFromTriples(triples, node) if err == nil { t.Errorf("expected an error saying more than one rdf:type found, got %v", err) } } // following tests are copy pasted from tvloader/parser2v3/util_test.go func TestCanExtractDocumentAndElementRefsFromID(t *testing.T) { // test with valid ID in this document helperForExtractDocElementID(t, "SPDXRef-file1", false, "", "file1") // test with valid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file2", false, "doc2", "file2") // test with invalid ID in this document helperForExtractDocElementID(t, "a:SPDXRef-file1", true, "", "") helperForExtractDocElementID(t, "file1", true, "", "") helperForExtractDocElementID(t, "SPDXRef-", true, "", "") helperForExtractDocElementID(t, "SPDXRef-file1:", true, "", "") // test with invalid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:a", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:SPDXRef-file1", true, "", "") // test with invalid formats helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file1:file2", true, "", "") } func helperForExtractDocElementID(t *testing.T, tst string, wantErr bool, wantDoc string, wantElt string) { deID, err := ExtractDocElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if deID.DocumentRefID != wantDoc { if wantDoc == "" { t.Errorf("testing %v: want empty string for DocumentRefID, got %v", tst, deID.DocumentRefID) } else { t.Errorf("testing %v: want %v for DocumentRefID, got %v", tst, wantDoc, deID.DocumentRefID) } } if deID.ElementRefID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, deID.ElementRefID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, deID.ElementRefID) } } } func TestCanExtractElementRefsOnlyFromID(t *testing.T) { // test with valid ID in this document helperForExtractElementID(t, "SPDXRef-file1", false, "file1") // test with valid ID in another document helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-file2", true, "") // test with invalid ID in this document helperForExtractElementID(t, "a:SPDXRef-file1", true, "") helperForExtractElementID(t, "file1", true, "") helperForExtractElementID(t, "SPDXRef-", true, "") helperForExtractElementID(t, "SPDXRef-file1:", true, "") // test with invalid ID in another document helperForExtractElementID(t, "DocumentRef-doc2", true, "") helperForExtractElementID(t, "DocumentRef-doc2:", true, "") helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-", true, "") helperForExtractElementID(t, "DocumentRef-doc2:a", true, "") helperForExtractElementID(t, "DocumentRef-:", true, "") helperForExtractElementID(t, "DocumentRef-:SPDXRef-file1", true, "") } func helperForExtractElementID(t *testing.T, tst string, wantErr bool, wantElt string) { eID, err := ExtractElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if eID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, eID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, eID) } } } func TestCanExtractSubvalues(t *testing.T) { subkey, subvalue, err := ExtractSubs("SHA1: abc123", ":") if err != nil { t.Errorf("got error when calling extractSubs: %v", err) } if subkey != "SHA1" { t.Errorf("got %v for subkey", subkey) } if subvalue != "abc123" { t.Errorf("got %v for subvalue", subvalue) } } func TestReturnsErrorForInvalidSubvalueFormat(t *testing.T) { _, _, err := ExtractSubs("blah", ":") if err == nil { t.Errorf("expected error when calling extractSubs for invalid format (0 colons), got nil") } } tools-golang-0.5.5/spdx/v2/v2_3/rdf/reader_test.go000066400000000000000000000060511463371440000216130ustar00rootroot00000000000000package rdf import ( "io" "strings" "testing" ) func Test_Read(t *testing.T) { var reader io.Reader var err error // TestCase 1: invalid rdf/xml must raise an error reader = strings.NewReader("") _, err = Read(reader) if err == nil { t.Errorf("expected an EOF error reading an empty file, got %v", err) } // TestCase 2: Valid rdf/xml but invalid spdx document must raise an error reader = strings.NewReader(` `) _, err = Read(reader) if err == nil { t.Errorf("expected an error due to no SpdxDocument Node in the document") } // TestCase 3: New SPDX package elements reader = strings.NewReader(` SPDX-2.0 Some-Package 2021-10-15T02:38:00Z 2021-09-15T02:38:00Z 2022-10-15T02:38:00Z `) doc, err := Read(reader) if err != nil { t.Errorf("expected valid SPDX document: %v", err) } if doc == nil { t.Fatalf("expected valid SPDX document but got nil") } if len(doc.Packages) == 0 { t.Errorf("expected packages but got none") } pkg := doc.Packages[0] if pkg.PackageName != "Some-Package" { t.Errorf("expected package nameof Some-Package but got: %s", pkg.PackageName) } if pkg.PrimaryPackagePurpose != "CONTAINER" { t.Errorf("expected package primary purpose of CONTAINER but got: %s", pkg.PrimaryPackagePurpose) } if pkg.ReleaseDate != "2021-10-15T02:38:00Z" { t.Errorf("expected release date of 2021-10-15T02:38:00Z but got: %s", pkg.ReleaseDate) } if pkg.BuiltDate != "2021-09-15T02:38:00Z" { t.Errorf("expected built date of 2021-09-15T02:38:00Z but got: %s", pkg.BuiltDate) } if pkg.ValidUntilDate != "2022-10-15T02:38:00Z" { t.Errorf("expected valid until date of 2022-10-15T02:38:00Z but got: %s", pkg.ValidUntilDate) } } tools-golang-0.5.5/spdx/v2/v2_3/relationship.go000066400000000000000000000013671463371440000212450ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Relationship is a Relationship section of an SPDX Document type Relationship struct { // 11.1: Relationship // Cardinality: optional, one or more; one per Relationship // one mandatory for SPDX Document with multiple packages // RefA and RefB are first and second item // Relationship is type from 11.1.1 RefA common.DocElementID `json:"spdxElementId"` RefB common.DocElementID `json:"relatedSpdxElement"` Relationship string `json:"relationshipType"` // 11.2: Relationship Comment // Cardinality: optional, one RelationshipComment string `json:"comment,omitempty"` } tools-golang-0.5.5/spdx/v2/v2_3/review.go000066400000000000000000000013011463371440000200310ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 // Review is a Review section of an SPDX Document. // DEPRECATED in version 2.0 of spec; retained here for compatibility. type Review struct { // DEPRECATED in version 2.0 of spec // 13.1: Reviewer // Cardinality: optional, one Reviewer string // including AnnotatorType: one of "Person", "Organization" or "Tool" ReviewerType string // DEPRECATED in version 2.0 of spec // 13.2: Review Date: YYYY-MM-DDThh:mm:ssZ // Cardinality: conditional (mandatory, one) if there is a Reviewer ReviewDate string // DEPRECATED in version 2.0 of spec // 13.3: Review Comment // Cardinality: optional, one ReviewComment string } tools-golang-0.5.5/spdx/v2/v2_3/snippet.go000066400000000000000000000032041463371440000202160ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package v2_3 import ( "github.com/spdx/tools-golang/spdx/v2/common" ) // Snippet is a Snippet section of an SPDX Document type Snippet struct { // 9.1: Snippet SPDX Identifier: "SPDXRef-[idstring]" // Cardinality: mandatory, one SnippetSPDXIdentifier common.ElementID `json:"SPDXID"` // 9.2: Snippet from File SPDX Identifier // Cardinality: mandatory, one SnippetFromFileSPDXIdentifier common.ElementID `json:"snippetFromFile"` // Ranges denotes the start/end byte offsets or line numbers that the snippet is relevant to Ranges []common.SnippetRange `json:"ranges"` // 9.5: Snippet Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one SnippetLicenseConcluded string `json:"licenseConcluded,omitempty"` // 9.6: License Information in Snippet: SPDX License Expression, "NONE" or "NOASSERTION" // Cardinality: optional, one or many LicenseInfoInSnippet []string `json:"licenseInfoInSnippets,omitempty"` // 9.7: Snippet Comments on License // Cardinality: optional, one SnippetLicenseComments string `json:"licenseComments,omitempty"` // 9.8: Snippet Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION" // Cardinality: mandatory, one SnippetCopyrightText string `json:"copyrightText"` // 9.9: Snippet Comment // Cardinality: optional, one SnippetComment string `json:"comment,omitempty"` // 9.10: Snippet Name // Cardinality: optional, one SnippetName string `json:"name,omitempty"` // 9.11: Snippet Attribution Text // Cardinality: optional, one or many SnippetAttributionTexts []string `json:"-" yaml:"-"` } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/000077500000000000000000000000001463371440000200165ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/000077500000000000000000000000001463371440000212605ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_annotation.go000066400000000000000000000020531463371440000251530ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" ) func (parser *tvParser) parsePairForAnnotation(tag string, value string) error { if parser.ann == nil { return fmt.Errorf("no annotation struct created in parser ann pointer") } switch tag { case "Annotator": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if subkey == "Person" || subkey == "Organization" || subkey == "Tool" { parser.ann.Annotator.AnnotatorType = subkey parser.ann.Annotator.Annotator = subvalue return nil } return fmt.Errorf("unrecognized Annotator type %v", subkey) case "AnnotationDate": parser.ann.AnnotationDate = value case "AnnotationType": parser.ann.AnnotationType = value case "SPDXREF": deID, err := extractDocElementID(value) if err != nil { return err } parser.ann.AnnotationSPDXIdentifier = deID case "AnnotationComment": parser.ann.AnnotationComment = value default: return fmt.Errorf("received unknown tag %v in Annotation section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_annotation_test.go000066400000000000000000000110061463371440000262100ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Annotation section tests ===== func TestParserFailsIfAnnotationNotSet(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairForAnnotation("Annotator", "Person: John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected error when calling parsePairFromAnnotation without setting ann pointer") } } func TestParserFailsIfAnnotationTagUnknown(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // start with valid annotator err := parser.parsePair("Annotator", "Person: John Doe (jdoe@example.com)") if err != nil { t.Errorf("expected nil error, got %v", err) } // parse invalid tag, using parsePairForAnnotation( err = parser.parsePairForAnnotation("blah", "oops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotationFieldsWithoutAnnotation(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("AnnotationDate", "2018-09-15T17:25:00Z") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationDate without Annotator first") } err = parser.parsePair("AnnotationType", "REVIEW") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationType without Annotator first") } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err == nil { t.Errorf("expected error when calling parsePair for SPDXREF without Annotator first") } err = parser.parsePair("AnnotationComment", "comment whatever") if err == nil { t.Errorf("expected error when calling parsePair for AnnotationComment without Annotator first") } } func TestParserCanParseAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Annotator without email address err := parser.parsePair("Annotator", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.Annotator.Annotator != "John Doe" { t.Errorf("got %+v for Annotator, expected John Doe", parser.ann.Annotator.Annotator) } if parser.ann.Annotator.AnnotatorType != "Person" { t.Errorf("got %v for AnnotatorType, expected Person", parser.ann.Annotator.AnnotatorType) } // Annotation Date dt := "2018-09-15T17:32:00Z" err = parser.parsePair("AnnotationDate", dt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationDate != dt { t.Errorf("got %v for AnnotationDate, expected %v", parser.ann.AnnotationDate, dt) } // Annotation type aType := "REVIEW" err = parser.parsePair("AnnotationType", aType) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationType != aType { t.Errorf("got %v for AnnotationType, expected %v", parser.ann.AnnotationType, aType) } // SPDX Identifier Reference ref := "SPDXRef-30" err = parser.parsePair("SPDXREF", ref) if err != nil { t.Errorf("expected nil error, got %v", err) } deID := parser.ann.AnnotationSPDXIdentifier if deID.DocumentRefID != "" || deID.ElementRefID != "30" { t.Errorf("got %v for SPDXREF, expected %v", parser.ann.AnnotationSPDXIdentifier, "30") } // Annotation Comment cmt := "this is a comment" err = parser.parsePair("AnnotationComment", cmt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.ann.AnnotationComment != cmt { t.Errorf("got %v for AnnotationComment, expected %v", parser.ann.AnnotationComment, cmt) } } func TestParserFailsIfAnnotatorInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotatorTypeInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Human: John Doe (jdoe@example.com)") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfAnnotationRefInvalid(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // start with valid annotator err := parser.parsePair("Annotator", "Person: John Doe (jdoe@example.com)") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePair("SPDXREF", "blah:other") if err == nil { t.Errorf("expected non-nil error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_creation_info.go000066400000000000000000000117611463371440000256260ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *tvParser) parsePairFromCreationInfo(tag string, value string) error { // fail if not in Creation Info parser state if parser.st != psCreationInfo { return fmt.Errorf("got invalid state %v in parsePairFromCreationInfo", parser.st) } // create an SPDX Creation Info data struct if we don't have one already if parser.doc.CreationInfo == nil { parser.doc.CreationInfo = &spdx.CreationInfo{} } ci := parser.doc.CreationInfo switch tag { case "LicenseListVersion": ci.LicenseListVersion = value case "Creator": subkey, subvalue, err := extractSubs(value) if err != nil { return err } creator := common.Creator{Creator: subvalue} switch subkey { case "Person", "Organization", "Tool": creator.CreatorType = subkey default: return fmt.Errorf("unrecognized Creator type %v", subkey) } ci.Creators = append(ci.Creators, creator) case "Created": ci.Created = value case "CreatorComment": ci.CreatorComment = value // tag for going on to package section case "PackageName": // error if last file does not have an identifier // this may be a null case: can we ever have a "last file" in // the "creation info" state? should go on to "file" state // even when parsing unpackaged files. if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.st = psPackage parser.pkg = &spdx.Package{ FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, } return parser.parsePairFromPackage(tag, value) // tag for going on to _unpackaged_ file section case "FileName": // leave pkg as nil, so that packages will be placed in Files parser.st = psFile parser.pkg = nil return parser.parsePairFromFile(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &spdx.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &spdx.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) case "DocumentComment": parser.st = psStart return parser.parsePairFromStart(tag, value) default: return fmt.Errorf("received unknown tag %v in CreationInfo section", tag) } return nil } // ===== Helper functions ===== func extractExternalDocumentReference(value string) (string, string, string, string, error) { sp := strings.Split(value, " ") // remove any that are just whitespace keepSp := []string{} for _, s := range sp { ss := strings.TrimSpace(s) if ss != "" { keepSp = append(keepSp, ss) } } var documentRefID, uri, alg, checksum string // now, should have 4 items (or 3, if Alg and Checksum were joined) // and should be able to map them if len(keepSp) == 4 { documentRefID = keepSp[0] uri = keepSp[1] alg = keepSp[2] // check that colon is present for alg, and remove it if !strings.HasSuffix(alg, ":") { return "", "", "", "", fmt.Errorf("algorithm does not end with colon") } alg = strings.TrimSuffix(alg, ":") checksum = keepSp[3] } else if len(keepSp) == 3 { documentRefID = keepSp[0] uri = keepSp[1] // split on colon into alg and checksum parts := strings.SplitN(keepSp[2], ":", 2) if len(parts) != 2 { return "", "", "", "", fmt.Errorf("missing colon separator between algorithm and checksum") } alg = parts[0] checksum = parts[1] } else { return "", "", "", "", fmt.Errorf("expected 4 elements, got %d", len(keepSp)) } // additionally, we should be able to parse the first element as a // DocumentRef- ID string, and we should remove that prefix if !strings.HasPrefix(documentRefID, "DocumentRef-") { return "", "", "", "", fmt.Errorf("expected first element to have DocumentRef- prefix") } documentRefID = strings.TrimPrefix(documentRefID, "DocumentRef-") if documentRefID == "" { return "", "", "", "", fmt.Errorf("document identifier has nothing after prefix") } return documentRefID, uri, alg, checksum, nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_creation_info_test.go000066400000000000000000000325711463371440000266670ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Parser creation info state change tests ===== func TestParserCIMovesToPackageAfterParsingPackageNameTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } pkgName := "testPkg" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the package should NOT be in the SPDX Document's map of packages, // because it doesn't have an SPDX identifier yet if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } } func TestParserCIMovesToFileAfterParsingFileNameTagWithNoPackages(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("FileName", "testFile") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } // and current package should be nil, meaning Files are placed in the // Files map instead of in a Package if parser.pkg != nil { t.Fatalf("expected pkg to be nil, got non-nil pkg") } } func TestParserCIMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } } func TestParserCIMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } } func TestParserCIStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } } func TestParserCIStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this spdx file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psCreationInfo { t.Errorf("parser is in state %v, expected %v", parser.st, psCreationInfo) } } func TestParserFailsParsingCreationInfoWithInvalidState(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psPackage, } err := parser.parsePairFromCreationInfo("SPDXVersion", spdx.Version) if err == nil { t.Errorf("expected non-nil error, got nil") } } // ===== Creation Info section tests ===== func TestParserHasCreationInfoAfterCallToParseFirstTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("LicenseListVersion", "3.9") if err != nil { t.Errorf("got error when calling parsePairFromCreationInfo: %v", err) } if parser.doc.CreationInfo == nil { t.Errorf("doc.CreationInfo is still nil after parsing first pair") } } func TestParserCanParseCreationInfoTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // License List Version err := parser.parsePairFromCreationInfo("LicenseListVersion", "1.3") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.LicenseListVersion != "1.3" { t.Errorf("got %v for LicenseListVersion", parser.doc.CreationInfo.LicenseListVersion) } // Creators: Persons refPersons := []string{ "Person: Person A", "Person: Person B", } err = parser.parsePairFromCreationInfo("Creator", refPersons[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refPersons[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 2 || parser.doc.CreationInfo.Creators[0].Creator != "Person A" || parser.doc.CreationInfo.Creators[1].Creator != "Person B" { t.Errorf("got %v for CreatorPersons", parser.doc.CreationInfo.Creators) } // Creators: Organizations refOrgs := []string{ "Organization: Organization A", "Organization: Organization B", } err = parser.parsePairFromCreationInfo("Creator", refOrgs[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refOrgs[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 4 || parser.doc.CreationInfo.Creators[2].Creator != "Organization A" || parser.doc.CreationInfo.Creators[3].Creator != "Organization B" { t.Errorf("got %v for CreatorOrganizations", parser.doc.CreationInfo.Creators) } // Creators: Tools refTools := []string{ "Tool: Tool A", "Tool: Tool B", } err = parser.parsePairFromCreationInfo("Creator", refTools[0]) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromCreationInfo("Creator", refTools[1]) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.doc.CreationInfo.Creators) != 6 || parser.doc.CreationInfo.Creators[4].Creator != "Tool A" || parser.doc.CreationInfo.Creators[5].Creator != "Tool B" { t.Errorf("got %v for CreatorTools", parser.doc.CreationInfo.Creators) } // Created date err = parser.parsePairFromCreationInfo("Created", "2018-09-10T11:46:00Z") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.Created != "2018-09-10T11:46:00Z" { t.Errorf("got %v for Created", parser.doc.CreationInfo.Created) } // Creator Comment err = parser.parsePairFromCreationInfo("CreatorComment", "Blah whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.doc.CreationInfo.CreatorComment != "Blah whatever" { t.Errorf("got %v for CreatorComment", parser.doc.CreationInfo.CreatorComment) } } func TestParserInvalidCreatorTagsFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("Creator", "blah: somebody") if err == nil { t.Errorf("expected error from parsing invalid Creator format, got nil") } err = parser.parsePairFromCreationInfo("Creator", "Tool with no colons") if err == nil { t.Errorf("expected error from parsing invalid Creator format, got nil") } } func TestParserCreatorTagWithMultipleColonsPasses(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("Creator", "Tool: tool1:2:3") if err != nil { t.Errorf("unexpected error from parsing valid Creator format") } } func TestParserCIUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairFromCreationInfo("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserCICreatesRelationship(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserCICreatesAnnotation(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } // ===== Helper function tests ===== func TestCanExtractExternalDocumentReference(t *testing.T) { refstring := "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1:d6a770ba38583ed4bb4525bd96e50461655d2759" wantDocumentRefID := "spdx-tool-1.2" wantURI := "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" wantAlg := "SHA1" wantChecksum := "d6a770ba38583ed4bb4525bd96e50461655d2759" gotDocumentRefID, gotURI, gotAlg, gotChecksum, err := extractExternalDocumentReference(refstring) if err != nil { t.Errorf("got non-nil error: %v", err) } if wantDocumentRefID != gotDocumentRefID { t.Errorf("wanted document ref ID %s, got %s", wantDocumentRefID, gotDocumentRefID) } if wantURI != gotURI { t.Errorf("wanted URI %s, got %s", wantURI, gotURI) } if wantAlg != gotAlg { t.Errorf("wanted alg %s, got %s", wantAlg, gotAlg) } if wantChecksum != gotChecksum { t.Errorf("wanted checksum %s, got %s", wantChecksum, gotChecksum) } } func TestCanExtractExternalDocumentReferenceWithExtraWhitespace(t *testing.T) { refstring := " DocumentRef-spdx-tool-1.2 \t http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 \t SHA1: \t d6a770ba38583ed4bb4525bd96e50461655d2759" wantDocumentRefID := "spdx-tool-1.2" wantURI := "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" wantAlg := "SHA1" wantChecksum := "d6a770ba38583ed4bb4525bd96e50461655d2759" gotDocumentRefID, gotURI, gotAlg, gotChecksum, err := extractExternalDocumentReference(refstring) if err != nil { t.Errorf("got non-nil error: %v", err) } if wantDocumentRefID != gotDocumentRefID { t.Errorf("wanted document ref ID %s, got %s", wantDocumentRefID, gotDocumentRefID) } if wantURI != gotURI { t.Errorf("wanted URI %s, got %s", wantURI, gotURI) } if wantAlg != gotAlg { t.Errorf("wanted alg %s, got %s", wantAlg, gotAlg) } if wantChecksum != gotChecksum { t.Errorf("wanted checksum %s, got %s", wantChecksum, gotChecksum) } } func TestFailsExternalDocumentReferenceWithInvalidFormats(t *testing.T) { invalidRefs := []string{ "whoops", "DocumentRef-", "DocumentRef- ", "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", "DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 d6a770ba38583ed4bb4525bd96e50461655d2759", "DocumentRef-spdx-tool-1.2", "spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1:d6a770ba38583ed4bb4525bd96e50461655d2759", } for _, refstring := range invalidRefs { _, _, _, _, err := extractExternalDocumentReference(refstring) if err == nil { t.Errorf("expected non-nil error for %s, got nil", refstring) } } } func TestParserForDocumentComment(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("DocumentComment", "document comment") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_file.go000066400000000000000000000122061463371440000237210ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *tvParser) parsePairFromFile(tag string, value string) error { // expire fileAOP for anything other than an AOPHomePage or AOPURI // (we'll actually handle the HomePage and URI further below) if tag != "ArtifactOfProjectHomePage" && tag != "ArtifactOfProjectURI" { parser.fileAOP = nil } switch tag { // tag for creating new file section case "FileName": // check if the previous file contained an spdx Id or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.file = &spdx.File{} parser.file.FileName = value // tag for creating new package section and going back to parsing Package case "PackageName": parser.st = psPackage // check if the previous file contained an spdx Id or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.file = nil return parser.parsePairFromPackage(tag, value) // tag for going on to snippet section case "SnippetSPDXID": parser.st = psSnippet return parser.parsePairFromSnippet(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tags for file data case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.file.FileSPDXIdentifier = eID if parser.pkg == nil { if parser.doc.Files == nil { parser.doc.Files = []*spdx.File{} } parser.doc.Files = append(parser.doc.Files, parser.file) } else { if parser.pkg.Files == nil { parser.pkg.Files = []*spdx.File{} } parser.pkg.Files = append(parser.pkg.Files, parser.file) } case "FileType": parser.file.FileTypes = append(parser.file.FileTypes, value) case "FileChecksum": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if parser.file.Checksums == nil { parser.file.Checksums = []common.Checksum{} } switch common.ChecksumAlgorithm(subkey) { case common.SHA1, common.SHA224, common.SHA256, common.SHA384, common.SHA512, common.MD2, common.MD4, common.MD5, common.MD6, common.SHA3_256, common.SHA3_384, common.SHA3_512, common.BLAKE2b_256, common.BLAKE2b_384, common.BLAKE2b_512, common.BLAKE3, common.ADLER32: algorithm := common.ChecksumAlgorithm(subkey) parser.file.Checksums = append(parser.file.Checksums, common.Checksum{Algorithm: algorithm, Value: subvalue}) default: return fmt.Errorf("got unknown checksum type %s", subkey) } case "LicenseConcluded": parser.file.LicenseConcluded = value case "LicenseInfoInFile": parser.file.LicenseInfoInFiles = append(parser.file.LicenseInfoInFiles, value) case "LicenseComments": parser.file.LicenseComments = value case "FileCopyrightText": parser.file.FileCopyrightText = value case "ArtifactOfProjectName": parser.fileAOP = &spdx.ArtifactOfProject{} parser.file.ArtifactOfProjects = append(parser.file.ArtifactOfProjects, parser.fileAOP) parser.fileAOP.Name = value case "ArtifactOfProjectHomePage": if parser.fileAOP == nil { return fmt.Errorf("no current ArtifactOfProject found") } parser.fileAOP.HomePage = value case "ArtifactOfProjectURI": if parser.fileAOP == nil { return fmt.Errorf("no current ArtifactOfProject found") } parser.fileAOP.URI = value case "FileComment": parser.file.FileComment = value case "FileNotice": parser.file.FileNotice = value case "FileContributor": parser.file.FileContributors = append(parser.file.FileContributors, value) case "FileDependency": parser.file.FileDependencies = append(parser.file.FileDependencies, value) case "FileAttributionText": parser.file.FileAttributionTexts = append(parser.file.FileAttributionTexts, value) // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &spdx.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &spdx.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in File section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_file_test.go000066400000000000000000000753641463371440000247760ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Parser file section state change tests ===== func TestParserFileStartsNewFileAfterParsingFileNameTag(t *testing.T) { // create the first file fileOldName := "f1.txt" parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: fileOldName, FileSPDXIdentifier: "f1"}, } fileOld := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, fileOld) // the Package's Files should have this one only if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } // now add a new file fileName := "f2.txt" err := parser.parsePair("FileName", fileName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } // and a file should be created if parser.file == nil { t.Fatalf("parser didn't create new file") } // and the file name should be as expected if parser.file.FileName != fileName { t.Errorf("expected file name %s, got %s", fileName, parser.file.FileName) } // and the Package's Files should still be of size 1 and not have this new // one yet, since it hasn't seen an SPDX identifier if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } // now parse an SPDX identifier tag err = parser.parsePair("SPDXID", "SPDXRef-f2ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // and the Package's Files should now be of size 2 and have this new one if len(parser.pkg.Files) != 2 { t.Fatalf("expected 2 files, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != fileOld { t.Errorf("expected file %v in Files[f1], got %v", fileOld, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != fileOldName { t.Errorf("expected file name %s in Files[f1], got %s", fileOldName, parser.pkg.Files[0].FileName) } if parser.pkg.Files[1] != parser.file { t.Errorf("expected file %v in Files[f2ID], got %v", parser.file, parser.pkg.Files[1]) } if parser.pkg.Files[1].FileName != fileName { t.Errorf("expected file name %s in Files[f2ID], got %s", fileName, parser.pkg.Files[1].FileName) } } func TestParserFileAddsToPackageOrUnpackagedFiles(t *testing.T) { // start with no packages parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // add a file and SPDX identifier fileName := "f2.txt" err := parser.parsePair("FileName", fileName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-f2ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } fileOld := parser.file // should have been added to Files if len(parser.doc.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.doc.Files)) } if parser.doc.Files[0] != fileOld { t.Errorf("expected file %v in Files[f2ID], got %v", fileOld, parser.doc.Files[0]) } // now create a package and a new file err = parser.parsePair("PackageName", "package1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-pkg1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("FileName", "f3.txt") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } err = parser.parsePair("SPDXID", "SPDXRef-f3ID") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // Files should still be size 1 and have old file only if len(parser.doc.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.doc.Files)) } if parser.doc.Files[0] != fileOld { t.Errorf("expected file %v in Files[f2ID], got %v", fileOld, parser.doc.Files[0]) } // and new package should have gotten the new file if len(parser.pkg.Files) != 1 { t.Fatalf("expected 1 file in Files, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != parser.file { t.Errorf("expected file %v in Files[f3ID], got %v", parser.file, parser.pkg.Files[0]) } } func TestParserFileStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { // create the first file and package p1Name := "package1" f1Name := "f1.txt" parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: p1Name, PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: f1Name, FileSPDXIdentifier: "f1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, p1) parser.pkg.Files = append(parser.pkg.Files, f1) // now add a new package p2Name := "package2" err := parser.parsePair("PackageName", p2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should go back to Package if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new pkg") } // and the package name should be as expected if parser.pkg.PackageName != p2Name { t.Errorf("expected package name %s, got %s", p2Name, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the new Package should have no files if len(parser.pkg.Files) != 0 { t.Errorf("Expected no files in pkg.Files, got %d", len(parser.pkg.Files)) } // and the Document's Packages should still be of size 1 and not have this // new one, because no SPDX identifier has been seen yet if len(parser.doc.Packages) != 1 { t.Fatalf("expected 1 package, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != p1 { t.Errorf("Expected package %v in Packages[package1], got %v", p1, parser.doc.Packages[0]) } if parser.doc.Packages[0].PackageName != p1Name { t.Errorf("expected package name %s in Packages[package1], got %s", p1Name, parser.doc.Packages[0].PackageName) } // and the first Package's Files should be of size 1 and have f1 only if len(parser.doc.Packages[0].Files) != 1 { t.Errorf("Expected 1 file in Packages[package1].Files, got %d", len(parser.doc.Packages[0].Files)) } if parser.doc.Packages[0].Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.doc.Packages[0].Files[0]) } if parser.doc.Packages[0].Files[0].FileName != f1Name { t.Errorf("expected file name %s in Files[f1], got %s", f1Name, parser.doc.Packages[0].Files[0].FileName) } // and the current file should be nil if parser.file != nil { t.Errorf("Expected nil for parser.file, got %v", parser.file) } } func TestParserFileMovesToSnippetAfterParsingSnippetSPDXIDTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) fileCurrent := parser.file err := parser.parsePair("SnippetSPDXID", "SPDXRef-Test1") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and current file should remain what it was if parser.file != fileCurrent { t.Fatalf("expected file to remain %v, got %v", fileCurrent, parser.file) } } func TestParserFileMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserFileMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserFileStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } } func TestParserFileStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psFile { t.Errorf("parser is in state %v, expected %v", parser.st, psFile) } } // ===== File data section tests ===== func TestParserCanParseFileTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileName != "f1.txt" { t.Errorf("got %v for FileName", parser.file.FileName) } // should not yet be added to the Packages file list, because we haven't // seen an SPDX identifier yet if len(parser.pkg.Files) != 0 { t.Errorf("expected 0 files, got %d", len(parser.pkg.Files)) } // File SPDX Identifier err = parser.parsePairFromFile("SPDXID", "SPDXRef-f1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileSPDXIdentifier != "f1" { t.Errorf("got %v for FileSPDXIdentifier", parser.file.FileSPDXIdentifier) } // should now be added to the Packages file list if len(parser.pkg.Files) != 1 { t.Errorf("expected 1 file, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != parser.file { t.Errorf("expected Files[f1] to be %v, got %v", parser.file, parser.pkg.Files[0]) } // File Type fileTypes := []string{ "TEXT", "DOCUMENTATION", } for _, ty := range fileTypes { err = parser.parsePairFromFile("FileType", ty) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, typeWant := range fileTypes { flagFound := false for _, typeCheck := range parser.file.FileTypes { if typeWant == typeCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileTypes", typeWant) } } if len(fileTypes) != len(parser.file.FileTypes) { t.Errorf("expected %d types in FileTypes, got %d", len(fileTypes), len(parser.file.FileTypes)) } testChecksums := map[common.ChecksumAlgorithm]string{ "MD5": "624c1abb3664f4b35547e7c73864ad24", "SHA1": "85ed0817af83a24ad8da68c2b5094de69833983c", "SHA256": "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", "SHA512": "4ced3267f5ed38df65ceebc43e97aa6c2948cc7ef3288c2e5074e7df7fab544cc93339604513ea5f65616f9ed1c48581465043c8a9b693ef20fd4fddaf25e1b9", "BLAKE3": "981d32ed7aad9e408c5c36f6346c915ba11c2bd8b3e7d44902a11d7a141abdd9", } for algo, tc := range testChecksums { if err := parser.parsePairFromFile( "FileChecksum", fmt.Sprintf("%s: %s", algo, tc)); err != nil { t.Errorf("expected error, got %v", err) } } for _, checksum := range parser.file.Checksums { if checksum.Value != testChecksums[checksum.Algorithm] { t.Errorf( "expected %s for FileChecksum%s, got %s", testChecksums[checksum.Algorithm], checksum.Algorithm, checksum.Value, ) } } // Concluded License err = parser.parsePairFromFile("LicenseConcluded", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.LicenseConcluded != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for LicenseConcluded", parser.file.LicenseConcluded) } // License Information in File lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromFile("LicenseInfoInFile", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.file.LicenseInfoInFiles { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseInfoInFiles", licWant) } } if len(lics) != len(parser.file.LicenseInfoInFiles) { t.Errorf("expected %d licenses in LicenseInfoInFiles, got %d", len(lics), len(parser.file.LicenseInfoInFiles)) } // Comments on License err = parser.parsePairFromFile("LicenseComments", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.LicenseComments != "this is a comment" { t.Errorf("got %v for LicenseComments", parser.file.LicenseComments) } // Copyright Text err = parser.parsePairFromFile("FileCopyrightText", "copyright (c) me") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileCopyrightText != "copyright (c) me" { t.Errorf("got %v for FileCopyrightText", parser.file.FileCopyrightText) } // Artifact of Projects: Name, HomePage and URI // Artifact set 1 err = parser.parsePairFromFile("ArtifactOfProjectName", "project1") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/1/") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/1/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 2 -- just name err = parser.parsePairFromFile("ArtifactOfProjectName", "project2") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 3 -- just name and home page err = parser.parsePairFromFile("ArtifactOfProjectName", "project3") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/3/") if err != nil { t.Errorf("expected nil error, got %v", err) } // Artifact set 4 -- just name and URI err = parser.parsePairFromFile("ArtifactOfProjectName", "project4") if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/4/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.file.ArtifactOfProjects) != 4 { t.Fatalf("expected len %d, got %d", 4, len(parser.file.ArtifactOfProjects)) } aop := parser.file.ArtifactOfProjects[0] if aop.Name != "project1" { t.Errorf("expected %v, got %v", "project1", aop.Name) } if aop.HomePage != "http://example.com/1/" { t.Errorf("expected %v, got %v", "http://example.com/1/", aop.HomePage) } if aop.URI != "http://example.com/1/uri.whatever" { t.Errorf("expected %v, got %v", "http://example.com/1/uri.whatever", aop.URI) } aop = parser.file.ArtifactOfProjects[1] if aop.Name != "project2" { t.Errorf("expected %v, got %v", "project2", aop.Name) } if aop.HomePage != "" { t.Errorf("expected %v, got %v", "", aop.HomePage) } if aop.URI != "" { t.Errorf("expected %v, got %v", "", aop.URI) } aop = parser.file.ArtifactOfProjects[2] if aop.Name != "project3" { t.Errorf("expected %v, got %v", "project3", aop.Name) } if aop.HomePage != "http://example.com/3/" { t.Errorf("expected %v, got %v", "http://example.com/3/", aop.HomePage) } if aop.URI != "" { t.Errorf("expected %v, got %v", "", aop.URI) } aop = parser.file.ArtifactOfProjects[3] if aop.Name != "project4" { t.Errorf("expected %v, got %v", "project4", aop.Name) } if aop.HomePage != "" { t.Errorf("expected %v, got %v", "", aop.HomePage) } if aop.URI != "http://example.com/4/uri.whatever" { t.Errorf("expected %v, got %v", "http://example.com/4/uri.whatever", aop.URI) } // File Comment err = parser.parsePairFromFile("FileComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileComment != "this is a comment" { t.Errorf("got %v for FileComment", parser.file.FileComment) } // File Notice err = parser.parsePairFromFile("FileNotice", "this is a Notice") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.file.FileNotice != "this is a Notice" { t.Errorf("got %v for FileNotice", parser.file.FileNotice) } // File Contributor contribs := []string{ "John Doe jdoe@example.com", "EvilCorp", } for _, contrib := range contribs { err = parser.parsePairFromFile("FileContributor", contrib) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, contribWant := range contribs { flagFound := false for _, contribCheck := range parser.file.FileContributors { if contribWant == contribCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileContributors", contribWant) } } if len(contribs) != len(parser.file.FileContributors) { t.Errorf("expected %d contribenses in FileContributors, got %d", len(contribs), len(parser.file.FileContributors)) } // File Dependencies deps := []string{ "f-1.txt", "g.txt", } for _, dep := range deps { err = parser.parsePairFromFile("FileDependency", dep) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, depWant := range deps { flagFound := false for _, depCheck := range parser.file.FileDependencies { if depWant == depCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileDependency", depWant) } } if len(deps) != len(parser.file.FileDependencies) { t.Errorf("expected %d depenses in FileDependency, got %d", len(deps), len(parser.file.FileDependencies)) } // File Attribution Texts attrs := []string{ "Include this notice in all advertising materials", "This is a \nmulti-line string", } for _, attr := range attrs { err = parser.parsePairFromFile("FileAttributionText", attr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, attrWant := range attrs { flagFound := false for _, attrCheck := range parser.file.FileAttributionTexts { if attrWant == attrCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in FileAttributionText", attrWant) } } if len(attrs) != len(parser.file.FileAttributionTexts) { t.Errorf("expected %d attribution texts in FileAttributionTexts, got %d", len(attrs), len(parser.file.FileAttributionTexts)) } } func TestParserFileCreatesRelationshipInDocument(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserFileCreatesAnnotationInDocument(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } func TestParserFileUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromFile("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestFileAOPPointerChangesAfterTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromFile("ArtifactOfProjectName", "project1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP == nil { t.Errorf("expected non-nil AOP pointer, got nil") } curPtr := parser.fileAOP // now, a home page; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "http://example.com/1/") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != curPtr { t.Errorf("expected no change in AOP pointer, was %v, got %v", curPtr, parser.fileAOP) } // a URI; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectURI", "http://example.com/1/uri.whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != curPtr { t.Errorf("expected no change in AOP pointer, was %v, got %v", curPtr, parser.fileAOP) } // now, another artifact name; pointer should change but be non-nil // now, a home page; pointer should stay err = parser.parsePairFromFile("ArtifactOfProjectName", "project2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP == curPtr { t.Errorf("expected change in AOP pointer, got no change") } // finally, an unrelated tag; pointer should go away err = parser.parsePairFromFile("FileComment", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.fileAOP != nil { t.Errorf("expected nil AOP pointer, got %v", parser.fileAOP) } } func TestParserFailsIfInvalidSPDXIDInFileSection(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid SPDX Identifier err = parser.parsePairFromFile("SPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidChecksumFormatInFileSection(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid format for checksum line, missing colon err = parser.parsePairFromFile("FileChecksum", "SHA1 85ed0817af83a24ad8da68c2b5094de69833983c") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownChecksumTypeInFileSection(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // unknown checksum type err = parser.parsePairFromFile("FileChecksum", "Special: 85ed0817af83a24ad8da68c2b5094de69833983c") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfArtifactHomePageBeforeArtifactName(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // artifact home page appears before artifact name err = parser.parsePairFromFile("ArtifactOfProjectHomePage", "https://example.com") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfArtifactURIBeforeArtifactName(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // start with File Name err := parser.parsePairFromFile("FileName", "f1.txt") if err != nil { t.Errorf("expected nil error, got %v", err) } // artifact home page appears before artifact name err = parser.parsePairFromFile("ArtifactOfProjectURI", "https://example.com") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowError(t *testing.T) { // case 1: The previous file (packaged or unpackaged) does not contain spdx ID parser1 := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, file: &spdx.File{FileName: "FileName"}, } err := parser1.parsePair("FileName", "f2") if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } // case 2: Invalid file with snippet // Last unpackaged file before the snippet start fileName := "f2.txt" sid1 := common.ElementID("s1") parser2 := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, file: &spdx.File{FileName: fileName}, } err = parser2.parsePair("SnippetSPDXID", string(sid1)) if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } // case 3: Invalid File without snippets // Last unpackaged file before the package starts // Last file of a package and New package starts parser3 := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } fileName = "f3.txt" err = parser3.parsePair("FileName", fileName) if err != nil { t.Errorf("%s", err) } err = parser3.parsePair("PackageName", "p2") if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_other_license.go000066400000000000000000000035431463371440000256310ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *tvParser) parsePairFromOtherLicense(tag string, value string) error { switch tag { // tag for creating new other license section case "LicenseID": parser.otherLic = &spdx.OtherLicense{} parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.otherLic.LicenseIdentifier = value case "ExtractedText": parser.otherLic.ExtractedText = value case "LicenseName": parser.otherLic.LicenseName = value case "LicenseCrossReference": parser.otherLic.LicenseCrossReferences = append(parser.otherLic.LicenseCrossReferences, value) case "LicenseComment": parser.otherLic.LicenseComment = value // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &spdx.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &spdx.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in OtherLicense section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_other_license_test.go000066400000000000000000000306441463371440000266720ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Parser other license section state change tests ===== func TestParserOLStartsNewOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { // create the first other license olid1 := "LicenseRef-Lic11" olname1 := "License 11" parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psOtherLicense, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: olid1, LicenseName: olname1, }, } olic1 := parser.otherLic parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // the Document's OtherLicenses should have this one only if parser.doc.OtherLicenses[0] != olic1 { t.Errorf("Expected other license %v in OtherLicenses[0], got %v", olic1, parser.doc.OtherLicenses[0]) } if parser.doc.OtherLicenses[0].LicenseName != olname1 { t.Errorf("expected other license name %s in OtherLicenses[0], got %s", olname1, parser.doc.OtherLicenses[0].LicenseName) } // now add a new other license olid2 := "LicenseRef-22" olname2 := "License 22" err := parser.parsePair("LicenseID", olid2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and an other license should be created if parser.otherLic == nil { t.Fatalf("parser didn't create new other license") } // also parse the new license's name err = parser.parsePair("LicenseName", olname2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still be correct if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and the other license name should be as expected if parser.otherLic.LicenseName != olname2 { t.Errorf("expected other license name %s, got %s", olname2, parser.otherLic.LicenseName) } // and the Document's Other Licenses should be of size 2 and have these two if len(parser.doc.OtherLicenses) != 2 { t.Fatalf("Expected OtherLicenses to have len 2, got %d", len(parser.doc.OtherLicenses)) } if parser.doc.OtherLicenses[0] != olic1 { t.Errorf("Expected other license %v in OtherLicenses[0], got %v", olic1, parser.doc.OtherLicenses[0]) } if parser.doc.OtherLicenses[0].LicenseIdentifier != olid1 { t.Errorf("expected other license ID %s in OtherLicenses[0], got %s", olid1, parser.doc.OtherLicenses[0].LicenseIdentifier) } if parser.doc.OtherLicenses[0].LicenseName != olname1 { t.Errorf("expected other license name %s in OtherLicenses[0], got %s", olname1, parser.doc.OtherLicenses[0].LicenseName) } if parser.doc.OtherLicenses[1] != parser.otherLic { t.Errorf("Expected other license %v in OtherLicenses[1], got %v", parser.otherLic, parser.doc.OtherLicenses[1]) } if parser.doc.OtherLicenses[1].LicenseIdentifier != olid2 { t.Errorf("expected other license ID %s in OtherLicenses[1], got %s", olid2, parser.doc.OtherLicenses[1].LicenseIdentifier) } if parser.doc.OtherLicenses[1].LicenseName != olname2 { t.Errorf("expected other license name %s in OtherLicenses[1], got %s", olname2, parser.doc.OtherLicenses[1].LicenseName) } } func TestParserOLMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psOtherLicense, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserOtherLicenseStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psOtherLicense, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-whatever", LicenseName: "the whatever license", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserOtherLicenseStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psOtherLicense, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-whatever", LicenseName: "the whatever license", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("parser is in state %v, expected %v", parser.st, psOtherLicense) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } func TestParserOLFailsAfterParsingOtherSectionTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psOtherLicense, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // can't go back to old sections err := parser.parsePair("SPDXVersion", spdx.Version) if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("PackageName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("FileName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } } // ===== Other License data section tests ===== func TestParserCanParseOtherLicenseTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psOtherLicense, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) // License Identifier err := parser.parsePairFromOtherLicense("LicenseID", "LicenseRef-Lic11") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseIdentifier != "LicenseRef-Lic11" { t.Errorf("got %v for LicenseID", parser.otherLic.LicenseIdentifier) } // Extracted Text err = parser.parsePairFromOtherLicense("ExtractedText", "You are permitted to do anything with the software, hooray!") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.ExtractedText != "You are permitted to do anything with the software, hooray!" { t.Errorf("got %v for ExtractedText", parser.otherLic.ExtractedText) } // License Name err = parser.parsePairFromOtherLicense("LicenseName", "License 11") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseName != "License 11" { t.Errorf("got %v for LicenseName", parser.otherLic.LicenseName) } // License Cross Reference crossRefs := []string{ "https://example.com/1", "https://example.com/2", "https://example.com/3", } for _, cr := range crossRefs { err = parser.parsePairFromOtherLicense("LicenseCrossReference", cr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, refWant := range crossRefs { flagFound := false for _, refCheck := range parser.otherLic.LicenseCrossReferences { if refWant == refCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseCrossReferences", refWant) } } if len(crossRefs) != len(parser.otherLic.LicenseCrossReferences) { t.Errorf("expected %d types in LicenseCrossReferences, got %d", len(crossRefs), len(parser.otherLic.LicenseCrossReferences)) } // License Comment err = parser.parsePairFromOtherLicense("LicenseComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.otherLic.LicenseComment != "this is a comment" { t.Errorf("got %v for LicenseComment", parser.otherLic.LicenseComment) } } func TestParserOLUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psOtherLicense, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) err := parser.parsePairFromOtherLicense("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_package.go000066400000000000000000000174671463371440000244130ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *tvParser) parsePairFromPackage(tag string, value string) error { // expire pkgExtRef for anything other than a comment // (we'll actually handle the comment further below) if tag != "ExternalRefComment" { parser.pkgExtRef = nil } switch tag { case "PackageName": // if package already has a name, create and go on to a new package if parser.pkg == nil || parser.pkg.PackageName != "" { // check if the previous package contained an spdx Id or not if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName) } parser.pkg = &spdx.Package{ FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, } } parser.pkg.PackageName = value // tag for going on to file section case "FileName": parser.st = psFile return parser.parsePairFromFile(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.pkg.PackageSPDXIdentifier = eID if parser.doc.Packages == nil { parser.doc.Packages = []*spdx.Package{} } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) case "PackageVersion": parser.pkg.PackageVersion = value case "PackageFileName": parser.pkg.PackageFileName = value case "PackageSupplier": supplier := &common.Supplier{Supplier: value} if value == "NOASSERTION" { parser.pkg.PackageSupplier = supplier break } subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person", "Organization": supplier.Supplier = subvalue supplier.SupplierType = subkey default: return fmt.Errorf("unrecognized PackageSupplier type %v", subkey) } parser.pkg.PackageSupplier = supplier case "PackageOriginator": originator := &common.Originator{Originator: value} if value == "NOASSERTION" { parser.pkg.PackageOriginator = originator break } subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person", "Organization": originator.Originator = subvalue originator.OriginatorType = subkey default: return fmt.Errorf("unrecognized PackageOriginator type %v", subkey) } parser.pkg.PackageOriginator = originator case "PackageDownloadLocation": parser.pkg.PackageDownloadLocation = value case "FilesAnalyzed": parser.pkg.IsFilesAnalyzedTagPresent = true if value == "false" { parser.pkg.FilesAnalyzed = false } else if value == "true" { parser.pkg.FilesAnalyzed = true } case "PackageVerificationCode": parser.pkg.PackageVerificationCode = extractCodeAndExcludes(value) case "PackageChecksum": subkey, subvalue, err := extractSubs(value) if err != nil { return err } if parser.pkg.PackageChecksums == nil { parser.pkg.PackageChecksums = []common.Checksum{} } switch common.ChecksumAlgorithm(subkey) { case common.SHA1, common.SHA224, common.SHA256, common.SHA384, common.SHA512, common.MD2, common.MD4, common.MD5, common.MD6, common.SHA3_256, common.SHA3_384, common.SHA3_512, common.BLAKE2b_256, common.BLAKE2b_384, common.BLAKE2b_512, common.BLAKE3, common.ADLER32: algorithm := common.ChecksumAlgorithm(subkey) parser.pkg.PackageChecksums = append(parser.pkg.PackageChecksums, common.Checksum{Algorithm: algorithm, Value: subvalue}) default: return fmt.Errorf("got unknown checksum type %s", subkey) } case "PackageHomePage": parser.pkg.PackageHomePage = value case "PackageSourceInfo": parser.pkg.PackageSourceInfo = value case "PackageLicenseConcluded": parser.pkg.PackageLicenseConcluded = value case "PackageLicenseInfoFromFiles": parser.pkg.PackageLicenseInfoFromFiles = append(parser.pkg.PackageLicenseInfoFromFiles, value) case "PackageLicenseDeclared": parser.pkg.PackageLicenseDeclared = value case "PackageLicenseComments": parser.pkg.PackageLicenseComments = value case "PackageCopyrightText": parser.pkg.PackageCopyrightText = value case "PackageSummary": parser.pkg.PackageSummary = value case "PackageDescription": parser.pkg.PackageDescription = value case "PackageComment": parser.pkg.PackageComment = value case "PrimaryPackagePurpose": parser.pkg.PrimaryPackagePurpose = value case "ReleaseDate": parser.pkg.ReleaseDate = value case "BuiltDate": parser.pkg.BuiltDate = value case "ValidUntilDate": parser.pkg.ValidUntilDate = value case "PackageAttributionText": parser.pkg.PackageAttributionTexts = append(parser.pkg.PackageAttributionTexts, value) case "ExternalRef": parser.pkgExtRef = &spdx.PackageExternalReference{} parser.pkg.PackageExternalReferences = append(parser.pkg.PackageExternalReferences, parser.pkgExtRef) category, refType, locator, err := extractPackageExternalReference(value) if err != nil { return err } parser.pkgExtRef.Category = category parser.pkgExtRef.RefType = refType parser.pkgExtRef.Locator = locator case "ExternalRefComment": if parser.pkgExtRef == nil { return fmt.Errorf("no current ExternalRef found") } parser.pkgExtRef.ExternalRefComment = value // now, expire pkgExtRef anyway because it can have at most one comment parser.pkgExtRef = nil // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &spdx.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &spdx.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in Package section", tag) } return nil } // ===== Helper functions ===== func extractCodeAndExcludes(value string) *common.PackageVerificationCode { // FIXME this should probably be done using regular expressions instead // split by paren + word "excludes:" sp := strings.SplitN(value, "(excludes:", 2) if len(sp) < 2 { // not found; return the whole string as just the code return &common.PackageVerificationCode{Value: value, ExcludedFiles: []string{}} } // if we're here, code is in first part and excludes filename is in // second part, with trailing paren code := strings.TrimSpace(sp[0]) parsedSp := strings.SplitN(sp[1], ")", 2) fileName := strings.TrimSpace(parsedSp[0]) return &common.PackageVerificationCode{Value: code, ExcludedFiles: []string{fileName}} } func extractPackageExternalReference(value string) (string, string, string, error) { sp := strings.Split(value, " ") // remove any that are just whitespace keepSp := []string{} for _, s := range sp { ss := strings.TrimSpace(s) if ss != "" { keepSp = append(keepSp, ss) } } // now, should have 3 items and should be able to map them if len(keepSp) != 3 { return "", "", "", fmt.Errorf("expected 3 elements, got %d", len(keepSp)) } return keepSp[0], keepSp[1], keepSp[2], nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_package_test.go000066400000000000000000001077161463371440000254470ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Parser package section state change tests ===== func TestParserPackageStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { // create the first package pkgOldName := "p1" parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: pkgOldName, PackageSPDXIdentifier: "p1"}, } pkgOld := parser.pkg parser.doc.Packages = append(parser.doc.Packages, pkgOld) // the Document's Packages should have this one only if parser.doc.Packages[0] != pkgOld { t.Errorf("expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } // now add a new package pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and it should not be pkgOld if parser.pkg == pkgOld { t.Errorf("expected new package, got pkgOld") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should still be of size 1 and have pkgOld only if parser.doc.Packages[0] != pkgOld { t.Errorf("Expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } } func TestParserPackageStartsNewPackageAfterParsingPackageNameTagWhileInUnpackaged(t *testing.T) { // pkg is nil, so that Files appearing before the first PackageName tag // are added to Files instead of Packages parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psFile, pkg: nil, } // the Document's Packages should be empty if len(parser.doc.Packages) != 0 { t.Errorf("Expected zero packages, got %d", len(parser.doc.Packages)) } // now add a new package pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should be of size 0, because the prior was // unpackaged files and this one won't be added until an SPDXID is seen if len(parser.doc.Packages) != 0 { t.Errorf("Expected %v packages in doc, got %v", 0, len(parser.doc.Packages)) } } func TestParserPackageMovesToFileAfterParsingFileNameTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) pkgCurrent := parser.pkg err := parser.parsePair("FileName", "testFile") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psFile, parser.st) } // and current package should remain what it was if parser.pkg != pkgCurrent { t.Fatalf("expected package to remain %v, got %v", pkgCurrent, parser.pkg) } } func TestParserPackageMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserPackageMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserPackageStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } } func TestParserPackageStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this package") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psPackage { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage) } } // ===== Package data section tests ===== func TestParserCanParsePackageTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // should not yet be in Packages map, b/c no SPDX identifier if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } // Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageName != "p1" { t.Errorf("got %v for PackageName", parser.pkg.PackageName) } // still should not yet be in Packages map, b/c no SPDX identifier if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } // Package SPDX Identifier err = parser.parsePairFromPackage("SPDXID", "SPDXRef-p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // "SPDXRef-" prefix should be removed from the item if parser.pkg.PackageSPDXIdentifier != "p1" { t.Errorf("got %v for PackageSPDXIdentifier", parser.pkg.PackageSPDXIdentifier) } // and it should now be added to the Packages map if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != parser.pkg { t.Errorf("expected to point to parser.pkg, got %v", parser.doc.Packages[0]) } // Package Version err = parser.parsePairFromPackage("PackageVersion", "2.1.1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVersion != "2.1.1" { t.Errorf("got %v for PackageVersion", parser.pkg.PackageVersion) } // Package File Name err = parser.parsePairFromPackage("PackageFileName", "p1-2.1.1.tar.gz") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageFileName != "p1-2.1.1.tar.gz" { t.Errorf("got %v for PackageFileName", parser.pkg.PackageFileName) } // Package Supplier // SKIP -- separate tests for subvalues below // Package Originator // SKIP -- separate tests for subvalues below // Package Download Location err = parser.parsePairFromPackage("PackageDownloadLocation", "https://example.com/whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageDownloadLocation != "https://example.com/whatever" { t.Errorf("got %v for PackageDownloadLocation", parser.pkg.PackageDownloadLocation) } // Files Analyzed err = parser.parsePairFromPackage("FilesAnalyzed", "false") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.FilesAnalyzed != false { t.Errorf("got %v for FilesAnalyzed", parser.pkg.FilesAnalyzed) } if parser.pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("got %v for IsFilesAnalyzedTagPresent", parser.pkg.IsFilesAnalyzedTagPresent) } // Package Verification Code // SKIP -- separate tests for "excludes", or not, below testChecksums := map[common.ChecksumAlgorithm]string{ "MD5": "624c1abb3664f4b35547e7c73864ad24", "SHA1": "85ed0817af83a24ad8da68c2b5094de69833983c", "SHA256": "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", "SHA512": "4ced3267f5ed38df65ceebc43e97aa6c2948cc7ef3288c2e5074e7df7fab544cc93339604513ea5f65616f9ed1c48581465043c8a9b693ef20fd4fddaf25e1b9", "BLAKE3": "981d32ed7aad9e408c5c36f6346c915ba11c2bd8b3e7d44902a11d7a141abdd9", } for algo, tc := range testChecksums { if err := parser.parsePairFromPackage( "PackageChecksum", fmt.Sprintf("%s: %s", algo, tc)); err != nil { t.Errorf("expected error, got %v", err) } } for _, checksum := range parser.pkg.PackageChecksums { if checksum.Value != testChecksums[checksum.Algorithm] { t.Errorf( "expected %s for PackageChecksum%s, got %s", testChecksums[checksum.Algorithm], checksum.Algorithm, checksum.Value, ) } } // Package Home Page err = parser.parsePairFromPackage("PackageHomePage", "https://example.com/whatever2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageHomePage != "https://example.com/whatever2" { t.Errorf("got %v for PackageHomePage", parser.pkg.PackageHomePage) } // Package Source Info err = parser.parsePairFromPackage("PackageSourceInfo", "random comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSourceInfo != "random comment" { t.Errorf("got %v for PackageSourceInfo", parser.pkg.PackageSourceInfo) } // Package License Concluded err = parser.parsePairFromPackage("PackageLicenseConcluded", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseConcluded != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for PackageLicenseConcluded", parser.pkg.PackageLicenseConcluded) } // All Licenses Info From Files lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromPackage("PackageLicenseInfoFromFiles", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.pkg.PackageLicenseInfoFromFiles { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in PackageLicenseInfoFromFiles", licWant) } } if len(lics) != len(parser.pkg.PackageLicenseInfoFromFiles) { t.Errorf("expected %d licenses in PackageLicenseInfoFromFiles, got %d", len(lics), len(parser.pkg.PackageLicenseInfoFromFiles)) } // Package License Declared err = parser.parsePairFromPackage("PackageLicenseDeclared", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseDeclared != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for PackageLicenseDeclared", parser.pkg.PackageLicenseDeclared) } // Package License Comments err = parser.parsePairFromPackage("PackageLicenseComments", "this is a license comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseComments != "this is a license comment" { t.Errorf("got %v for PackageLicenseComments", parser.pkg.PackageLicenseComments) } // Package Copyright Text err = parser.parsePairFromPackage("PackageCopyrightText", "Copyright (c) me myself and i") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageCopyrightText != "Copyright (c) me myself and i" { t.Errorf("got %v for PackageCopyrightText", parser.pkg.PackageCopyrightText) } // Package Summary err = parser.parsePairFromPackage("PackageSummary", "i wrote this package") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSummary != "i wrote this package" { t.Errorf("got %v for PackageSummary", parser.pkg.PackageSummary) } // Package Description err = parser.parsePairFromPackage("PackageDescription", "i wrote this package a lot") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageDescription != "i wrote this package a lot" { t.Errorf("got %v for PackageDescription", parser.pkg.PackageDescription) } // Package Comment err = parser.parsePairFromPackage("PackageComment", "i scanned this package") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageComment != "i scanned this package" { t.Errorf("got %v for PackageComment", parser.pkg.PackageComment) } // Package Attribution Text attrs := []string{ "Include this notice in all advertising materials", "This is a \nmulti-line string", } for _, attr := range attrs { err = parser.parsePairFromPackage("PackageAttributionText", attr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, attrWant := range attrs { flagFound := false for _, attrCheck := range parser.pkg.PackageAttributionTexts { if attrWant == attrCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in PackageAttributionText", attrWant) } } if len(attrs) != len(parser.pkg.PackageAttributionTexts) { t.Errorf("expected %d attribution texts in PackageAttributionTexts, got %d", len(attrs), len(parser.pkg.PackageAttributionTexts)) } // Package External References and Comments ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" ref1Category := "SECURITY" ref1Type := "cpe23Type" ref1Locator := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" ref1Comment := "this is comment #1" ref2 := "OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3alpha" ref2Category := "OTHER" ref2Type := "LocationRef-acmeforge" ref2Locator := "acmecorp/acmenator/4.1.3alpha" ref2Comment := "this is comment #2" err = parser.parsePairFromPackage("ExternalRef", ref1) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.pkg.PackageExternalReferences) != 1 { t.Errorf("expected 1 external reference, got %d", len(parser.pkg.PackageExternalReferences)) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil pkgExtRef, got nil") } if parser.pkg.PackageExternalReferences[0] == nil { t.Errorf("expected non-nil PackageExternalReferences[0], got nil") } if parser.pkgExtRef != parser.pkg.PackageExternalReferences[0] { t.Errorf("expected pkgExtRef to match PackageExternalReferences[0], got no match") } err = parser.parsePairFromPackage("ExternalRefComment", ref1Comment) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromPackage("ExternalRef", ref2) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.pkg.PackageExternalReferences) != 2 { t.Errorf("expected 2 external references, got %d", len(parser.pkg.PackageExternalReferences)) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil pkgExtRef, got nil") } if parser.pkg.PackageExternalReferences[1] == nil { t.Errorf("expected non-nil PackageExternalReferences[1], got nil") } if parser.pkgExtRef != parser.pkg.PackageExternalReferences[1] { t.Errorf("expected pkgExtRef to match PackageExternalReferences[1], got no match") } err = parser.parsePairFromPackage("ExternalRefComment", ref2Comment) if err != nil { t.Errorf("expected nil error, got %v", err) } // finally, check these values gotRef1 := parser.pkg.PackageExternalReferences[0] if gotRef1.Category != ref1Category { t.Errorf("expected ref1 category to be %s, got %s", gotRef1.Category, ref1Category) } if gotRef1.RefType != ref1Type { t.Errorf("expected ref1 type to be %s, got %s", gotRef1.RefType, ref1Type) } if gotRef1.Locator != ref1Locator { t.Errorf("expected ref1 locator to be %s, got %s", gotRef1.Locator, ref1Locator) } if gotRef1.ExternalRefComment != ref1Comment { t.Errorf("expected ref1 comment to be %s, got %s", gotRef1.ExternalRefComment, ref1Comment) } gotRef2 := parser.pkg.PackageExternalReferences[1] if gotRef2.Category != ref2Category { t.Errorf("expected ref2 category to be %s, got %s", gotRef2.Category, ref2Category) } if gotRef2.RefType != ref2Type { t.Errorf("expected ref2 type to be %s, got %s", gotRef2.RefType, ref2Type) } if gotRef2.Locator != ref2Locator { t.Errorf("expected ref2 locator to be %s, got %s", gotRef2.Locator, ref2Locator) } if gotRef2.ExternalRefComment != ref2Comment { t.Errorf("expected ref2 comment to be %s, got %s", gotRef2.ExternalRefComment, ref2Comment) } } func TestParserCanParsePackageSupplierPersonTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: Person err := parser.parsePairFromPackage("PackageSupplier", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "John Doe" { t.Errorf("got %v for PackageSupplierPerson", parser.pkg.PackageSupplier.Supplier) } } func TestParserCanParsePackageSupplierOrganizationTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: Organization err := parser.parsePairFromPackage("PackageSupplier", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "John Doe, Inc." { t.Errorf("got %v for PackageSupplierOrganization", parser.pkg.PackageSupplier.Supplier) } } func TestParserCanParsePackageSupplierNOASSERTIONTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: NOASSERTION err := parser.parsePairFromPackage("PackageSupplier", "NOASSERTION") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "NOASSERTION" { t.Errorf("got value for Supplier, expected NOASSERTION") } } func TestParserCanParsePackageOriginatorPersonTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: Person err := parser.parsePairFromPackage("PackageOriginator", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "John Doe" { t.Errorf("got %v for PackageOriginator", parser.pkg.PackageOriginator.Originator) } } func TestParserCanParsePackageOriginatorOrganizationTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: Organization err := parser.parsePairFromPackage("PackageOriginator", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "John Doe, Inc." { t.Errorf("got %v for PackageOriginator", parser.pkg.PackageOriginator.Originator) } } func TestParserCanParsePackageOriginatorNOASSERTIONTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: NOASSERTION err := parser.parsePairFromPackage("PackageOriginator", "NOASSERTION") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "NOASSERTION" { t.Errorf("got false for PackageOriginatorNOASSERTION") } } func TestParserCanParsePackageVerificationCodeTagWithExcludes(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Verification Code with excludes parenthetical code := "d6a770ba38583ed4bb4525bd96e50461655d2758" fileName := "./package.spdx" fullCodeValue := "d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx)" err := parser.parsePairFromPackage("PackageVerificationCode", fullCodeValue) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVerificationCode.Value != code { t.Errorf("got %v for PackageVerificationCode", parser.pkg.PackageVerificationCode) } if len(parser.pkg.PackageVerificationCode.ExcludedFiles) != 1 || parser.pkg.PackageVerificationCode.ExcludedFiles[0] != fileName { t.Errorf("got %v for PackageVerificationCodeExcludedFile", parser.pkg.PackageVerificationCode.ExcludedFiles) } } func TestParserCanParsePackageVerificationCodeTagWithoutExcludes(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Verification Code without excludes parenthetical code := "d6a770ba38583ed4bb4525bd96e50461655d2758" err := parser.parsePairFromPackage("PackageVerificationCode", code) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVerificationCode.Value != code { t.Errorf("got %v for PackageVerificationCode", parser.pkg.PackageVerificationCode) } if len(parser.pkg.PackageVerificationCode.ExcludedFiles) != 0 { t.Errorf("got %v for PackageVerificationCodeExcludedFile", parser.pkg.PackageVerificationCode.ExcludedFiles) } } func TestParserPackageExternalRefPointerChangesAfterTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" err := parser.parsePairFromPackage("ExternalRef", ref1) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil external reference pointer, got nil") } // now, a comment; pointer should go away err = parser.parsePairFromPackage("ExternalRefComment", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef != nil { t.Errorf("expected nil external reference pointer, got non-nil") } ref2 := "Other LocationRef-something https://example.com/whatever" err = parser.parsePairFromPackage("ExternalRef", ref2) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil external reference pointer, got nil") } // and some other random tag makes the pointer go away too err = parser.parsePairFromPackage("PackageSummary", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef != nil { t.Errorf("expected nil external reference pointer, got non-nil") } } func TestParserPackageCreatesRelationshipInDocument(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParserPackageCreatesAnnotationInDocument(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } func TestParserPackageUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePairFromPackage("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserFailsIfInvalidSPDXIDInPackageSection(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid ID format err = parser.parsePairFromPackage("SPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageSupplierFormat(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid supplier format err = parser.parsePairFromPackage("PackageSupplier", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownPackageSupplierType(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid supplier type err = parser.parsePairFromPackage("PackageSupplier", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageOriginatorFormat(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid originator format err = parser.parsePairFromPackage("PackageOriginator", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfUnknownPackageOriginatorType(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid originator type err = parser.parsePairFromPackage("PackageOriginator", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserSetsFilesAnalyzedTagsCorrectly(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // set tag err = parser.parsePairFromPackage("FilesAnalyzed", "true") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.FilesAnalyzed != true { t.Errorf("expected %v, got %v", true, parser.pkg.FilesAnalyzed) } if parser.pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("expected %v, got %v", true, parser.pkg.IsFilesAnalyzedTagPresent) } } func TestParserFailsIfInvalidPackageChecksumFormat(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid checksum format err = parser.parsePairFromPackage("PackageChecksum", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidPackageChecksumType(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid checksum type err = parser.parsePairFromPackage("PackageChecksum", "whoops: blah") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfInvalidExternalRefFormat(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid external ref format err = parser.parsePairFromPackage("ExternalRef", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfExternalRefCommentBeforeExternalRef(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{}, } // start with Package Name err := parser.parsePairFromPackage("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // external ref comment before external ref err = parser.parsePairFromPackage("ExternalRefComment", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } // ===== Helper function tests ===== func TestCanCheckAndExtractExcludesFilenameAndCode(t *testing.T) { code := "d6a770ba38583ed4bb4525bd96e50461655d2758" fileName := "./package.spdx" fullCodeValue := "d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx)" gotCode := extractCodeAndExcludes(fullCodeValue) if gotCode.Value != code { t.Errorf("got %v for gotCode", gotCode) } if len(gotCode.ExcludedFiles) != 1 || gotCode.ExcludedFiles[0] != fileName { t.Errorf("got %v for gotFileName", gotCode.ExcludedFiles) } } func TestCanExtractPackageExternalReference(t *testing.T) { ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" category := "SECURITY" refType := "cpe23Type" location := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" gotCategory, gotRefType, gotLocation, err := extractPackageExternalReference(ref1) if err != nil { t.Errorf("got non-nil error: %v", err) } if gotCategory != category { t.Errorf("expected category %s, got %s", category, gotCategory) } if gotRefType != refType { t.Errorf("expected refType %s, got %s", refType, gotRefType) } if gotLocation != location { t.Errorf("expected location %s, got %s", location, gotLocation) } } func TestCanExtractPackageExternalReferenceWithExtraWhitespace(t *testing.T) { ref1 := " SECURITY \t cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* \t " category := "SECURITY" refType := "cpe23Type" location := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" gotCategory, gotRefType, gotLocation, err := extractPackageExternalReference(ref1) if err != nil { t.Errorf("got non-nil error: %v", err) } if gotCategory != category { t.Errorf("expected category %s, got %s", category, gotCategory) } if gotRefType != refType { t.Errorf("expected refType %s, got %s", refType, gotRefType) } if gotLocation != location { t.Errorf("expected location %s, got %s", location, gotLocation) } } func TestFailsPackageExternalRefWithInvalidFormat(t *testing.T) { _, _, _, err := extractPackageExternalReference("whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserPackageWithoutSpdxIdentifierThrowsError(t *testing.T) { // More than one package, the previous package doesn't contain an SPDX ID pkgOldName := "p1" parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psPackage, pkg: &spdx.Package{PackageName: pkgOldName}, } pkgOld := parser.pkg parser.doc.Packages = append(parser.doc.Packages, pkgOld) // the Document's Packages should have this one only if parser.doc.Packages[0] != pkgOld { t.Errorf("expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } pkgName := "p2" err := parser.parsePair("PackageName", pkgName) if err == nil { t.Errorf("package without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_relationship.go000066400000000000000000000024131463371440000255020ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" ) func (parser *tvParser) parsePairForRelationship(tag string, value string) error { if parser.rln == nil { return fmt.Errorf("no relationship struct created in parser rln pointer") } if tag == "Relationship" { // parse the value to see if it's a valid relationship format sp := strings.SplitN(value, " ", -1) // filter out any purely-whitespace items var rp []string for _, v := range sp { v = strings.TrimSpace(v) if v != "" { rp = append(rp, v) } } if len(rp) != 3 { return fmt.Errorf("invalid relationship format for %s", value) } aID, err := extractDocElementID(strings.TrimSpace(rp[0])) if err != nil { return err } parser.rln.RefA = aID parser.rln.Relationship = strings.TrimSpace(rp[1]) // NONE and NOASSERTION are permitted on right side permittedSpecial := []string{"NONE", "NOASSERTION"} bID, err := extractDocElementSpecial(strings.TrimSpace(rp[2]), permittedSpecial) if err != nil { return err } parser.rln.RefB = bID return nil } if tag == "RelationshipComment" { parser.rln.RelationshipComment = value return nil } return fmt.Errorf("received unknown tag %v in Relationship section", tag) } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_relationship_test.go000066400000000000000000000132551463371440000265470ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Relationship section tests ===== func TestParserFailsIfRelationshipNotSet(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePairForRelationship("Relationship", "SPDXRef-A CONTAINS SPDXRef-B") if err == nil { t.Errorf("expected error when calling parsePairFromRelationship without setting rln pointer") } } func TestParserFailsIfRelationshipCommentWithoutRelationship(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } err := parser.parsePair("RelationshipComment", "comment whatever") if err == nil { t.Errorf("expected error when calling parsePair for RelationshipComment without Relationship first") } } func TestParserCanParseRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Relationship err := parser.parsePair("Relationship", "SPDXRef-something CONTAINS DocumentRef-otherdoc:SPDXRef-something-else") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rln.RefA.DocumentRefID != "" || parser.rln.RefA.ElementRefID != "something" { t.Errorf("got %v for first part of Relationship, expected something", parser.rln.RefA) } if parser.rln.RefB.DocumentRefID != "otherdoc" || parser.rln.RefB.ElementRefID != "something-else" { t.Errorf("got %v for second part of Relationship, expected otherdoc:something-else", parser.rln.RefB) } if parser.rln.Relationship != "CONTAINS" { t.Errorf("got %v for Relationship type, expected CONTAINS", parser.rln.Relationship) } // Relationship Comment cmt := "this is a comment" err = parser.parsePair("RelationshipComment", cmt) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rln.RelationshipComment != cmt { t.Errorf("got %v for RelationshipComment, expected %v", parser.rln.RelationshipComment, cmt) } } func TestParserInvalidRelationshipTagsNoValueFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // no items parser.rln = nil err := parser.parsePair("Relationship", "") if err == nil { t.Errorf("expected error for empty items in relationship, got nil") } } func TestParserInvalidRelationshipTagsOneValueFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // one item parser.rln = nil err := parser.parsePair("Relationship", "DESCRIBES") if err == nil { t.Errorf("expected error for only one item in relationship, got nil") } } func TestParserInvalidRelationshipTagsTwoValuesFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // two items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-DOCUMENT DESCRIBES") if err == nil { t.Errorf("expected error for only two items in relationship, got nil") } } func TestParserInvalidRelationshipTagsThreeValuesSucceed(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // three items but with interspersed additional whitespace parser.rln = nil err := parser.parsePair("Relationship", " SPDXRef-DOCUMENT \t DESCRIBES SPDXRef-something-else ") if err != nil { t.Errorf("expected pass for three items in relationship w/ extra whitespace, got: %v", err) } } func TestParserInvalidRelationshipTagsFourValuesFail(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // four items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-a DESCRIBES SPDXRef-b SPDXRef-c") if err == nil { t.Errorf("expected error for more than three items in relationship, got nil") } } func TestParserInvalidRelationshipTagsInvalidRefIDs(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // four items parser.rln = nil err := parser.parsePair("Relationship", "SPDXRef-a DESCRIBES b") if err == nil { t.Errorf("expected error for missing SPDXRef- prefix, got nil") } parser.rln = nil err = parser.parsePair("Relationship", "a DESCRIBES SPDXRef-b") if err == nil { t.Errorf("expected error for missing SPDXRef- prefix, got nil") } } func TestParserSpecialValuesValidForRightSideOfRelationship(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // NONE in right side of relationship should pass err := parser.parsePair("Relationship", "SPDXRef-a CONTAINS NONE") if err != nil { t.Errorf("expected nil error for CONTAINS NONE, got %v", err) } // NOASSERTION in right side of relationship should pass err = parser.parsePair("Relationship", "SPDXRef-a CONTAINS NOASSERTION") if err != nil { t.Errorf("expected nil error for CONTAINS NOASSERTION, got %v", err) } // NONE in left side of relationship should fail err = parser.parsePair("Relationship", "NONE CONTAINS SPDXRef-a") if err == nil { t.Errorf("expected non-nil error for NONE CONTAINS, got nil") } // NOASSERTION in left side of relationship should fail err = parser.parsePair("Relationship", "NOASSERTION CONTAINS SPDXRef-a") if err == nil { t.Errorf("expected non-nil error for NOASSERTION CONTAINS, got nil") } } func TestParserFailsToParseUnknownTagInRelationshipSection(t *testing.T) { parser := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, } // Relationship err := parser.parsePair("Relationship", "SPDXRef-something CONTAINS DocumentRef-otherdoc:SPDXRef-something-else") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid tag err = parser.parsePairForRelationship("blah", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_review.go000066400000000000000000000035611463371440000243070ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *tvParser) parsePairFromReview(tag string, value string) error { switch tag { // tag for creating new review section case "Reviewer": parser.rev = &spdx.Review{} parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) subkey, subvalue, err := extractSubs(value) if err != nil { return err } switch subkey { case "Person": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Person" case "Organization": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Organization" case "Tool": parser.rev.Reviewer = subvalue parser.rev.ReviewerType = "Tool" default: return fmt.Errorf("unrecognized Reviewer type %v", subkey) } case "ReviewDate": parser.rev.ReviewDate = value case "ReviewComment": parser.rev.ReviewComment = value // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &spdx.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &spdx.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) default: return fmt.Errorf("received unknown tag %v in Review section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_review_test.go000066400000000000000000000342371463371440000253520ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Parser review section state change tests ===== func TestParserReviewStartsNewReviewAfterParsingReviewerTag(t *testing.T) { // create the first review rev1 := "John Doe" parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{ Reviewer: rev1, ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) r1 := parser.rev // the Document's Reviews should have this one only if len(parser.doc.Reviews) != 1 { t.Errorf("Expected only one review, got %d", len(parser.doc.Reviews)) } if parser.doc.Reviews[0] != r1 { t.Errorf("Expected review %v in Reviews[0], got %v", r1, parser.doc.Reviews[0]) } if parser.doc.Reviews[0].Reviewer != rev1 { t.Errorf("expected review name %s in Reviews[0], got %s", rev1, parser.doc.Reviews[0].Reviewer) } // now add a new review rev2 := "Steve" rp2 := "Person: Steve" err := parser.parsePair("Reviewer", rp2) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } // and a review should be created if parser.rev == nil { t.Fatalf("parser didn't create new review") } // and the reviewer's name should be as expected if parser.rev.Reviewer != rev2 { t.Errorf("expected reviewer name %s, got %s", rev2, parser.rev.Reviewer) } // and the Document's reviews should be of size 2 and have these two if len(parser.doc.Reviews) != 2 { t.Fatalf("Expected Reviews to have len 2, got %d", len(parser.doc.Reviews)) } if parser.doc.Reviews[0] != r1 { t.Errorf("Expected review %v in Reviews[0], got %v", r1, parser.doc.Reviews[0]) } if parser.doc.Reviews[0].Reviewer != rev1 { t.Errorf("expected reviewer name %s in Reviews[0], got %s", rev1, parser.doc.Reviews[0].Reviewer) } if parser.doc.Reviews[1] != parser.rev { t.Errorf("Expected review %v in Reviews[1], got %v", parser.rev, parser.doc.Reviews[1]) } if parser.doc.Reviews[1].Reviewer != rev2 { t.Errorf("expected reviewer name %s in Reviews[1], got %s", rev2, parser.doc.Reviews[1].Reviewer) } } func TestParserReviewStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{ Reviewer: "Jane Doe", ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserReviewStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{ Reviewer: "Jane Doe", ReviewerType: "Person", }, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("parser is in state %v, expected %v", parser.st, psReview) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } func TestParserReviewFailsAfterParsingOtherSectionTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // can't go back to old sections err := parser.parsePair("SPDXVersion", spdx.Version) if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("PackageName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("FileName", "whatever") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } err = parser.parsePair("LicenseID", "LicenseRef-Lic22") if err == nil { t.Errorf("expected error when calling parsePair, got nil") } } // ===== Review data section tests ===== func TestParserCanParseReviewTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer (DEPRECATED) // handled in subsequent subtests // Review Date (DEPRECATED) err := parser.parsePairFromReview("ReviewDate", "2018-09-23T08:30:00Z") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.ReviewDate != "2018-09-23T08:30:00Z" { t.Errorf("got %v for ReviewDate", parser.rev.ReviewDate) } // Review Comment (DEPRECATED) err = parser.parsePairFromReview("ReviewComment", "this is a comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.ReviewComment != "this is a comment" { t.Errorf("got %v for ReviewComment", parser.rev.ReviewComment) } } func TestParserCanParseReviewerPersonTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Person err := parser.parsePairFromReview("Reviewer", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "John Doe" { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Person" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserCanParseReviewerOrganizationTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Organization err := parser.parsePairFromReview("Reviewer", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "John Doe, Inc." { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Organization" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserCanParseReviewerToolTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) // Reviewer: Tool err := parser.parsePairFromReview("Reviewer", "Tool: scannertool - 1.2.12") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.rev.Reviewer != "scannertool - 1.2.12" { t.Errorf("got %v for Reviewer", parser.rev.Reviewer) } if parser.rev.ReviewerType != "Tool" { t.Errorf("got %v for ReviewerType", parser.rev.ReviewerType) } } func TestParserFailsIfReviewerInvalidFormat(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, rev: &spdx.Review{}, } parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("Reviewer", "oops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsIfReviewerUnknownType(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, rev: &spdx.Review{}, } parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("Reviewer", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserReviewUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psReview, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1"}, otherLic: &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-Lic11", LicenseName: "License 11", }, rev: &spdx.Review{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, parser.otherLic) parser.doc.Reviews = append(parser.doc.Reviews, parser.rev) err := parser.parsePairFromReview("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_snippet.go000066400000000000000000000110231463371440000244600ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strconv" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func (parser *tvParser) parsePairFromSnippet(tag string, value string) error { switch tag { // tag for creating new snippet section case "SnippetSPDXID": // check here whether the file contained an SPDX ID or not if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } parser.snippet = &spdx.Snippet{} eID, err := extractElementID(value) if err != nil { return err } // FIXME: how should we handle where not associated with current file? if parser.file != nil { if parser.file.Snippets == nil { parser.file.Snippets = map[common.ElementID]*spdx.Snippet{} } parser.file.Snippets[eID] = parser.snippet } parser.snippet.SnippetSPDXIdentifier = eID // tag for creating new file section and going back to parsing File case "FileName": parser.st = psFile parser.snippet = nil return parser.parsePairFromFile(tag, value) // tag for creating new package section and going back to parsing Package case "PackageName": parser.st = psPackage parser.file = nil parser.snippet = nil return parser.parsePairFromPackage(tag, value) // tag for going on to other license section case "LicenseID": parser.st = psOtherLicense return parser.parsePairFromOtherLicense(tag, value) // tags for snippet data case "SnippetFromFileSPDXID": deID, err := extractDocElementID(value) if err != nil { return err } parser.snippet.SnippetFromFileSPDXIdentifier = deID.ElementRefID case "SnippetByteRange": byteStart, byteEnd, err := extractSubs(value) if err != nil { return err } bIntStart, err := strconv.Atoi(byteStart) if err != nil { return err } bIntEnd, err := strconv.Atoi(byteEnd) if err != nil { return err } if parser.snippet.Ranges == nil { parser.snippet.Ranges = []common.SnippetRange{} } byteRange := common.SnippetRange{StartPointer: common.SnippetRangePointer{Offset: bIntStart}, EndPointer: common.SnippetRangePointer{Offset: bIntEnd}} parser.snippet.Ranges = append(parser.snippet.Ranges, byteRange) case "SnippetLineRange": lineStart, lineEnd, err := extractSubs(value) if err != nil { return err } lInttStart, err := strconv.Atoi(lineStart) if err != nil { return err } lInttEnd, err := strconv.Atoi(lineEnd) if err != nil { return err } if parser.snippet.Ranges == nil { parser.snippet.Ranges = []common.SnippetRange{} } lineRange := common.SnippetRange{StartPointer: common.SnippetRangePointer{LineNumber: lInttStart}, EndPointer: common.SnippetRangePointer{LineNumber: lInttEnd}} parser.snippet.Ranges = append(parser.snippet.Ranges, lineRange) case "SnippetLicenseConcluded": parser.snippet.SnippetLicenseConcluded = value case "LicenseInfoInSnippet": parser.snippet.LicenseInfoInSnippet = append(parser.snippet.LicenseInfoInSnippet, value) case "SnippetLicenseComments": parser.snippet.SnippetLicenseComments = value case "SnippetCopyrightText": parser.snippet.SnippetCopyrightText = value case "SnippetComment": parser.snippet.SnippetComment = value case "SnippetName": parser.snippet.SnippetName = value case "SnippetAttributionText": parser.snippet.SnippetAttributionTexts = append(parser.snippet.SnippetAttributionTexts, value) // for relationship tags, pass along but don't change state case "Relationship": parser.rln = &spdx.Relationship{} parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) return parser.parsePairForRelationship(tag, value) case "RelationshipComment": return parser.parsePairForRelationship(tag, value) // for annotation tags, pass along but don't change state case "Annotator": parser.ann = &spdx.Annotation{} parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) return parser.parsePairForAnnotation(tag, value) case "AnnotationDate": return parser.parsePairForAnnotation(tag, value) case "AnnotationType": return parser.parsePairForAnnotation(tag, value) case "SPDXREF": return parser.parsePairForAnnotation(tag, value) case "AnnotationComment": return parser.parsePairForAnnotation(tag, value) // tag for going on to review section (DEPRECATED) case "Reviewer": parser.st = psReview return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("received unknown tag %v in Snippet section", tag) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parse_snippet_test.go000066400000000000000000000567611463371440000255410ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Parser snippet section state change tests ===== func TestParserSnippetStartsNewSnippetAfterParsingSnippetSPDXIDTag(t *testing.T) { // create the first snippet sid1 := common.ElementID("s1") parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "test", PackageSPDXIdentifier: "test", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{SnippetSPDXIdentifier: sid1}, } s1 := parser.snippet parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets[sid1] = parser.snippet // the File's Snippets should have this one only if len(parser.file.Snippets) != 1 { t.Errorf("Expected len(Snippets) to be 1, got %d", len(parser.file.Snippets)) } if parser.file.Snippets["s1"] != s1 { t.Errorf("Expected snippet %v in Snippets[s1], got %v", s1, parser.file.Snippets["s1"]) } if parser.file.Snippets["s1"].SnippetSPDXIdentifier != sid1 { t.Errorf("expected snippet ID %s in Snippets[s1], got %s", sid1, parser.file.Snippets["s1"].SnippetSPDXIdentifier) } // now add a new snippet err := parser.parsePair("SnippetSPDXID", "SPDXRef-s2") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and a snippet should be created if parser.snippet == nil { t.Fatalf("parser didn't create new snippet") } // and the snippet ID should be as expected if parser.snippet.SnippetSPDXIdentifier != "s2" { t.Errorf("expected snippet ID %s, got %s", "s2", parser.snippet.SnippetSPDXIdentifier) } // and the File's Snippets should be of size 2 and have these two if len(parser.file.Snippets) != 2 { t.Errorf("Expected len(Snippets) to be 2, got %d", len(parser.file.Snippets)) } if parser.file.Snippets["s1"] != s1 { t.Errorf("Expected snippet %v in Snippets[s1], got %v", s1, parser.file.Snippets["s1"]) } if parser.file.Snippets["s1"].SnippetSPDXIdentifier != sid1 { t.Errorf("expected snippet ID %s in Snippets[s1], got %s", sid1, parser.file.Snippets["s1"].SnippetSPDXIdentifier) } if parser.file.Snippets["s2"] != parser.snippet { t.Errorf("Expected snippet %v in Snippets[s2], got %v", parser.snippet, parser.file.Snippets["s2"]) } if parser.file.Snippets["s2"].SnippetSPDXIdentifier != "s2" { t.Errorf("expected snippet ID %s in Snippets[s2], got %s", "s2", parser.file.Snippets["s2"].SnippetSPDXIdentifier) } } func TestParserSnippetStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{SnippetSPDXIdentifier: "s1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet // now add a new package p2Name := "package2" err := parser.parsePair("PackageName", p2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should go back to Package if parser.st != psPackage { t.Errorf("expected state to be %v, got %v", psPackage, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new pkg") } // and the package name should be as expected if parser.pkg.PackageName != p2Name { t.Errorf("expected package name %s, got %s", p2Name, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should still be of size 1 b/c no SPDX // identifier has been seen yet if len(parser.doc.Packages) != 1 { t.Errorf("Expected len(Packages) to be 1, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != p1 { t.Errorf("Expected package %v in Packages[package1], got %v", p1, parser.doc.Packages[0]) } if parser.doc.Packages[0].PackageName != "package1" { t.Errorf("expected package name %s in Packages[package1], got %s", "package1", parser.doc.Packages[0].PackageName) } // and the first Package's Files should be of size 1 and have f1 only if len(parser.doc.Packages[0].Files) != 1 { t.Errorf("Expected 1 file in Packages[package1].Files, got %d", len(parser.doc.Packages[0].Files)) } if parser.doc.Packages[0].Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.doc.Packages[0].Files[0]) } if parser.doc.Packages[0].Files[0].FileName != "f1.txt" { t.Errorf("expected file name %s in Files[f1], got %s", "f1.txt", parser.doc.Packages[0].Files[0].FileName) } // and the new Package should have no files if len(parser.pkg.Files) != 0 { t.Errorf("Expected no files in Packages[1].Files, got %d", len(parser.pkg.Files)) } // and the current file should be nil if parser.file != nil { t.Errorf("Expected nil for parser.file, got %v", parser.file) } // and the current snippet should be nil if parser.snippet != nil { t.Errorf("Expected nil for parser.snippet, got %v", parser.snippet) } } func TestParserSnippetMovesToFileAfterParsingFileNameTag(t *testing.T) { f1Name := "f1.txt" parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{SnippetSPDXIdentifier: "s1"}, } p1 := parser.pkg f1 := parser.file parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet f2Name := "f2.txt" err := parser.parsePair("FileName", f2Name) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should be correct if parser.st != psFile { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and current package should remain what it was if parser.pkg != p1 { t.Fatalf("expected package to remain %v, got %v", p1, parser.pkg) } // and a file should be created if parser.file == nil { t.Fatalf("parser didn't create new file") } // and the file name should be as expected if parser.file.FileName != f2Name { t.Errorf("expected file name %s, got %s", f2Name, parser.file.FileName) } // and the Package's Files should still be of size 1 since we haven't seen // an SPDX identifier yet for this new file if len(parser.pkg.Files) != 1 { t.Errorf("Expected len(Files) to be 1, got %d", len(parser.pkg.Files)) } if parser.pkg.Files[0] != f1 { t.Errorf("Expected file %v in Files[f1], got %v", f1, parser.pkg.Files[0]) } if parser.pkg.Files[0].FileName != f1Name { t.Errorf("expected file name %s in Files[f1], got %s", f1Name, parser.pkg.Files[0].FileName) } // and the current snippet should be nil if parser.snippet != nil { t.Errorf("Expected nil for parser.snippet, got %v", parser.snippet) } } func TestParserSnippetMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psOtherLicense { t.Errorf("expected state to be %v, got %v", psOtherLicense, parser.st) } } func TestParserSnippetMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psReview { t.Errorf("expected state to be %v, got %v", psReview, parser.st) } } func TestParserSnippetStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should remain unchanged if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } // and the relationship should be in the Document's Relationships if len(parser.doc.Relationships) != 1 { t.Fatalf("expected doc.Relationships to have len 1, got %d", len(parser.doc.Relationships)) } deID := parser.doc.Relationships[0].RefA if deID.DocumentRefID != "" || deID.ElementRefID != "blah" { t.Errorf("expected RefA to be %s, got %s", "blah", parser.doc.Relationships[0].RefA) } err = parser.parsePair("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } // state should still remain unchanged if parser.st != psSnippet { t.Errorf("expected state to be %v, got %v", psSnippet, parser.st) } } func TestParserSnippetStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) parser.file.Snippets["s1"] = parser.snippet err := parser.parsePair("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } err = parser.parsePair("AnnotationComment", "i guess i had something to say about this particular file") if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.st != psSnippet { t.Errorf("parser is in state %v, expected %v", parser.st, psSnippet) } // and the annotation should be in the Document's Annotations if len(parser.doc.Annotations) != 1 { t.Fatalf("expected doc.Annotations to have len 1, got %d", len(parser.doc.Annotations)) } if parser.doc.Annotations[0].Annotator.Annotator != "John Doe ()" { t.Errorf("expected Annotator to be %s, got %s", "John Doe ()", parser.doc.Annotations[0].Annotator) } } // ===== Snippet data section tests ===== func TestParserCanParseSnippetTags(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetSPDXIdentifier != "s1" { t.Errorf("got %v for SnippetSPDXIdentifier", parser.snippet.SnippetSPDXIdentifier) } // Snippet from File SPDX Identifier err = parser.parsePairFromSnippet("SnippetFromFileSPDXID", "SPDXRef-f1") if err != nil { t.Errorf("expected nil error, got %v", err) } wantDeID := common.DocElementID{DocumentRefID: "", ElementRefID: common.ElementID("f1")} if parser.snippet.SnippetFromFileSPDXIdentifier != wantDeID.ElementRefID { t.Errorf("got %v for SnippetFromFileSPDXIdentifier", parser.snippet.SnippetFromFileSPDXIdentifier) } // Snippet Byte Range err = parser.parsePairFromSnippet("SnippetByteRange", "20:320") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.Ranges[0].StartPointer.Offset != 20 { t.Errorf("got %v for SnippetByteRangeStart", parser.snippet.Ranges[0].StartPointer.Offset) } if parser.snippet.Ranges[0].EndPointer.Offset != 320 { t.Errorf("got %v for SnippetByteRangeEnd", parser.snippet.Ranges[0].EndPointer.Offset) } // Snippet Line Range err = parser.parsePairFromSnippet("SnippetLineRange", "5:12") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.Ranges[1].StartPointer.LineNumber != 5 { t.Errorf("got %v for SnippetLineRangeStart", parser.snippet.Ranges[1].StartPointer.LineNumber) } if parser.snippet.Ranges[1].EndPointer.LineNumber != 12 { t.Errorf("got %v for SnippetLineRangeEnd", parser.snippet.Ranges[1].EndPointer.LineNumber) } // Snippet Concluded License err = parser.parsePairFromSnippet("SnippetLicenseConcluded", "BSD-3-Clause") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetLicenseConcluded != "BSD-3-Clause" { t.Errorf("got %v for SnippetLicenseConcluded", parser.snippet.SnippetLicenseConcluded) } // License Information in Snippet lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromSnippet("LicenseInfoInSnippet", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.snippet.LicenseInfoInSnippet { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in LicenseInfoInSnippet", licWant) } } if len(lics) != len(parser.snippet.LicenseInfoInSnippet) { t.Errorf("expected %d licenses in LicenseInfoInSnippet, got %d", len(lics), len(parser.snippet.LicenseInfoInSnippet)) } // Snippet Comments on License err = parser.parsePairFromSnippet("SnippetLicenseComments", "this is a comment about the licenses") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetLicenseComments != "this is a comment about the licenses" { t.Errorf("got %v for SnippetLicenseComments", parser.snippet.SnippetLicenseComments) } // Snippet Copyright Text err = parser.parsePairFromSnippet("SnippetCopyrightText", "copyright (c) John Doe and friends") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetCopyrightText != "copyright (c) John Doe and friends" { t.Errorf("got %v for SnippetCopyrightText", parser.snippet.SnippetCopyrightText) } // Snippet Comment err = parser.parsePairFromSnippet("SnippetComment", "this is a comment about the snippet") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetComment != "this is a comment about the snippet" { t.Errorf("got %v for SnippetComment", parser.snippet.SnippetComment) } // Snippet Name err = parser.parsePairFromSnippet("SnippetName", "from some other package called abc") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.snippet.SnippetName != "from some other package called abc" { t.Errorf("got %v for SnippetName", parser.snippet.SnippetName) } // Snippet Attribution Texts attrs := []string{ "Include this notice in all advertising materials", "This is a \nmulti-line string", } for _, attr := range attrs { err = parser.parsePairFromSnippet("SnippetAttributionText", attr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, attrWant := range attrs { flagFound := false for _, attrCheck := range parser.snippet.SnippetAttributionTexts { if attrWant == attrCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in SnippetAttributionText", attrWant) } } if len(attrs) != len(parser.snippet.SnippetAttributionTexts) { t.Errorf("expected %d attribution texts in SnippetAttributionTexts, got %d", len(attrs), len(parser.snippet.SnippetAttributionTexts)) } } func TestParserSnippetUnknownTagFails(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{SnippetSPDXIdentifier: "s1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) err := parser.parsePairFromSnippet("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParserFailsForInvalidSnippetSPDXID(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // invalid Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetFromFileSPDXID(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid From File identifier err = parser.parsePairFromSnippet("SnippetFromFileSPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetByteValues(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid byte formats and values err = parser.parsePairFromSnippet("SnippetByteRange", "200 210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetByteRange", "a:210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetByteRange", "200:a") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFailsForInvalidSnippetLineValues(t *testing.T) { parser := tvParser{ doc: &spdx.Document{Packages: []*spdx.Package{}}, st: psSnippet, pkg: &spdx.Package{PackageName: "package1", PackageSPDXIdentifier: "package1", Files: []*spdx.File{}}, file: &spdx.File{FileName: "f1.txt", FileSPDXIdentifier: "f1", Snippets: map[common.ElementID]*spdx.Snippet{}}, snippet: &spdx.Snippet{}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) parser.pkg.Files = append(parser.pkg.Files, parser.file) // start with Snippet SPDX Identifier err := parser.parsePairFromSnippet("SnippetSPDXID", "SPDXRef-s1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid byte formats and values err = parser.parsePairFromSnippet("SnippetLineRange", "200 210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetLineRange", "a:210") if err == nil { t.Errorf("expected non-nil error, got nil") } err = parser.parsePairFromSnippet("SnippetLineRange", "200:a") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowErrorWithSnippets(t *testing.T) { // Invalid file with snippet // Last unpackaged file before the snippet starts // Last file of a package and New package starts fileName := "f2.txt" sid1 := common.ElementID("s1") parser2 := tvParser{ doc: &spdx.Document{}, st: psCreationInfo, file: &spdx.File{FileName: fileName}, } err := parser2.parsePair("SnippetSPDXID", string(sid1)) if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parser.go000066400000000000000000000061131463371440000231040ustar00rootroot00000000000000// Package reader contains functions to read, load and parse SPDX tag-value files. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" "github.com/spdx/tools-golang/tagvalue/reader" ) // ParseTagValues takes a list of (tag, value) pairs, parses it and returns // a pointer to a parsed SPDX Document. func ParseTagValues(tvs []reader.TagValuePair) (*spdx.Document, error) { parser := tvParser{} for _, tv := range tvs { err := parser.parsePair(tv.Tag, tv.Value) if err != nil { return nil, err } } if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId { return nil, fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName) } if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId { return nil, fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName) } return parser.doc, nil } func (parser *tvParser) parsePair(tag string, value string) error { switch parser.st { case psStart: return parser.parsePairFromStart(tag, value) case psCreationInfo: return parser.parsePairFromCreationInfo(tag, value) case psPackage: return parser.parsePairFromPackage(tag, value) case psFile: return parser.parsePairFromFile(tag, value) case psSnippet: return parser.parsePairFromSnippet(tag, value) case psOtherLicense: return parser.parsePairFromOtherLicense(tag, value) case psReview: return parser.parsePairFromReview(tag, value) default: return fmt.Errorf("parser state %v not recognized when parsing (%s, %s)", parser.st, tag, value) } } func (parser *tvParser) parsePairFromStart(tag string, value string) error { // fail if not in Start parser state if parser.st != psStart { return fmt.Errorf("got invalid state %v in parsePairFromStart", parser.st) } // create an SPDX Document data struct if we don't have one already if parser.doc == nil { parser.doc = &spdx.Document{ExternalDocumentReferences: []spdx.ExternalDocumentRef{}} } switch tag { case "DocumentComment": parser.doc.DocumentComment = value case "SPDXVersion": parser.doc.SPDXVersion = value case "DataLicense": parser.doc.DataLicense = value case "SPDXID": eID, err := extractElementID(value) if err != nil { return err } parser.doc.SPDXIdentifier = eID case "DocumentName": parser.doc.DocumentName = value case "DocumentNamespace": parser.doc.DocumentNamespace = value case "ExternalDocumentRef": documentRefID, uri, alg, checksum, err := extractExternalDocumentReference(value) if err != nil { return err } edr := spdx.ExternalDocumentRef{ DocumentRefID: documentRefID, URI: uri, Checksum: common.Checksum{Algorithm: common.ChecksumAlgorithm(alg), Value: checksum}, } parser.doc.ExternalDocumentReferences = append(parser.doc.ExternalDocumentReferences, edr) default: // move to Creation Info parser state parser.st = psCreationInfo return parser.parsePairFromCreationInfo(tag, value) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/parser_test.go000066400000000000000000000056641463371440000241550ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" "github.com/spdx/tools-golang/tagvalue/reader" ) // ===== Parser exported entry point tests ===== func TestParserCanParseTagValues(t *testing.T) { var tvPairs []reader.TagValuePair // create some pairs tvPair1 := reader.TagValuePair{Tag: "SPDXVersion", Value: spdx.Version} tvPairs = append(tvPairs, tvPair1) tvPair2 := reader.TagValuePair{Tag: "DataLicense", Value: spdx.DataLicense} tvPairs = append(tvPairs, tvPair2) tvPair3 := reader.TagValuePair{Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"} tvPairs = append(tvPairs, tvPair3) // now parse them doc, err := ParseTagValues(tvPairs) if err != nil { t.Errorf("got error when calling ParseTagValues: %v", err) } if doc.SPDXVersion != spdx.Version { t.Errorf("expected SPDXVersion to be %s, got %v", spdx.Version, doc.SPDXVersion) } if doc.DataLicense != spdx.DataLicense { t.Errorf("expected DataLicense to be %s, got %v", spdx.DataLicense, doc.DataLicense) } if doc.SPDXIdentifier != "DOCUMENT" { t.Errorf("expected SPDXIdentifier to be DOCUMENT, got %v", doc.SPDXIdentifier) } } // ===== Parser initialization tests ===== func TestParserInitCreatesResetStatus(t *testing.T) { parser := tvParser{} if parser.st != psStart { t.Errorf("parser did not begin in start state") } if parser.doc != nil { t.Errorf("parser did not begin with nil document") } } func TestParserHasDocumentAfterCallToParseFirstTag(t *testing.T) { parser := tvParser{} err := parser.parsePair("SPDXVersion", spdx.Version) if err != nil { t.Errorf("got error when calling parsePair: %v", err) } if parser.doc == nil { t.Errorf("doc is still nil after parsing first pair") } } func TestParserStartFailsToParseIfInInvalidState(t *testing.T) { parser := tvParser{st: psReview} err := parser.parsePairFromStart("SPDXVersion", spdx.Version) if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParserFilesWithoutSpdxIdThrowErrorAtCompleteParse(t *testing.T) { // case: Checks the last file // Last unpackaged file with no packages in doc // Last file of last package in the doc tvPairs := []reader.TagValuePair{ {Tag: "SPDXVersion", Value: spdx.Version}, {Tag: "DataLicense", Value: spdx.DataLicense}, {Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"}, {Tag: "FileName", Value: "f1"}, } _, err := ParseTagValues(tvPairs) if err == nil { t.Errorf("file without SPDX Identifier getting accepted") } } func TestParserPackageWithoutSpdxIdThrowErrorAtCompleteParse(t *testing.T) { // case: Checks the last package tvPairs := []reader.TagValuePair{ {Tag: "SPDXVersion", Value: spdx.Version}, {Tag: "DataLicense", Value: spdx.DataLicense}, {Tag: "SPDXID", Value: "SPDXRef-DOCUMENT"}, {Tag: "PackageName", Value: "p1"}, } _, err := ParseTagValues(tvPairs) if err == nil { t.Errorf("package without SPDX Identifier getting accepted") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/types.go000066400000000000000000000022701463371440000227540ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) type tvParser struct { // document into which data is being parsed doc *spdx.Document // current parser state st tvParserState // current SPDX item being filled in, if any pkg *spdx.Package pkgExtRef *spdx.PackageExternalReference file *spdx.File fileAOP *spdx.ArtifactOfProject snippet *spdx.Snippet otherLic *spdx.OtherLicense rln *spdx.Relationship ann *spdx.Annotation rev *spdx.Review // don't need creation info pointer b/c only one, // and we can get to it via doc.CreationInfo } // parser state (SPDX document) type tvParserState int const ( // at beginning of document psStart tvParserState = iota // in document creation info section psCreationInfo // in package data section psPackage // in file data section (including "unpackaged" files) psFile // in snippet data section (including "unpackaged" files) psSnippet // in other license section psOtherLicense // in review section psReview ) const nullSpdxElementId = common.ElementID("") tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/util.go000066400000000000000000000077701463371440000225770ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "fmt" "strings" "github.com/spdx/tools-golang/spdx/v2/common" ) // used to extract key / value from embedded substrings // returns subkey, subvalue, nil if no error, or "", "", error otherwise func extractSubs(value string) (string, string, error) { // parse the value to see if it's a valid subvalue format sp := strings.SplitN(value, ":", 2) if len(sp) == 1 { return "", "", fmt.Errorf("invalid subvalue format for %s (no colon found)", value) } subkey := strings.TrimSpace(sp[0]) subvalue := strings.TrimSpace(sp[1]) return subkey, subvalue, nil } // used to extract DocumentRef and SPDXRef values from an SPDX Identifier // which can point either to this document or to a different one func extractDocElementID(value string) (common.DocElementID, error) { docRefID := "" idStr := value // check prefix to see if it's a DocumentRef ID if strings.HasPrefix(idStr, "DocumentRef-") { // extract the part that comes between "DocumentRef-" and ":" strs := strings.Split(idStr, ":") // should be exactly two, part before and part after if len(strs) < 2 { return common.DocElementID{}, fmt.Errorf("no colon found although DocumentRef- prefix present") } if len(strs) > 2 { return common.DocElementID{}, fmt.Errorf("more than one colon found") } // trim the prefix and confirm non-empty docRefID = strings.TrimPrefix(strs[0], "DocumentRef-") if docRefID == "" { return common.DocElementID{}, fmt.Errorf("document identifier has nothing after prefix") } // and use remainder for element ID parsing idStr = strs[1] } // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(idStr, "SPDXRef-") { return common.DocElementID{}, fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(idStr, ":") { // we know this means there was no DocumentRef- prefix, because // we would have handled multiple colons above if it was return common.DocElementID{}, fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(idStr, "SPDXRef-") if eltRefID == "" { return common.DocElementID{}, fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.DocElementID{DocumentRefID: docRefID, ElementRefID: common.ElementID(eltRefID)}, nil } // used to extract SPDXRef values from an SPDX Identifier, OR "special" strings // from a specified set of permitted values. The primary use case for this is // the right-hand side of Relationships, // "NONE" and "NOASSERTION" are permitted. If the value does not match one of // the specified permitted values, it will fall back to the ordinary // DocElementID extractor. func extractDocElementSpecial(value string, permittedSpecial []string) (common.DocElementID, error) { // check value against special set first for _, sp := range permittedSpecial { if sp == value { return common.DocElementID{SpecialID: sp}, nil } } // not found, fall back to regular search return extractDocElementID(value) } // used to extract SPDXRef values only from an SPDX Identifier which can point // to this document only. Use extractDocElementID for parsing IDs that can // refer either to this document or a different one. func extractElementID(value string) (common.ElementID, error) { // check prefix to confirm it's got the right prefix for element IDs if !strings.HasPrefix(value, "SPDXRef-") { return common.ElementID(""), fmt.Errorf("missing SPDXRef- prefix for element identifier") } // make sure no colons are present if strings.Contains(value, ":") { return common.ElementID(""), fmt.Errorf("invalid colon in element identifier") } // trim the prefix and confirm non-empty eltRefID := strings.TrimPrefix(value, "SPDXRef-") if eltRefID == "" { return common.ElementID(""), fmt.Errorf("element identifier has nothing after prefix") } // we're good return common.ElementID(eltRefID), nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/reader/util_test.go000066400000000000000000000145021463371440000236250ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "testing" "github.com/spdx/tools-golang/spdx/v2/common" ) // ===== Helper function tests ===== func TestCanExtractSubvalues(t *testing.T) { subkey, subvalue, err := extractSubs("SHA1: abc123") if err != nil { t.Errorf("got error when calling extractSubs: %v", err) } if subkey != "SHA1" { t.Errorf("got %v for subkey", subkey) } if subvalue != "abc123" { t.Errorf("got %v for subvalue", subvalue) } } func TestReturnsErrorForInvalidSubvalueFormat(t *testing.T) { _, _, err := extractSubs("blah") if err == nil { t.Errorf("expected error when calling extractSubs for invalid format (0 colons), got nil") } } func TestCanExtractDocumentAndElementRefsFromID(t *testing.T) { // test with valid ID in this document helperForExtractDocElementID(t, "SPDXRef-file1", false, "", "file1") // test with valid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file2", false, "doc2", "file2") // test with invalid ID in this document helperForExtractDocElementID(t, "a:SPDXRef-file1", true, "", "") helperForExtractDocElementID(t, "file1", true, "", "") helperForExtractDocElementID(t, "SPDXRef-", true, "", "") helperForExtractDocElementID(t, "SPDXRef-file1:", true, "", "") // test with invalid ID in another document helperForExtractDocElementID(t, "DocumentRef-doc2", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-", true, "", "") helperForExtractDocElementID(t, "DocumentRef-doc2:a", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:", true, "", "") helperForExtractDocElementID(t, "DocumentRef-:SPDXRef-file1", true, "", "") // test with invalid formats helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file1:file2", true, "", "") } func helperForExtractDocElementID(t *testing.T, tst string, wantErr bool, wantDoc string, wantElt string) { deID, err := extractDocElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if deID.DocumentRefID != wantDoc { if wantDoc == "" { t.Errorf("testing %v: want empty string for DocumentRefID, got %v", tst, deID.DocumentRefID) } else { t.Errorf("testing %v: want %v for DocumentRefID, got %v", tst, wantDoc, deID.DocumentRefID) } } if deID.ElementRefID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want empty string for ElementRefID, got %v", tst, deID.ElementRefID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, deID.ElementRefID) } } } func TestCanExtractSpecialDocumentIDs(t *testing.T) { permittedSpecial := []string{"NONE", "NOASSERTION"} // test with valid special values helperForExtractDocElementSpecial(t, permittedSpecial, "NONE", false, "", "", "NONE") helperForExtractDocElementSpecial(t, permittedSpecial, "NOASSERTION", false, "", "", "NOASSERTION") // test with valid regular IDs helperForExtractDocElementSpecial(t, permittedSpecial, "SPDXRef-file1", false, "", "file1", "") helperForExtractDocElementSpecial(t, permittedSpecial, "DocumentRef-doc2:SPDXRef-file2", false, "doc2", "file2", "") helperForExtractDocElementSpecial(t, permittedSpecial, "a:SPDXRef-file1", true, "", "", "") helperForExtractDocElementSpecial(t, permittedSpecial, "DocumentRef-doc2", true, "", "", "") // test with invalid other words not on permitted list helperForExtractDocElementSpecial(t, permittedSpecial, "FOO", true, "", "", "") } func helperForExtractDocElementSpecial(t *testing.T, permittedSpecial []string, tst string, wantErr bool, wantDoc string, wantElt string, wantSpecial string) { deID, err := extractDocElementSpecial(tst, permittedSpecial) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if deID.DocumentRefID != wantDoc { if wantDoc == "" { t.Errorf("testing %v: want empty string for DocumentRefID, got %v", tst, deID.DocumentRefID) } else { t.Errorf("testing %v: want %v for DocumentRefID, got %v", tst, wantDoc, deID.DocumentRefID) } } if deID.ElementRefID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want empty string for ElementRefID, got %v", tst, deID.ElementRefID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, deID.ElementRefID) } } if deID.SpecialID != wantSpecial { if wantSpecial == "" { t.Errorf("testing %v: want empty string for SpecialID, got %v", tst, deID.SpecialID) } else { t.Errorf("testing %v: want %v for SpecialID, got %v", tst, wantSpecial, deID.SpecialID) } } } func TestCanExtractElementRefsOnlyFromID(t *testing.T) { // test with valid ID in this document helperForExtractElementID(t, "SPDXRef-file1", false, "file1") // test with valid ID in another document helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-file2", true, "") // test with invalid ID in this document helperForExtractElementID(t, "a:SPDXRef-file1", true, "") helperForExtractElementID(t, "file1", true, "") helperForExtractElementID(t, "SPDXRef-", true, "") helperForExtractElementID(t, "SPDXRef-file1:", true, "") // test with invalid ID in another document helperForExtractElementID(t, "DocumentRef-doc2", true, "") helperForExtractElementID(t, "DocumentRef-doc2:", true, "") helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-", true, "") helperForExtractElementID(t, "DocumentRef-doc2:a", true, "") helperForExtractElementID(t, "DocumentRef-:", true, "") helperForExtractElementID(t, "DocumentRef-:SPDXRef-file1", true, "") } func helperForExtractElementID(t *testing.T, tst string, wantErr bool, wantElt string) { eID, err := extractElementID(tst) if err != nil && wantErr == false { t.Errorf("testing %v: expected nil error, got %v", tst, err) } if err == nil && wantErr == true { t.Errorf("testing %v: expected non-nil error, got nil", tst) } if eID != common.ElementID(wantElt) { if wantElt == "" { t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, eID) } else { t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, eID) } } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/tagvalue_test.go000066400000000000000000000046231463371440000232210ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package tagvalue import ( "bytes" "flag" "fmt" "os" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" "github.com/spdx/tools-golang/spdx/v2/v2_3/example" "github.com/spdx/tools-golang/tagvalue" ) var update = *flag.Bool("update-snapshots", false, "update the example snapshot") func Test_Read(t *testing.T) { fileName := "../../../../examples/sample-docs/tv/SPDXTagExample-v2.3.spdx" want := example.Copy() if update { f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { t.Errorf("unable to open file to write: %v", err) } err = tagvalue.Write(want, f) if err != nil { t.Errorf("unable to call tagvalue.Write: %v", err) } } else { // tagvalue.Write sorts a few items in the in-memory SPDX document, run this // so the same sorting happens: err := tagvalue.Write(want, &bytes.Buffer{}) if err != nil { t.Errorf("unable to call tagvalue.Write: %v", err) } } file, err := os.Open(fileName) if err != nil { panic(fmt.Errorf("error opening File: %s", err)) } var got spdx.Document err = tagvalue.ReadInto(file, &got) if err != nil { t.Errorf("Read() error = %v", err) return } if !cmp.Equal(want, got, ignores...) { t.Errorf("got incorrect struct after parsing example: %s", cmp.Diff(want, got, ignores...)) return } } func Test_ReadWrite(t *testing.T) { want := example.Copy() w := &bytes.Buffer{} // get a copy of the handwritten struct so we don't mutate it on accident if err := tagvalue.Write(want, w); err != nil { t.Errorf("Write() error = %v", err.Error()) return } // we should be able to parse what the writer wrote, and it should be identical to the original struct we wrote var got spdx.Document err := tagvalue.ReadInto(bytes.NewReader(w.Bytes()), &got) if err != nil { t.Errorf("failed to parse written document: %v", err.Error()) return } if !cmp.Equal(want, got, ignores...) { t.Errorf("got incorrect struct after writing and re-parsing YAML example: %s", cmp.Diff(want, got, ignores...)) return } } var ignores = []cmp.Option{ cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.IgnoreFields(spdx.Document{}, "Snippets"), cmpopts.IgnoreFields(spdx.File{}, "Annotations"), cmpopts.IgnoreFields(spdx.Package{}, "IsFilesAnalyzedTagPresent", "Annotations"), } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/000077500000000000000000000000001463371440000213325ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_annotation.go000066400000000000000000000016011463371440000250470ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func renderAnnotation(ann *spdx.Annotation, w io.Writer) error { if ann.Annotator.Annotator != "" && ann.Annotator.AnnotatorType != "" { fmt.Fprintf(w, "Annotator: %s: %s\n", ann.Annotator.AnnotatorType, ann.Annotator.Annotator) } if ann.AnnotationDate != "" { fmt.Fprintf(w, "AnnotationDate: %s\n", ann.AnnotationDate) } if ann.AnnotationType != "" { fmt.Fprintf(w, "AnnotationType: %s\n", ann.AnnotationType) } annIDStr := common.RenderDocElementID(ann.AnnotationSPDXIdentifier) if annIDStr != "SPDXRef-" { fmt.Fprintf(w, "SPDXREF: %s\n", annIDStr) } if ann.AnnotationComment != "" { fmt.Fprintf(w, "AnnotationComment: %s\n", textify(ann.AnnotationComment)) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_annotation_test.go000066400000000000000000000065211463371440000261140ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Annotation section Saver tests ===== func TestSaverAnnotationSavesTextForPerson(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Person", Annotator: "John Doe"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Person: John Doe AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverAnnotationSavesTextForOrganization(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Organization", Annotator: "John Doe, Inc."}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Organization: John Doe, Inc. AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverAnnotationSavesTextForTool(t *testing.T) { ann := &spdx.Annotation{ Annotator: common.Annotator{AnnotatorType: "Tool", Annotator: "magictool-1.1"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Annotator: Tool: magictool-1.1 AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document `) // render as buffer of bytes var got bytes.Buffer err := renderAnnotation(ann, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } // note that the annotation has no optional or multiple fields tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_creation_info.go000066400000000000000000000012771463371440000255250ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func renderCreationInfo(ci *spdx.CreationInfo, w io.Writer) error { if ci.LicenseListVersion != "" { fmt.Fprintf(w, "LicenseListVersion: %s\n", ci.LicenseListVersion) } for _, creator := range ci.Creators { fmt.Fprintf(w, "Creator: %s: %s\n", creator.CreatorType, creator.Creator) } if ci.Created != "" { fmt.Fprintf(w, "Created: %s\n", ci.Created) } if ci.CreatorComment != "" { fmt.Fprintf(w, "CreatorComment: %s\n", textify(ci.CreatorComment)) } // add blank newline b/c end of a main section fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_creation_info_test.go000066400000000000000000000057331463371440000265650ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Creation Info section Saver tests ===== func TestSaverCISavesText(t *testing.T) { ci := &spdx.CreationInfo{ LicenseListVersion: "3.9", Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, {Creator: "Jane Doe (janedoe@example.com)", CreatorType: "Person"}, {Creator: "John Doe, Inc.", CreatorType: "Organization"}, {Creator: "Jane Doe LLC", CreatorType: "Organization"}, {Creator: "magictool1-1.0", CreatorType: "Tool"}, {Creator: "magictool2-1.0", CreatorType: "Tool"}, {Creator: "magictool3-1.0", CreatorType: "Tool"}, }, Created: "2018-10-10T06:20:00Z", CreatorComment: "this is a creator comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseListVersion: 3.9 Creator: Person: John Doe Creator: Person: Jane Doe (janedoe@example.com) Creator: Organization: John Doe, Inc. Creator: Organization: Jane Doe LLC Creator: Tool: magictool1-1.0 Creator: Tool: magictool2-1.0 Creator: Tool: magictool3-1.0 Created: 2018-10-10T06:20:00Z CreatorComment: this is a creator comment `) // render as buffer of bytes var got bytes.Buffer err := renderCreationInfo(ci, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverCIOmitsOptionalFieldsIfEmpty(t *testing.T) { // --- need at least one creator; do first for Persons --- ci1 := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, }, Created: "2018-10-10T06:20:00Z", } // what we want to get, as a buffer of bytes want1 := bytes.NewBufferString(`Creator: Person: John Doe Created: 2018-10-10T06:20:00Z `) // render as buffer of bytes var got1 bytes.Buffer err := renderCreationInfo(ci1, &got1) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c1 := bytes.Compare(want1.Bytes(), got1.Bytes()) if c1 != 0 { t.Errorf("Expected %v, got %v", want1.String(), got1.String()) } // --- need at least one creator; now switch to organization --- ci2 := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe, Inc.", CreatorType: "Organization"}, }, Created: "2018-10-10T06:20:00Z", } // what we want to get, as a buffer of bytes want2 := bytes.NewBufferString(`Creator: Organization: John Doe, Inc. Created: 2018-10-10T06:20:00Z `) // render as buffer of bytes var got2 bytes.Buffer err = renderCreationInfo(ci2, &got2) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c2 := bytes.Compare(want2.Bytes(), got2.Bytes()) if c2 != 0 { t.Errorf("Expected %v, got %v", want2.String(), got2.String()) } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_document.go000066400000000000000000000057061463371440000245250ustar00rootroot00000000000000// Package saver2v3 contains functions to render and write a tag-value // formatted version of an in-memory SPDX document and its sections. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // RenderDocument is the main entry point to take an SPDX in-memory // Document, and render it to the received io.Writer. // It is only exported in order to be available to the tvsaver package, // and typically does not need to be called by client code. func RenderDocument(doc *spdx.Document, w io.Writer) error { if doc.CreationInfo == nil { return fmt.Errorf("Document had nil CreationInfo section") } if doc.SPDXVersion != "" { fmt.Fprintf(w, "SPDXVersion: %s\n", doc.SPDXVersion) } if doc.DataLicense != "" { fmt.Fprintf(w, "DataLicense: %s\n", doc.DataLicense) } if doc.SPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(doc.SPDXIdentifier)) } if doc.DocumentName != "" { fmt.Fprintf(w, "DocumentName: %s\n", doc.DocumentName) } if doc.DocumentNamespace != "" { fmt.Fprintf(w, "DocumentNamespace: %s\n", doc.DocumentNamespace) } // print EDRs in order sorted by identifier sort.Slice(doc.ExternalDocumentReferences, func(i, j int) bool { return doc.ExternalDocumentReferences[i].DocumentRefID < doc.ExternalDocumentReferences[j].DocumentRefID }) for _, edr := range doc.ExternalDocumentReferences { fmt.Fprintf(w, "ExternalDocumentRef: DocumentRef-%s %s %s:%s\n", edr.DocumentRefID, edr.URI, edr.Checksum.Algorithm, edr.Checksum.Value) } if doc.DocumentComment != "" { fmt.Fprintf(w, "DocumentComment: %s\n", textify(doc.DocumentComment)) } renderCreationInfo(doc.CreationInfo, w) if len(doc.Files) > 0 { fmt.Fprintf(w, "##### Unpackaged files\n\n") sort.Slice(doc.Files, func(i, j int) bool { return doc.Files[i].FileSPDXIdentifier < doc.Files[j].FileSPDXIdentifier }) for _, fi := range doc.Files { renderFile(fi, w) } } // sort Packages by identifier sort.Slice(doc.Packages, func(i, j int) bool { return doc.Packages[i].PackageSPDXIdentifier < doc.Packages[j].PackageSPDXIdentifier }) for _, pkg := range doc.Packages { fmt.Fprintf(w, "##### Package: %s\n\n", pkg.PackageName) renderPackage(pkg, w) } if len(doc.OtherLicenses) > 0 { fmt.Fprintf(w, "##### Other Licenses\n\n") for _, ol := range doc.OtherLicenses { renderOtherLicense(ol, w) } } if len(doc.Relationships) > 0 { fmt.Fprintf(w, "##### Relationships\n\n") for _, rln := range doc.Relationships { renderRelationship(rln, w) } fmt.Fprintf(w, "\n") } if len(doc.Annotations) > 0 { fmt.Fprintf(w, "##### Annotations\n\n") for _, ann := range doc.Annotations { renderAnnotation(ann, w) fmt.Fprintf(w, "\n") } } if len(doc.Reviews) > 0 { fmt.Fprintf(w, "##### Reviews\n\n") for _, rev := range doc.Reviews { renderReview(rev, w) } } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_document_test.go000066400000000000000000000240631463371440000255610ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== entire Document Saver tests ===== func TestSaverDocumentSavesText(t *testing.T) { // Creation Info section ci := &spdx.CreationInfo{ Creators: []common.Creator{ {Creator: "John Doe", CreatorType: "Person"}, }, Created: "2018-10-10T06:20:00Z", } // unpackaged files f1 := &spdx.File{ FileName: "/tmp/whatever1.txt", FileSPDXIdentifier: common.ElementID("File1231"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983c", Algorithm: common.SHA1}}, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{"Apache-2.0"}, FileCopyrightText: "Copyright (c) Jane Doe", } f2 := &spdx.File{ FileName: "/tmp/whatever2.txt", FileSPDXIdentifier: common.ElementID("File1232"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983d", Algorithm: common.SHA1}}, LicenseConcluded: "MIT", LicenseInfoInFiles: []string{"MIT"}, FileCopyrightText: "Copyright (c) John Doe", } unFiles := []*spdx.File{ f1, f2, } // Package 1: packaged files with snippets sn1 := &spdx.Snippet{ SnippetSPDXIdentifier: "Snippet19", SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "FileHasSnippets").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}}}, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } sn2 := &spdx.Snippet{ SnippetSPDXIdentifier: "Snippet20", SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "FileHasSnippets").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 268}, EndPointer: common.SnippetRangePointer{Offset: 309}}}, SnippetLicenseConcluded: "WTFPL", SnippetCopyrightText: "NOASSERTION", } f3 := &spdx.File{ FileName: "/tmp/file-with-snippets.txt", FileSPDXIdentifier: common.ElementID("FileHasSnippets"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983e", Algorithm: common.SHA1}}, LicenseConcluded: "GPL-2.0-or-later AND WTFPL", LicenseInfoInFiles: []string{ "Apache-2.0", "GPL-2.0-or-later", "WTFPL", }, FileCopyrightText: "Copyright (c) Jane Doe", Snippets: map[common.ElementID]*spdx.Snippet{ common.ElementID("Snippet19"): sn1, common.ElementID("Snippet20"): sn2, }, } f4 := &spdx.File{ FileName: "/tmp/another-file.txt", FileSPDXIdentifier: common.ElementID("FileAnother"), Checksums: []common.Checksum{{Value: "85ed0817af83a24ad8da68c2b5094de69833983f", Algorithm: common.SHA1}}, LicenseConcluded: "BSD-3-Clause", LicenseInfoInFiles: []string{"BSD-3-Clause"}, FileCopyrightText: "Copyright (c) Jane Doe LLC", } pkgWith := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: &common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageLicenseConcluded: "GPL-2.0-or-later AND BSD-3-Clause AND WTFPL", PackageLicenseInfoFromFiles: []string{ "Apache-2.0", "GPL-2.0-or-later", "WTFPL", "BSD-3-Clause", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", Files: []*spdx.File{ f3, f4, }, } // Other Licenses 1 and 2 ol1 := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", } ol2 := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-2", ExtractedText: `License 2 text - this is a license that does some stuff`, LicenseName: "License 2", } // Relationships rln1 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p1"), Relationship: "DESCRIBES", } rln2 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "File1231"), Relationship: "DESCRIBES", } rln3 := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "File1232"), Relationship: "DESCRIBES", } // Annotations ann1 := &spdx.Annotation{ Annotator: common.Annotator{Annotator: "John Doe", AnnotatorType: "Person"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "DOCUMENT"), AnnotationComment: "This is an annotation about the SPDX document", } ann2 := &spdx.Annotation{ Annotator: common.Annotator{Annotator: "John Doe, Inc.", AnnotatorType: "Organization"}, AnnotationDate: "2018-10-10T17:52:00Z", AnnotationType: "REVIEW", AnnotationSPDXIdentifier: common.MakeDocElementID("", "p1"), AnnotationComment: "This is an annotation about Package p1", } // Reviews rev1 := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", } rev2 := &spdx.Review{ Reviewer: "Jane Doe LLC", ReviewerType: "Organization", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: "I have reviewed this SPDX document and it is awesome", } // now, build the document doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), DocumentName: "tools-golang-0.0.1.abcdef", DocumentNamespace: "https://github.com/spdx/spdx-docs/tools-golang/tools-golang-0.0.1.abcdef.whatever", CreationInfo: ci, Packages: []*spdx.Package{ pkgWith, }, Files: unFiles, OtherLicenses: []*spdx.OtherLicense{ ol1, ol2, }, Relationships: []*spdx.Relationship{ rln1, rln2, rln3, }, Annotations: []*spdx.Annotation{ ann1, ann2, }, Reviews: []*spdx.Review{ rev1, rev2, }, } want := bytes.NewBufferString(`SPDXVersion: ` + spdx.Version + ` DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: tools-golang-0.0.1.abcdef DocumentNamespace: https://github.com/spdx/spdx-docs/tools-golang/tools-golang-0.0.1.abcdef.whatever Creator: Person: John Doe Created: 2018-10-10T06:20:00Z ##### Unpackaged files FileName: /tmp/whatever1.txt SPDXID: SPDXRef-File1231 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe FileName: /tmp/whatever2.txt SPDXID: SPDXRef-File1232 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983d LicenseConcluded: MIT LicenseInfoInFile: MIT FileCopyrightText: Copyright (c) John Doe ##### Package: p1 PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: true PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 PackageLicenseConcluded: GPL-2.0-or-later AND BSD-3-Clause AND WTFPL PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseInfoFromFiles: WTFPL PackageLicenseInfoFromFiles: BSD-3-Clause PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. FileName: /tmp/another-file.txt SPDXID: SPDXRef-FileAnother FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983f LicenseConcluded: BSD-3-Clause LicenseInfoInFile: BSD-3-Clause FileCopyrightText: Copyright (c) Jane Doe LLC FileName: /tmp/file-with-snippets.txt SPDXID: SPDXRef-FileHasSnippets FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983e LicenseConcluded: GPL-2.0-or-later AND WTFPL LicenseInfoInFile: Apache-2.0 LicenseInfoInFile: GPL-2.0-or-later LicenseInfoInFile: WTFPL FileCopyrightText: Copyright (c) Jane Doe SnippetSPDXID: SPDXRef-Snippet19 SnippetFromFileSPDXID: SPDXRef-FileHasSnippets SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetSPDXID: SPDXRef-Snippet20 SnippetFromFileSPDXID: SPDXRef-FileHasSnippets SnippetByteRange: 268:309 SnippetLicenseConcluded: WTFPL SnippetCopyrightText: NOASSERTION ##### Other Licenses LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 LicenseID: LicenseRef-2 ExtractedText: License 2 text - this is a license that does some stuff LicenseName: License 2 ##### Relationships Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-p1 Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File1231 Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File1232 ##### Annotations Annotator: Person: John Doe AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-DOCUMENT AnnotationComment: This is an annotation about the SPDX document Annotator: Organization: John Doe, Inc. AnnotationDate: 2018-10-10T17:52:00Z AnnotationType: REVIEW SPDXREF: SPDXRef-p1 AnnotationComment: This is an annotation about Package p1 ##### Reviews Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z Reviewer: Organization: Jane Doe LLC ReviewDate: 2018-10-14T10:28:00Z ReviewComment: I have reviewed this SPDX document and it is awesome `) // render as buffer of bytes var got bytes.Buffer err := RenderDocument(doc, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected {{{%v}}}, got {{{%v}}}", want.String(), got.String()) } } func TestSaverDocumentReturnsErrorIfNilCreationInfo(t *testing.T) { doc := &spdx.Document{} var got bytes.Buffer err := RenderDocument(doc, &got) if err == nil { t.Errorf("Expected error, got nil") } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_file.go000066400000000000000000000041611463371440000236200ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func renderFile(f *spdx.File, w io.Writer) error { if f.FileName != "" { fmt.Fprintf(w, "FileName: %s\n", f.FileName) } if f.FileSPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(f.FileSPDXIdentifier)) } for _, s := range f.FileTypes { fmt.Fprintf(w, "FileType: %s\n", s) } for _, checksum := range f.Checksums { fmt.Fprintf(w, "FileChecksum: %s: %s\n", checksum.Algorithm, checksum.Value) } if f.LicenseConcluded != "" { fmt.Fprintf(w, "LicenseConcluded: %s\n", f.LicenseConcluded) } for _, s := range f.LicenseInfoInFiles { fmt.Fprintf(w, "LicenseInfoInFile: %s\n", s) } if f.LicenseComments != "" { fmt.Fprintf(w, "LicenseComments: %s\n", textify(f.LicenseComments)) } if f.FileCopyrightText != "" { fmt.Fprintf(w, "FileCopyrightText: %s\n", textify(f.FileCopyrightText)) } for _, aop := range f.ArtifactOfProjects { fmt.Fprintf(w, "ArtifactOfProjectName: %s\n", aop.Name) if aop.HomePage != "" { fmt.Fprintf(w, "ArtifactOfProjectHomePage: %s\n", aop.HomePage) } if aop.URI != "" { fmt.Fprintf(w, "ArtifactOfProjectURI: %s\n", aop.URI) } } if f.FileComment != "" { fmt.Fprintf(w, "FileComment: %s\n", textify(f.FileComment)) } if f.FileNotice != "" { fmt.Fprintf(w, "FileNotice: %s\n", textify(f.FileNotice)) } for _, s := range f.FileContributors { fmt.Fprintf(w, "FileContributor: %s\n", s) } for _, s := range f.FileAttributionTexts { fmt.Fprintf(w, "FileAttributionText: %s\n", textify(s)) } for _, s := range f.FileDependencies { fmt.Fprintf(w, "FileDependency: %s\n", s) } fmt.Fprintf(w, "\n") // also render any snippets for this file // get slice of Snippet identifiers so we can sort them snippetKeys := []string{} for k := range f.Snippets { snippetKeys = append(snippetKeys, string(k)) } sort.Strings(snippetKeys) for _, sID := range snippetKeys { s := f.Snippets[common.ElementID(sID)] renderSnippet(s, w) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_file_test.go000066400000000000000000000217001463371440000246550ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== File section Saver tests ===== func TestSaverFileSavesText(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), FileTypes: []string{ "TEXT", "DOCUMENTATION", }, Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, {Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"}, {Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", "Apache-1.1", }, LicenseComments: "this is a license comment(s)", FileCopyrightText: "Copyright (c) Jane Doe", ArtifactOfProjects: []*spdx.ArtifactOfProject{ { Name: "project1", HomePage: "http://example.com/1/", URI: "http://example.com/1/uri.whatever", }, { Name: "project2", }, { Name: "project3", HomePage: "http://example.com/3/", }, { Name: "project4", URI: "http://example.com/4/uri.whatever", }, }, FileComment: "this is a file comment", FileNotice: "This file may be used under either Apache-2.0 or Apache-1.1.", FileContributors: []string{ "John Doe jdoe@example.com", "EvilCorp", }, FileAttributionTexts: []string{ "attributions", `multi-line attribution`, }, FileDependencies: []string{ "f-1.txt", "g.txt", }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileType: TEXT FileType: DOCUMENTATION FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c FileChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd FileChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 LicenseInfoInFile: Apache-1.1 LicenseComments: this is a license comment(s) FileCopyrightText: Copyright (c) Jane Doe ArtifactOfProjectName: project1 ArtifactOfProjectHomePage: http://example.com/1/ ArtifactOfProjectURI: http://example.com/1/uri.whatever ArtifactOfProjectName: project2 ArtifactOfProjectName: project3 ArtifactOfProjectHomePage: http://example.com/3/ ArtifactOfProjectName: project4 ArtifactOfProjectURI: http://example.com/4/uri.whatever FileComment: this is a file comment FileNotice: This file may be used under either Apache-2.0 or Apache-1.1. FileContributor: John Doe jdoe@example.com FileContributor: EvilCorp FileAttributionText: attributions FileAttributionText: multi-line attribution FileDependency: f-1.txt FileDependency: g.txt `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileSavesSnippetsAlso(t *testing.T) { sn1 := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet19"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File123").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}}}, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } sn2 := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet20"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File123").ElementRefID, Ranges: []common.SnippetRange{{StartPointer: common.SnippetRangePointer{Offset: 268}, EndPointer: common.SnippetRangePointer{Offset: 309}}}, SnippetLicenseConcluded: "WTFPL", SnippetCopyrightText: "NOASSERTION", } sns := map[common.ElementID]*spdx.Snippet{ common.ElementID("Snippet19"): sn1, common.ElementID("Snippet20"): sn2, } f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", Snippets: sns, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe SnippetSPDXID: SPDXRef-Snippet19 SnippetFromFileSPDXID: SPDXRef-File123 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetSPDXID: SPDXRef-Snippet20 SnippetFromFileSPDXID: SPDXRef-File123 SnippetByteRange: 268:309 SnippetLicenseConcluded: WTFPL SnippetCopyrightText: NOASSERTION `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileOmitsOptionalFieldsIfEmpty(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileWrapsCopyrightMultiLine(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: `Copyright (c) Jane Doe Copyright (c) John Doe`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe Copyright (c) John Doe `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverFileWrapsCommentsAndNoticesMultiLine(t *testing.T) { f := &spdx.File{ FileName: "/tmp/whatever.txt", FileSPDXIdentifier: common.ElementID("File123"), Checksums: []common.Checksum{ {Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c"}, }, LicenseComments: `this is a multi-line license comment`, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{ "Apache-2.0", }, FileCopyrightText: "Copyright (c) Jane Doe", FileComment: `this is a multi-line file comment`, FileNotice: `This file may be used under either Apache-2.0 or Apache-1.1.`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`FileName: /tmp/whatever.txt SPDXID: SPDXRef-File123 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 LicenseComments: this is a multi-line license comment FileCopyrightText: Copyright (c) Jane Doe FileComment: this is a multi-line file comment FileNotice: This file may be used under either Apache-2.0 or Apache-1.1. `) // render as buffer of bytes var got bytes.Buffer err := renderFile(f, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_other_license.go000066400000000000000000000013421463371440000255220ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func renderOtherLicense(ol *spdx.OtherLicense, w io.Writer) error { if ol.LicenseIdentifier != "" { fmt.Fprintf(w, "LicenseID: %s\n", ol.LicenseIdentifier) } if ol.ExtractedText != "" { fmt.Fprintf(w, "ExtractedText: %s\n", textify(ol.ExtractedText)) } if ol.LicenseName != "" { fmt.Fprintf(w, "LicenseName: %s\n", ol.LicenseName) } for _, s := range ol.LicenseCrossReferences { fmt.Fprintf(w, "LicenseCrossReference: %s\n", s) } if ol.LicenseComment != "" { fmt.Fprintf(w, "LicenseComment: %s\n", textify(ol.LicenseComment)) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_other_license_test.go000066400000000000000000000037461463371440000265730ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Other License section Saver tests ===== func TestSaverOtherLicenseSavesText(t *testing.T) { ol := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", LicenseCrossReferences: []string{ "http://example.com/License1/", "http://example.com/License1AnotherURL/", }, LicenseComment: "this is a license comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 LicenseCrossReference: http://example.com/License1/ LicenseCrossReference: http://example.com/License1AnotherURL/ LicenseComment: this is a license comment `) // render as buffer of bytes var got bytes.Buffer err := renderOtherLicense(ol, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverOtherLicenseOmitsOptionalFieldsIfEmpty(t *testing.T) { ol := &spdx.OtherLicense{ LicenseIdentifier: "LicenseRef-1", ExtractedText: `License 1 text blah blah blah blah blah blah blah`, LicenseName: "License 1", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`LicenseID: LicenseRef-1 ExtractedText: License 1 text blah blah blah blah blah blah blah LicenseName: License 1 `) // render as buffer of bytes var got bytes.Buffer err := renderOtherLicense(ol, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_package.go000066400000000000000000000104261463371440000242750ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "sort" "strings" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func renderPackage(pkg *spdx.Package, w io.Writer) error { if pkg.PackageName != "" { fmt.Fprintf(w, "PackageName: %s\n", pkg.PackageName) } if pkg.PackageSPDXIdentifier != "" { fmt.Fprintf(w, "SPDXID: %s\n", common.RenderElementID(pkg.PackageSPDXIdentifier)) } if pkg.PackageVersion != "" { fmt.Fprintf(w, "PackageVersion: %s\n", pkg.PackageVersion) } if pkg.PackageFileName != "" { fmt.Fprintf(w, "PackageFileName: %s\n", pkg.PackageFileName) } if pkg.PackageSupplier != nil && pkg.PackageSupplier.Supplier != "" { if pkg.PackageSupplier.SupplierType == "" { fmt.Fprintf(w, "PackageSupplier: %s\n", pkg.PackageSupplier.Supplier) } else { fmt.Fprintf(w, "PackageSupplier: %s: %s\n", pkg.PackageSupplier.SupplierType, pkg.PackageSupplier.Supplier) } } if pkg.PackageOriginator != nil && pkg.PackageOriginator.Originator != "" { if pkg.PackageOriginator.OriginatorType == "" { fmt.Fprintf(w, "PackageOriginator: %s\n", pkg.PackageOriginator.Originator) } else { fmt.Fprintf(w, "PackageOriginator: %s: %s\n", pkg.PackageOriginator.OriginatorType, pkg.PackageOriginator.Originator) } } if pkg.PackageDownloadLocation != "" { fmt.Fprintf(w, "PackageDownloadLocation: %s\n", pkg.PackageDownloadLocation) } if pkg.PrimaryPackagePurpose != "" { fmt.Fprintf(w, "PrimaryPackagePurpose: %s\n", pkg.PrimaryPackagePurpose) } if pkg.ReleaseDate != "" { fmt.Fprintf(w, "ReleaseDate: %s\n", pkg.ReleaseDate) } if pkg.BuiltDate != "" { fmt.Fprintf(w, "BuiltDate: %s\n", pkg.BuiltDate) } if pkg.ValidUntilDate != "" { fmt.Fprintf(w, "ValidUntilDate: %s\n", pkg.ValidUntilDate) } if pkg.FilesAnalyzed { if pkg.IsFilesAnalyzedTagPresent { fmt.Fprintf(w, "FilesAnalyzed: true\n") } } else { fmt.Fprintf(w, "FilesAnalyzed: false\n") } if pkg.PackageVerificationCode != nil && pkg.PackageVerificationCode.Value != "" && pkg.FilesAnalyzed == true { if len(pkg.PackageVerificationCode.ExcludedFiles) == 0 { fmt.Fprintf(w, "PackageVerificationCode: %s\n", pkg.PackageVerificationCode.Value) } else { fmt.Fprintf(w, "PackageVerificationCode: %s (excludes: %s)\n", pkg.PackageVerificationCode.Value, strings.Join(pkg.PackageVerificationCode.ExcludedFiles, ", ")) } } for _, checksum := range pkg.PackageChecksums { fmt.Fprintf(w, "PackageChecksum: %s: %s\n", checksum.Algorithm, checksum.Value) } if pkg.PackageHomePage != "" { fmt.Fprintf(w, "PackageHomePage: %s\n", pkg.PackageHomePage) } if pkg.PackageSourceInfo != "" { fmt.Fprintf(w, "PackageSourceInfo: %s\n", textify(pkg.PackageSourceInfo)) } if pkg.PackageLicenseConcluded != "" { fmt.Fprintf(w, "PackageLicenseConcluded: %s\n", pkg.PackageLicenseConcluded) } if pkg.FilesAnalyzed { for _, s := range pkg.PackageLicenseInfoFromFiles { fmt.Fprintf(w, "PackageLicenseInfoFromFiles: %s\n", s) } } if pkg.PackageLicenseDeclared != "" { fmt.Fprintf(w, "PackageLicenseDeclared: %s\n", pkg.PackageLicenseDeclared) } if pkg.PackageLicenseComments != "" { fmt.Fprintf(w, "PackageLicenseComments: %s\n", textify(pkg.PackageLicenseComments)) } if pkg.PackageCopyrightText != "" { fmt.Fprintf(w, "PackageCopyrightText: %s\n", textify(pkg.PackageCopyrightText)) } if pkg.PackageSummary != "" { fmt.Fprintf(w, "PackageSummary: %s\n", textify(pkg.PackageSummary)) } if pkg.PackageDescription != "" { fmt.Fprintf(w, "PackageDescription: %s\n", textify(pkg.PackageDescription)) } if pkg.PackageComment != "" { fmt.Fprintf(w, "PackageComment: %s\n", textify(pkg.PackageComment)) } for _, s := range pkg.PackageExternalReferences { fmt.Fprintf(w, "ExternalRef: %s %s %s\n", s.Category, s.RefType, s.Locator) if s.ExternalRefComment != "" { fmt.Fprintf(w, "ExternalRefComment: %s\n", textify(s.ExternalRefComment)) } } for _, s := range pkg.PackageAttributionTexts { fmt.Fprintf(w, "PackageAttributionText: %s\n", textify(s)) } fmt.Fprintf(w, "\n") // also render any files for this package sort.Slice(pkg.Files, func(i, j int) bool { return pkg.Files[i].FileSPDXIdentifier < pkg.Files[j].FileSPDXIdentifier }) for _, fi := range pkg.Files { renderFile(fi, w) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_package_test.go000066400000000000000000000437021463371440000253370ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Package section Saver tests ===== func TestSaverPackageSavesTextCombo1(t *testing.T) { // include package external refs // test Supplier:Organization, Originator:Person // FilesAnalyzed true, IsFilesAnalyzedTagPresent true // PackageVerificationCodeExcludedFile has string // NOTE, this is an entirely made up CPE and the format is likely invalid per1 := &spdx.PackageExternalReference{ Category: "SECURITY", RefType: "cpe22Type", Locator: "cpe:/a:john_doe_inc:p1:0.1.0", ExternalRefComment: "this is an external ref comment #1", } // NOTE, this is an entirely made up NPM per2 := &spdx.PackageExternalReference{ Category: "PACKAGE-MANAGER", RefType: "npm", Locator: "p1@0.1.0", ExternalRefComment: `this is a multi-line external ref comment`, } // NOTE, this is an entirely made up SWH persistent ID per3 := &spdx.PackageExternalReference{ Category: "PERSISTENT-ID", RefType: "swh", Locator: "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", // no ExternalRefComment for this one } per4 := &spdx.PackageExternalReference{ Category: "OTHER", RefType: "anything", Locator: "anything-without-spaces-can-go-here", // no ExternalRefComment for this one } pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{SupplierType: "Organization", Supplier: "John Doe, Inc."}, PackageOriginator: &common.Originator{Originator: "John Doe", OriginatorType: "Person"}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: true, PackageVerificationCode: &common.PackageVerificationCode{ Value: "0123456789abcdef0123456789abcdef01234567", ExcludedFiles: []string{"p1-0.1.0.spdx"}, }, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", PackageAttributionTexts: []string{"Include this notice in all advertising materials"}, PackageExternalReferences: []*spdx.PackageExternalReference{ per1, per2, per3, per4, }, PrimaryPackagePurpose: "LIBRARY", BuiltDate: "2021-09-15T02:38:00Z", ValidUntilDate: "2022-10-15T02:38:00Z", ReleaseDate: "2021-10-15T02:38:00Z", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: Organization: John Doe, Inc. PackageOriginator: Person: John Doe PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz PrimaryPackagePurpose: LIBRARY ReleaseDate: 2021-10-15T02:38:00Z BuiltDate: 2021-09-15T02:38:00Z ValidUntilDate: 2022-10-15T02:38:00Z FilesAnalyzed: true PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 (excludes: p1-0.1.0.spdx) PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseInfoFromFiles: Apache-1.1 PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment ExternalRef: SECURITY cpe22Type cpe:/a:john_doe_inc:p1:0.1.0 ExternalRefComment: this is an external ref comment #1 ExternalRef: PACKAGE-MANAGER npm p1@0.1.0 ExternalRefComment: this is a multi-line external ref comment ExternalRef: PERSISTENT-ID swh swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2 ExternalRef: OTHER anything anything-without-spaces-can-go-here PackageAttributionText: Include this notice in all advertising materials `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesTextCombo2(t *testing.T) { // no package external refs // test Supplier:NOASSERTION, Originator:Organization // FilesAnalyzed true, IsFilesAnalyzedTagPresent false // PackageVerificationCodeExcludedFile is empty pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{Supplier: "NOASSERTION"}, PackageOriginator: &common.Originator{OriginatorType: "Organization", Originator: "John Doe, Inc."}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: true, IsFilesAnalyzedTagPresent: false, PackageVerificationCode: &common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", PackageAttributionTexts: []string{"Include this notice in all advertising materials"}, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: NOASSERTION PackageOriginator: Organization: John Doe, Inc. PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz PackageVerificationCode: 0123456789abcdef0123456789abcdef01234567 PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseInfoFromFiles: Apache-1.1 PackageLicenseInfoFromFiles: Apache-2.0 PackageLicenseInfoFromFiles: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment PackageAttributionText: Include this notice in all advertising materials `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesTextCombo3(t *testing.T) { // no package external refs // test Supplier:Person, Originator:NOASSERTION // FilesAnalyzed false, IsFilesAnalyzedTagPresent true // PackageVerificationCodeExcludedFile is empty // three PackageAttributionTexts, one with multi-line text pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageVersion: "0.1.0", PackageFileName: "p1-0.1.0-master.tar.gz", PackageSupplier: &common.Supplier{Supplier: "John Doe", SupplierType: "Person"}, PackageOriginator: &common.Originator{Originator: "NOASSERTION"}, PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output // since FilesAnalyzed is false PackageVerificationCode: &common.PackageVerificationCode{Value: "0123456789abcdef0123456789abcdef01234567"}, PackageChecksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, { Algorithm: common.SHA256, Value: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", }, { Algorithm: common.MD5, Value: "624c1abb3664f4b35547e7c73864ad24", }, }, PackageHomePage: "http://example.com/p1", PackageSourceInfo: "this is a source comment", PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageLicenseComments: "this is a license comment(s)", PackageCopyrightText: "Copyright (c) John Doe, Inc.", PackageSummary: "this is a summary comment", PackageDescription: "this is a description comment", PackageComment: "this is a comment comment", PackageAttributionTexts: []string{ "Include this notice in all advertising materials", "and also this notice", `and this multi-line notice which goes across two lines`, }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageVersion: 0.1.0 PackageFileName: p1-0.1.0-master.tar.gz PackageSupplier: Person: John Doe PackageOriginator: NOASSERTION PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 PackageHomePage: http://example.com/p1 PackageSourceInfo: this is a source comment PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageLicenseComments: this is a license comment(s) PackageCopyrightText: Copyright (c) John Doe, Inc. PackageSummary: this is a summary comment PackageDescription: this is a description comment PackageComment: this is a comment comment PackageAttributionText: Include this notice in all advertising materials PackageAttributionText: and also this notice PackageAttributionText: and this multi-line notice which goes across two lines `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSaveOmitsOptionalFieldsIfEmpty(t *testing.T) { pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output, // even if present in model, since FilesAnalyzed is false PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // even if present in model, since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageSavesFilesIfPresent(t *testing.T) { f1 := &spdx.File{ FileName: "/tmp/whatever1.txt", FileSPDXIdentifier: common.ElementID("File1231"), Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983c", }, }, LicenseConcluded: "Apache-2.0", LicenseInfoInFiles: []string{"Apache-2.0"}, FileCopyrightText: "Copyright (c) Jane Doe", } f2 := &spdx.File{ FileName: "/tmp/whatever2.txt", FileSPDXIdentifier: common.ElementID("File1232"), Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "85ed0817af83a24ad8da68c2b5094de69833983d", }, }, LicenseConcluded: "MIT", LicenseInfoInFiles: []string{"MIT"}, FileCopyrightText: "Copyright (c) John Doe", } pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, // NOTE that verification code MUST be omitted from output, // even if present in model, since FilesAnalyzed is false PackageLicenseConcluded: "GPL-2.0-or-later", // NOTE that license info from files MUST be omitted from output // even if present in model, since FilesAnalyzed is false PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: "Copyright (c) John Doe, Inc.", Files: []*spdx.File{ f1, f2, }, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. FileName: /tmp/whatever1.txt SPDXID: SPDXRef-File1231 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c LicenseConcluded: Apache-2.0 LicenseInfoInFile: Apache-2.0 FileCopyrightText: Copyright (c) Jane Doe FileName: /tmp/whatever2.txt SPDXID: SPDXRef-File1232 FileChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983d LicenseConcluded: MIT LicenseInfoInFile: MIT FileCopyrightText: Copyright (c) John Doe `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverPackageWrapsMultiLine(t *testing.T) { pkg := &spdx.Package{ PackageName: "p1", PackageSPDXIdentifier: common.ElementID("p1"), PackageDownloadLocation: "http://example.com/p1/p1-0.1.0-master.tar.gz", FilesAnalyzed: false, IsFilesAnalyzedTagPresent: true, PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseInfoFromFiles: []string{ "Apache-1.1", "Apache-2.0", "GPL-2.0-or-later", }, PackageLicenseDeclared: "Apache-2.0 OR GPL-2.0-or-later", PackageCopyrightText: `Copyright (c) John Doe, Inc. Copyright Jane Doe`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`PackageName: p1 SPDXID: SPDXRef-p1 PackageDownloadLocation: http://example.com/p1/p1-0.1.0-master.tar.gz FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-or-later PackageLicenseDeclared: Apache-2.0 OR GPL-2.0-or-later PackageCopyrightText: Copyright (c) John Doe, Inc. Copyright Jane Doe `) // render as buffer of bytes var got bytes.Buffer err := renderPackage(pkg, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_relationship.go000066400000000000000000000012251463371440000254000ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func renderRelationship(rln *spdx.Relationship, w io.Writer) error { rlnAStr := common.RenderDocElementID(rln.RefA) rlnBStr := common.RenderDocElementID(rln.RefB) if rlnAStr != "SPDXRef-" && rlnBStr != "SPDXRef-" && rln.Relationship != "" { fmt.Fprintf(w, "Relationship: %s %s %s\n", rlnAStr, rln.Relationship, rlnBStr) } if rln.RelationshipComment != "" { fmt.Fprintf(w, "RelationshipComment: %s\n", textify(rln.RelationshipComment)) } return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_relationship_test.go000066400000000000000000000076671463371440000264570ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Relationship section Saver tests ===== func TestSaverRelationshipSavesText(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", RelationshipComment: "this is a comment", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2 RelationshipComment: this is a comment `) // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipOmitsOptionalFieldsIfEmpty(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString("Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2\n") // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipCanHaveNONEOnRight(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "PackageA"), RefB: common.MakeDocElementSpecial("NONE"), Relationship: "DEPENDS_ON", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString("Relationship: SPDXRef-PackageA DEPENDS_ON NONE\n") // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipCanHaveNOASSERTIONOnRight(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "PackageA"), RefB: common.MakeDocElementSpecial("NOASSERTION"), Relationship: "DEPENDS_ON", } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString("Relationship: SPDXRef-PackageA DEPENDS_ON NOASSERTION\n") // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverRelationshipWrapsCommentMultiLine(t *testing.T) { rln := &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "2"), Relationship: "DESCRIBES", RelationshipComment: `this is a multi-line comment`, } // what we want to get, as a buffer of bytes // no trailing blank newline want := bytes.NewBufferString(`Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2 RelationshipComment: this is a multi-line comment `) // render as buffer of bytes var got bytes.Buffer err := renderRelationship(rln, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_review.go000066400000000000000000000010541463371440000242000ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func renderReview(rev *spdx.Review, w io.Writer) error { if rev.Reviewer != "" && rev.ReviewerType != "" { fmt.Fprintf(w, "Reviewer: %s: %s\n", rev.ReviewerType, rev.Reviewer) } if rev.ReviewDate != "" { fmt.Fprintf(w, "ReviewDate: %s\n", rev.ReviewDate) } if rev.ReviewComment != "" { fmt.Fprintf(w, "ReviewComment: %s\n", textify(rev.ReviewComment)) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_review_test.go000066400000000000000000000043431463371440000252430ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Review section Saver tests ===== func TestSaverReviewSavesText(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: "this is a review comment", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z ReviewComment: this is a review comment `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverReviewOmitsOptionalFieldsIfEmpty(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverReviewWrapsMultiLine(t *testing.T) { rev := &spdx.Review{ Reviewer: "John Doe", ReviewerType: "Person", ReviewDate: "2018-10-14T10:28:00Z", ReviewComment: `this is a multi-line review comment`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`Reviewer: Person: John Doe ReviewDate: 2018-10-14T10:28:00Z ReviewComment: this is a multi-line review comment `) // render as buffer of bytes var got bytes.Buffer err := renderReview(rev, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_snippet.go000066400000000000000000000034401463371440000243620ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "io" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) func renderSnippet(sn *spdx.Snippet, w io.Writer) error { if sn.SnippetSPDXIdentifier != "" { fmt.Fprintf(w, "SnippetSPDXID: %s\n", common.RenderElementID(sn.SnippetSPDXIdentifier)) } snFromFileIDStr := common.RenderElementID(sn.SnippetFromFileSPDXIdentifier) if snFromFileIDStr != "" { fmt.Fprintf(w, "SnippetFromFileSPDXID: %s\n", snFromFileIDStr) } for _, snippetRange := range sn.Ranges { if snippetRange.StartPointer.Offset != 0 && snippetRange.EndPointer.Offset != 0 { fmt.Fprintf(w, "SnippetByteRange: %d:%d\n", snippetRange.StartPointer.Offset, snippetRange.EndPointer.Offset) } if snippetRange.StartPointer.LineNumber != 0 && snippetRange.EndPointer.LineNumber != 0 { fmt.Fprintf(w, "SnippetLineRange: %d:%d\n", snippetRange.StartPointer.LineNumber, snippetRange.EndPointer.LineNumber) } } if sn.SnippetLicenseConcluded != "" { fmt.Fprintf(w, "SnippetLicenseConcluded: %s\n", sn.SnippetLicenseConcluded) } for _, s := range sn.LicenseInfoInSnippet { fmt.Fprintf(w, "LicenseInfoInSnippet: %s\n", s) } if sn.SnippetLicenseComments != "" { fmt.Fprintf(w, "SnippetLicenseComments: %s\n", textify(sn.SnippetLicenseComments)) } if sn.SnippetCopyrightText != "" { fmt.Fprintf(w, "SnippetCopyrightText: %s\n", textify(sn.SnippetCopyrightText)) } if sn.SnippetComment != "" { fmt.Fprintf(w, "SnippetComment: %s\n", textify(sn.SnippetComment)) } if sn.SnippetName != "" { fmt.Fprintf(w, "SnippetName: %s\n", sn.SnippetName) } for _, s := range sn.SnippetAttributionTexts { fmt.Fprintf(w, "SnippetAttributionText: %s\n", textify(s)) } fmt.Fprintf(w, "\n") return nil } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/save_snippet_test.go000066400000000000000000000102721463371440000254220ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "bytes" "testing" "github.com/spdx/tools-golang/spdx/v2/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // ===== Snippet section Saver tests ===== func TestSaverSnippetSavesText(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{LineNumber: 3}, EndPointer: common.SnippetRangePointer{LineNumber: 8}, }, { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", LicenseInfoInSnippet: []string{ "GPL-2.0-or-later", "MIT", }, SnippetLicenseComments: "this is a comment(s) about the snippet license", SnippetCopyrightText: "Copyright (c) John Doe 20x6", SnippetComment: "this is a snippet comment", SnippetName: "from John's program", SnippetAttributionTexts: []string{"some attributions"}, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetLineRange: 3:8 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later LicenseInfoInSnippet: GPL-2.0-or-later LicenseInfoInSnippet: MIT SnippetLicenseComments: this is a comment(s) about the snippet license SnippetCopyrightText: Copyright (c) John Doe 20x6 SnippetComment: this is a snippet comment SnippetName: from John's program SnippetAttributionText: some attributions `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverSnippetOmitsOptionalFieldsIfEmpty(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: "Copyright (c) John Doe 20x6", } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } func TestSaverSnippetWrapsCopyrightMultiline(t *testing.T) { sn := &spdx.Snippet{ SnippetSPDXIdentifier: common.ElementID("Snippet17"), SnippetFromFileSPDXIdentifier: common.MakeDocElementID("", "File292").ElementRefID, Ranges: []common.SnippetRange{ { StartPointer: common.SnippetRangePointer{Offset: 17}, EndPointer: common.SnippetRangePointer{Offset: 209}, }, }, SnippetLicenseConcluded: "GPL-2.0-or-later", SnippetCopyrightText: `Copyright (c) John Doe 20x6 Copyright (c) John Doe 20x6`, } // what we want to get, as a buffer of bytes want := bytes.NewBufferString(`SnippetSPDXID: SPDXRef-Snippet17 SnippetFromFileSPDXID: SPDXRef-File292 SnippetByteRange: 17:209 SnippetLicenseConcluded: GPL-2.0-or-later SnippetCopyrightText: Copyright (c) John Doe 20x6 Copyright (c) John Doe 20x6 `) // render as buffer of bytes var got bytes.Buffer err := renderSnippet(sn, &got) if err != nil { t.Errorf("Expected nil error, got %v", err) } // check that they match c := bytes.Compare(want.Bytes(), got.Bytes()) if c != 0 { t.Errorf("Expected %v, got %v", want.String(), got.String()) } } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/util.go000066400000000000000000000003451463371440000226400ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "fmt" "strings" ) func textify(s string) string { if strings.Contains(s, "\n") { return fmt.Sprintf("%s", s) } return s } tools-golang-0.5.5/spdx/v2/v2_3/tagvalue/writer/util_test.go000066400000000000000000000011011463371440000236660ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package writer import ( "testing" ) // ===== Utility function tests ===== func TestTextifyWrapsStringWithNewline(t *testing.T) { s := `this text has a newline in it` want := `this text has a newline in it` got := textify(s) if want != got { t.Errorf("Expected %s, got %s", want, got) } } func TestTextifyDoesNotWrapsStringWithNoNewline(t *testing.T) { s := `this text has no newline in it` want := s got := textify(s) if want != got { t.Errorf("Expected %s, got %s", want, got) } } tools-golang-0.5.5/spdx/v2/v2_3/yaml/000077500000000000000000000000001463371440000171505ustar00rootroot00000000000000tools-golang-0.5.5/spdx/v2/v2_3/yaml/yaml_test.go000066400000000000000000000044531463371440000215060ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package yaml import ( "bytes" jsonenc "encoding/json" "flag" "fmt" "os" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" "github.com/spdx/tools-golang/spdx/v2/v2_3/example" "github.com/spdx/tools-golang/yaml" ) var update = *flag.Bool("update-snapshots", false, "update the example snapshot") func Test_Read(t *testing.T) { fileName := "../../../../examples/sample-docs/yaml/SPDXYAMLExample-2.3.spdx.yaml" want := example.Copy() if update { f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { t.Errorf("unable to write SPDX 2.3 example to YAML: %v", err) } err = yaml.Write(want, f) if err != nil { t.Errorf("unable to serialize SPDX 2.3 example to YAML: %v", err) } } file, err := os.Open(fileName) if err != nil { panic(fmt.Errorf("error opening File: %s", err)) } var got spdx.Document err = yaml.ReadInto(file, &got) if err != nil { t.Errorf("yaml.Read() error = %v", err) return } if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing YAML example: %s", diff) return } } func Test_Write(t *testing.T) { want := example.Copy() // we always output FilesAnalyzed, even though we handle reading files where it is omitted for _, p := range want.Packages { p.IsFilesAnalyzedTagPresent = true } w := &bytes.Buffer{} if err := yaml.Write(&want, w); err != nil { t.Errorf("Save() error = %v", err.Error()) return } // we should be able to parse what the writer wrote, and it should be identical to the original handwritten struct var got spdx.Document err := yaml.ReadInto(bytes.NewReader(w.Bytes()), &got) if err != nil { t.Errorf("failed to parse written document: %v", err.Error()) return } if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{}), cmpopts.SortSlices(relationshipLess)); len(diff) > 0 { t.Errorf("got incorrect struct after parsing YAML example: %s", diff) return } } func relationshipLess(a, b *spdx.Relationship) bool { aStr, _ := jsonenc.Marshal(a) bStr, _ := jsonenc.Marshal(b) return string(aStr) < string(bStr) } tools-golang-0.5.5/spdxlib/000077500000000000000000000000001463371440000155755ustar00rootroot00000000000000tools-golang-0.5.5/spdxlib/described_elements.go000066400000000000000000000041161463371440000217460ustar00rootroot00000000000000// Package spdxlib contains convenience and utility functions for working // with an SPDX document that has already been created in memory. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package spdxlib import ( "fmt" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) // GetDescribedPackageIDs returns a slice of ElementIDs for all Packages // in this Document that it "describes," according to SPDX rules: // - If the document has only one Package, its ID is returned. // - If the document has 2+ Packages, it returns the IDs of those that have // a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. func GetDescribedPackageIDs(doc *spdx.Document) ([]common.ElementID, error) { // if nil Packages map or zero packages in it, return empty slice if doc.Packages == nil { return nil, fmt.Errorf("Packages map is nil") } if len(doc.Packages) == 0 { return nil, fmt.Errorf("no Packages in Document") } if len(doc.Packages) == 1 { // get first (only) one and return its ID for _, pkg := range doc.Packages { return []common.ElementID{pkg.PackageSPDXIdentifier}, nil } } // two or more packages, so we need to go through the relationships, // find DESCRIBES or DESCRIBED_BY for this DOCUMENT, verify they are // valid IDs in this document's packages, and return them if doc.Relationships == nil { return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil") } eIDs, err := FilterRelationships(doc, func(relationship *spdx.Relationship) *common.ElementID { refDocument := common.MakeDocElementID("", "DOCUMENT") if relationship.Relationship == "DESCRIBES" && relationship.RefA == refDocument { return &relationship.RefB.ElementRefID } else if relationship.Relationship == "DESCRIBED_BY" && relationship.RefB == refDocument { return &relationship.RefA.ElementRefID } return nil }) if err != nil { return nil, err } if len(eIDs) == 0 { return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document") } eIDs = SortElementIDs(eIDs) return eIDs, nil } tools-golang-0.5.5/spdxlib/described_elements_test.go000066400000000000000000000130331463371440000230030ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package spdxlib import ( "testing" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) func TestCanGetIDsOfDescribedPackages(t *testing.T) { // set up document and some packages and relationships doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, {PackageName: "pkg4", PackageSPDXIdentifier: "p4"}, {PackageName: "pkg5", PackageSPDXIdentifier: "p5"}, }, Relationships: []*spdx.Relationship{ &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p1"), Relationship: "DESCRIBES", }, &spdx.Relationship{ RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p5"), Relationship: "DESCRIBES", }, // inverse relationship -- should also get detected &spdx.Relationship{ RefA: common.MakeDocElementID("", "p4"), RefB: common.MakeDocElementID("", "DOCUMENT"), Relationship: "DESCRIBED_BY", }, // different relationship &spdx.Relationship{ RefA: common.MakeDocElementID("", "p1"), RefB: common.MakeDocElementID("", "p2"), Relationship: "DEPENDS_ON", }, }, } // request IDs for DESCRIBES / DESCRIBED_BY relationships describedPkgIDs, err := GetDescribedPackageIDs(doc) if err != nil { t.Fatalf("expected nil error, got %v", err) } // should be three of the five IDs, returned in alphabetical order if len(describedPkgIDs) != 3 { t.Fatalf("expected %d packages, got %d", 3, len(describedPkgIDs)) } if describedPkgIDs[0] != common.ElementID("p1") { t.Errorf("expected %v, got %v", common.ElementID("p1"), describedPkgIDs[0]) } if describedPkgIDs[1] != common.ElementID("p4") { t.Errorf("expected %v, got %v", common.ElementID("p4"), describedPkgIDs[1]) } if describedPkgIDs[2] != common.ElementID("p5") { t.Errorf("expected %v, got %v", common.ElementID("p5"), describedPkgIDs[2]) } } func TestGetDescribedPackagesReturnsSinglePackageIfOnlyOne(t *testing.T) { // set up document and one package, but no relationships // b/c only one package doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, }, } // request IDs for DESCRIBES / DESCRIBED_BY relationships describedPkgIDs, err := GetDescribedPackageIDs(doc) if err != nil { t.Fatalf("expected nil error, got %v", err) } // should return the single package if len(describedPkgIDs) != 1 { t.Fatalf("expected %d package, got %d", 1, len(describedPkgIDs)) } if describedPkgIDs[0] != common.ElementID("p1") { t.Errorf("expected %v, got %v", common.ElementID("p1"), describedPkgIDs[0]) } } func TestFailsToGetDescribedPackagesIfMoreThanOneWithoutDescribesRelationship(t *testing.T) { // set up document and multiple packages, but no DESCRIBES relationships doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, {PackageName: "pkg4", PackageSPDXIdentifier: "p4"}, {PackageName: "pkg5", PackageSPDXIdentifier: "p5"}, }, Relationships: []*spdx.Relationship{ // different relationship &spdx.Relationship{ RefA: common.MakeDocElementID("", "p1"), RefB: common.MakeDocElementID("", "p2"), Relationship: "DEPENDS_ON", }, }, } _, err := GetDescribedPackageIDs(doc) if err == nil { t.Fatalf("expected non-nil error, got nil") } } func TestFailsToGetDescribedPackagesIfMoreThanOneWithNilRelationships(t *testing.T) { // set up document and multiple packages, but no relationships slice doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, }, } _, err := GetDescribedPackageIDs(doc) if err == nil { t.Fatalf("expected non-nil error, got nil") } } func TestFailsToGetDescribedPackagesIfZeroPackagesInMap(t *testing.T) { // set up document but no packages doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{}, } _, err := GetDescribedPackageIDs(doc) if err == nil { t.Fatalf("expected non-nil error, got nil") } } func TestFailsToGetDescribedPackagesIfNilMap(t *testing.T) { // set up document but no packages doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, } _, err := GetDescribedPackageIDs(doc) if err == nil { t.Fatalf("expected non-nil error, got nil") } } tools-golang-0.5.5/spdxlib/documents.go000066400000000000000000000024651463371440000201340ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package spdxlib import ( "fmt" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) // ValidateDocument returns an error if the Document is found to be invalid, or nil if the Document is valid. // Currently, this only verifies that all Element IDs mentioned in Relationships exist in the Document as either a // Package or an UnpackagedFile. func ValidateDocument(doc *spdx.Document) error { // cache a map of package IDs for quick lookups validElementIDs := make(map[common.ElementID]bool) for _, docPackage := range doc.Packages { validElementIDs[docPackage.PackageSPDXIdentifier] = true } for _, unpackagedFile := range doc.Files { validElementIDs[unpackagedFile.FileSPDXIdentifier] = true } // add the Document element ID validElementIDs[common.MakeDocElementID("", "DOCUMENT").ElementRefID] = true for _, relationship := range doc.Relationships { if !validElementIDs[relationship.RefA.ElementRefID] { return fmt.Errorf("%s used in relationship but no such package exists", string(relationship.RefA.ElementRefID)) } if !validElementIDs[relationship.RefB.ElementRefID] { return fmt.Errorf("%s used in relationship but no such package exists", string(relationship.RefB.ElementRefID)) } } return nil } tools-golang-0.5.5/spdxlib/documents_test.go000066400000000000000000000063531463371440000211730ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package spdxlib import ( "testing" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) func TestValidDocumentPassesValidation(t *testing.T) { // set up document and some packages and relationships doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, {PackageName: "pkg4", PackageSPDXIdentifier: "p4"}, {PackageName: "pkg5", PackageSPDXIdentifier: "p5"}, }, Files: []*spdx.File{ {FileName: "file1", FileSPDXIdentifier: "f1"}, }, Relationships: []*spdx.Relationship{ { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p1"), Relationship: "DESCRIBES", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p5"), Relationship: "DESCRIBES", }, // inverse relationship -- should also get detected { RefA: common.MakeDocElementID("", "p4"), RefB: common.MakeDocElementID("", "DOCUMENT"), Relationship: "DESCRIBED_BY", }, // different relationship { RefA: common.MakeDocElementID("", "p1"), RefB: common.MakeDocElementID("", "p2"), Relationship: "DEPENDS_ON", }, }, } err := ValidateDocument(doc) if err != nil { t.Fatalf("expected nil error, got: %s", err.Error()) } } func TestInvalidDocumentFailsValidation(t *testing.T) { // set up document and some packages and relationships doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, }, Relationships: []*spdx.Relationship{ { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p1"), Relationship: "DESCRIBES", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p5"), Relationship: "DESCRIBES", }, // invalid ID p99 { RefA: common.MakeDocElementID("", "p1"), RefB: common.MakeDocElementID("", "p99"), Relationship: "DEPENDS_ON", }, }, } err := ValidateDocument(doc) if err == nil { t.Fatalf("expected non-nil error, got nil") } doc = &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Relationships: []*spdx.Relationship{ { RefA: common.MakeDocElementID("", "p1"), RefB: common.MakeDocElementID("", "p99"), Relationship: "DEPENDS_ON", }, }, } err = ValidateDocument(doc) if err == nil { t.Fatalf("expected non-nil error, got nil") } } tools-golang-0.5.5/spdxlib/element_ids.go000066400000000000000000000005541463371440000204200ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package spdxlib import ( "sort" "github.com/spdx/tools-golang/spdx/v2/common" ) // SortElementIDs sorts and returns the given slice of ElementIDs func SortElementIDs(eIDs []common.ElementID) []common.ElementID { sort.Slice(eIDs, func(i, j int) bool { return eIDs[i] < eIDs[j] }) return eIDs } tools-golang-0.5.5/spdxlib/element_ids_test.go000066400000000000000000000006331463371440000214550ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package spdxlib import ( "reflect" "testing" "github.com/spdx/tools-golang/spdx/v2/common" ) func TestSortElementIDs(t *testing.T) { eIDs := []common.ElementID{"def", "abc", "123"} eIDs = SortElementIDs(eIDs) if !reflect.DeepEqual(eIDs, []common.ElementID{"123", "abc", "def"}) { t.Fatalf("expected sorted ElementIDs, got: %v", eIDs) } } tools-golang-0.5.5/spdxlib/relationships.go000066400000000000000000000012611463371440000210100ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package spdxlib import ( "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) // FilterRelationships returns a slice of Element IDs returned by the given filter closure. The closure is passed // one relationship at a time, and it can return an ElementID or nil. func FilterRelationships(doc *spdx.Document, filter func(*spdx.Relationship) *common.ElementID) ([]common.ElementID, error) { elementIDs := []common.ElementID{} for _, relationship := range doc.Relationships { if id := filter(relationship); id != nil { elementIDs = append(elementIDs, *id) } } return elementIDs, nil } tools-golang-0.5.5/spdxlib/relationships_test.go000066400000000000000000000042631463371440000220540ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package spdxlib import ( "testing" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) func TestFindsDependsOnRelationships(t *testing.T) { // set up document and some packages and relationships doc := &spdx.Document{ SPDXVersion: spdx.Version, DataLicense: spdx.DataLicense, SPDXIdentifier: common.ElementID("DOCUMENT"), CreationInfo: &spdx.CreationInfo{}, Packages: []*spdx.Package{ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, {PackageName: "pkg4", PackageSPDXIdentifier: "p4"}, {PackageName: "pkg5", PackageSPDXIdentifier: "p5"}, }, Relationships: []*spdx.Relationship{ { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p1"), Relationship: "DESCRIBES", }, { RefA: common.MakeDocElementID("", "DOCUMENT"), RefB: common.MakeDocElementID("", "p5"), Relationship: "DESCRIBES", }, // inverse relationship -- should also get detected { RefA: common.MakeDocElementID("", "p4"), RefB: common.MakeDocElementID("", "DOCUMENT"), Relationship: "DESCRIBED_BY", }, // different relationship { RefA: common.MakeDocElementID("", "p1"), RefB: common.MakeDocElementID("", "p2"), Relationship: "DEPENDS_ON", }, }, } eIDs, err := FilterRelationships(doc, func(relationship *spdx.Relationship) *common.ElementID { p1EID := common.MakeDocElementID("", "p1") if relationship.Relationship == "DEPENDS_ON" && relationship.RefA == p1EID { return &relationship.RefB.ElementRefID } else if relationship.Relationship == "DEPENDENCY_OF" && relationship.RefB == p1EID { return &relationship.RefA.ElementRefID } return nil }) if err != nil { t.Fatalf("expected non-nil err, got: %s", err.Error()) } if len(eIDs) != 1 { t.Fatalf("expected 1 ElementID, got: %v", eIDs) } if eIDs[0] != common.MakeDocElementID("", "p2").ElementRefID { t.Fatalf("received unexpected relationship: %v", eIDs[0]) } } tools-golang-0.5.5/tagvalue/000077500000000000000000000000001463371440000157405ustar00rootroot00000000000000tools-golang-0.5.5/tagvalue/reader.go000066400000000000000000000036451463371440000175410ustar00rootroot00000000000000// Package tvloader is used to load and parse SPDX tag-value documents // into tools-golang data structures. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package tagvalue import ( "fmt" "io" "github.com/spdx/tools-golang/convert" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" v2_1_reader "github.com/spdx/tools-golang/spdx/v2/v2_1/tagvalue/reader" "github.com/spdx/tools-golang/spdx/v2/v2_2" v2_2_reader "github.com/spdx/tools-golang/spdx/v2/v2_2/tagvalue/reader" "github.com/spdx/tools-golang/spdx/v2/v2_3" v2_3_reader "github.com/spdx/tools-golang/spdx/v2/v2_3/tagvalue/reader" "github.com/spdx/tools-golang/tagvalue/reader" ) // Read takes an io.Reader and returns a fully-parsed current model SPDX Document // or an error if any error is encountered. func Read(content io.Reader) (*spdx.Document, error) { doc := spdx.Document{} err := ReadInto(content, &doc) return &doc, err } // ReadInto takes an io.Reader, reads in the SPDX document at the version provided // and converts to the doc version func ReadInto(content io.Reader, doc common.AnyDocument) error { if !convert.IsPtr(doc) { return fmt.Errorf("doc to read into must be a pointer") } tvPairs, err := reader.ReadTagValues(content) if err != nil { return err } if len(tvPairs) == 0 { return fmt.Errorf("no tag values found") } version := "" for _, pair := range tvPairs { if pair.Tag == "SPDXVersion" { version = pair.Value break } } var data interface{} switch version { case v2_1.Version: data, err = v2_1_reader.ParseTagValues(tvPairs) case v2_2.Version: data, err = v2_2_reader.ParseTagValues(tvPairs) case v2_3.Version: data, err = v2_3_reader.ParseTagValues(tvPairs) default: return fmt.Errorf("unsupported SPDX version: '%v'", version) } if err != nil { return err } return convert.Document(data.(common.AnyDocument), doc) } tools-golang-0.5.5/tagvalue/reader/000077500000000000000000000000001463371440000172025ustar00rootroot00000000000000tools-golang-0.5.5/tagvalue/reader/reader.go000066400000000000000000000074761463371440000210110ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "bufio" "fmt" "io" "strings" "unicode" ) // TagValuePair is a convenience struct for a (tag, value) string pair. type TagValuePair struct { Tag string Value string } // ReadTagValues takes an io.Reader, scans it line by line and returns // a slice of {string, string} structs in the form {tag, value}. func ReadTagValues(content io.Reader) ([]TagValuePair, error) { r := &tvReader{} scanner := bufio.NewScanner(content) for scanner.Scan() { // read each line, one by one err := r.readNextLine(scanner.Text()) if err != nil { return nil, err } } if err := scanner.Err(); err != nil { return nil, err } // finalize and make sure all is well tvList, err := r.finalize() if err != nil { return nil, err } // convert internal format to exported TagValueList var exportedTVList []TagValuePair for _, tv := range tvList { tvPair := TagValuePair{Tag: tv.tag, Value: tv.value} exportedTVList = append(exportedTVList, tvPair) } return exportedTVList, nil } type tagvalue struct { tag string value string } type tvReader struct { midtext bool tvList []tagvalue currentLine int currentTag string currentValue string } func (reader *tvReader) finalize() ([]tagvalue, error) { if reader.midtext { return nil, fmt.Errorf("finalize called while still midtext parsing a text tag") } return reader.tvList, nil } func (reader *tvReader) readNextLine(line string) error { reader.currentLine++ if reader.midtext { return reader.readNextLineFromMidtext(line) } return reader.readNextLineFromReady(line) } func (reader *tvReader) readNextLineFromReady(line string) error { // strip whitespace from beginning of line line2 := strings.TrimLeftFunc(line, func(r rune) bool { return unicode.IsSpace(r) }) // ignore empty lines if line2 == "" { return nil } // ignore comment lines if strings.HasPrefix(line2, "#") { return nil } // split at colon substrings := strings.SplitN(line2, ":", 2) if len(substrings) == 1 { // error if a colon isn't found return fmt.Errorf("no colon found in '%s'", line) } // the first substring is the tag reader.currentTag = strings.TrimSpace(substrings[0]) // determine whether the value contains (or starts) a line substrings = strings.SplitN(substrings[1], "", 2) if len(substrings) == 1 { // no tag found means this is a single-line value // strip whitespace and use as a single line reader.currentValue = strings.TrimSpace(substrings[0]) } else { // there was a tag; now decide whether it's multi-line substrings = strings.SplitN(substrings[1], "", 2) if len(substrings) > 1 { // there is also a tag; take the middle part and // set as value reader.currentValue = substrings[0] } else { // there is no tag on this line; switch to midtext reader.currentValue = substrings[0] + "\n" reader.midtext = true return nil } } // if we got here, the value was on a single line // so go ahead and add it to the tag-value list tv := tagvalue{reader.currentTag, reader.currentValue} reader.tvList = append(reader.tvList, tv) // and reset reader.currentTag = "" reader.currentValue = "" return nil } func (reader *tvReader) readNextLineFromMidtext(line string) error { // look for whether the line closes here substrings := strings.SplitN(line, "", 2) if len(substrings) == 1 { // doesn't contain , so keep building the current value reader.currentValue += line + "\n" return nil } // contains , so end and record this pair reader.currentValue += substrings[0] tv := tagvalue{reader.currentTag, reader.currentValue} reader.tvList = append(reader.tvList, tv) // and reset reader.midtext = false reader.currentTag = "" reader.currentValue = "" return nil } tools-golang-0.5.5/tagvalue/reader/reader_test.go000066400000000000000000000430071463371440000220360ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package reader import ( "strings" "testing" ) func TestCanReadTagValues(t *testing.T) { sText := ` Tag1: Value1 Tag2: Value2 Tag3: line 1 line 2 # Comment Tag4: Value4 Tag5: Value 5 ` sReader := strings.NewReader(sText) tvPairList, err := ReadTagValues(sReader) if err != nil { t.Errorf("got error when calling ReadTagValues: %v", err) } if len(tvPairList) != 5 { t.Fatalf("expected len(tvPairList) to be 5, got %d", len(tvPairList)) } if tvPairList[0].Tag != "Tag1" { t.Errorf("expected tvPairList[0].Tag to be Tag1, got %s", tvPairList[0].Tag) } if tvPairList[0].Value != "Value1" { t.Errorf("expected tvPairList[0].Value to be Value1, got %s", tvPairList[0].Value) } if tvPairList[1].Tag != "Tag2" { t.Errorf("expected tvPairList[1].Tag to be Tag2, got %s", tvPairList[1].Tag) } if tvPairList[1].Value != "Value2" { t.Errorf("expected tvPairList[1].Value to be Value2, got %s", tvPairList[1].Value) } if tvPairList[2].Tag != "Tag3" { t.Errorf("expected tvPairList[2].Tag to be Tag3, got %s", tvPairList[2].Tag) } if tvPairList[2].Value != "line 1\n line 2" { t.Errorf("expected tvPairList[2].Value to be line 1\n line 2, got %s", tvPairList[2].Value) } if tvPairList[3].Tag != "Tag4" { t.Errorf("expected tvPairList[3].Tag to be Tag4, got %s", tvPairList[3].Tag) } if tvPairList[3].Value != "Value4" { t.Errorf("expected tvPairList[3].Value to be Value4, got %s", tvPairList[3].Value) } if tvPairList[4].Tag != "Tag5" { t.Errorf("expected tvPairList[4].Tag to be Tag5, got %s", tvPairList[4].Tag) } if tvPairList[4].Value != "Value 5" { t.Errorf("expected tvPairList[4].Value to be Value 5, got %s", tvPairList[4].Value) } } func TestCanGetTVListWithFinalize(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag:value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } tvList, err := reader.finalize() if err != nil { t.Errorf("got error when calling finalize: %v", err) } if len(tvList) != 1 || tvList[0].tag != "Tag" || tvList[0].value != "value" { t.Errorf("got invalid tag/value list: %v", tvList) } } func TestCanGetTVListIncludingMultilineWithFinalize(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag:value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } err = reader.readNextLine("rest of value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } tvList, err := reader.finalize() if err != nil { t.Errorf("got error when calling finalize: %v", err) } if len(tvList) != 1 || tvList[0].tag != "Tag" || tvList[0].value != "value\nrest of value" { t.Errorf("got invalid tag/value list: %v", tvList) } } func TestCannotFinalizeIfInMidtextState(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag:value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } _, err = reader.finalize() if err == nil { t.Errorf("should have gotten error when calling finalize midtext") } } func TestCurrentLineIncreasesOnEachReadCall(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag:value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } reader.currentLine = 23 err = reader.readNextLine("Tag:value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if reader.currentLine != 24 { t.Errorf("expected %d for currentLine, got %d", 23, reader.currentLine) } } func TestReadyCanReadSingleTagValue(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag:value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Tag" || reader.tvList[0].value != "value" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestReadyCanStripWhitespaceFromValue(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag: value ") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Tag" || reader.tvList[0].value != "value" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } } func TestReadyCannotReadLineWithNoColon(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("No colon should be an error") if err == nil { t.Errorf("should have gotten error when calling readNextLine") } } func TestReadyTextTagSwitchesToMidtext(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag: This begins a multiline value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if !reader.midtext { t.Errorf("expected midtext to be true, got false") } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "Tag" { t.Errorf("expected %s for currentTag, got %s", "Tag", reader.currentTag) } if reader.currentValue != "This begins a multiline value\n" { t.Errorf("expected %s for currentValue, got %s", "This begins a multiline value\n", reader.currentValue) } } func TestReadyTextTagAndClosingTagInOneLineFinishesRead(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag: Just one line") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Tag" || reader.tvList[0].value != "Just one line" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } if reader.midtext { t.Errorf("expected midtext to be false, got true") } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestCanReadMultilineTextAcrossThreeLines(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag: This value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } err = reader.readNextLine("is three") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } err = reader.readNextLine("lines long") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Tag" || reader.tvList[0].value != "This value\nis three\nlines long" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } if reader.midtext { t.Errorf("expected midtext to be false, got true") } if reader.currentLine != 3 { t.Errorf("expected %d for currentLine, got %d", 3, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestMidtextContinuesIfNoClosingText(t *testing.T) { reader := &tvReader{} reader.midtext = true reader.currentLine = 1 reader.currentTag = "Multiline" reader.currentValue = "First line\n" err := reader.readNextLine("Second line") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if !reader.midtext { t.Errorf("expected midtext to be true, got false") } if reader.currentLine != 2 { t.Errorf("expected %d for currentLine, got %d", 2, reader.currentLine) } if reader.currentTag != "Multiline" { t.Errorf("expected %s for currentTag, got %s", "Multiline", reader.currentTag) } if reader.currentValue != "First line\nSecond line\n" { t.Errorf("expected %s for currentValue, got %s", "First line\nSecond line\n", reader.currentValue) } } func TestMidtextFinishesIfReachingClosingText(t *testing.T) { reader := &tvReader{} reader.midtext = true reader.currentLine = 1 reader.currentTag = "Multiline" reader.currentValue = "First line\n" err := reader.readNextLine("Second line") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Multiline" || reader.tvList[0].value != "First line\nSecond line" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } if reader.midtext { t.Errorf("expected midtext to be false, got true") } if reader.currentLine != 2 { t.Errorf("expected %d for currentLine, got %d", 2, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestReadyIgnoresCommentLines(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("# this is a comment") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if reader.midtext { t.Errorf("expected midtext to be false, got true") } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestMidtextIncludesCommentLines(t *testing.T) { reader := &tvReader{} reader.midtext = true reader.currentLine = 1 reader.currentTag = "Multiline" reader.currentValue = "First line\n" err := reader.readNextLine("# This is part of multiline text") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if !reader.midtext { t.Errorf("expected midtext to be true, got false") } if reader.currentLine != 2 { t.Errorf("expected %d for currentLine, got %d", 2, reader.currentLine) } if reader.currentTag != "Multiline" { t.Errorf("expected %s for currentTag, got %s", "Multiline", reader.currentTag) } if reader.currentValue != "First line\n# This is part of multiline text\n" { t.Errorf("expected %s for currentValue, got %s", "First line\n# This is part of multiline text\n", reader.currentValue) } } func TestReadyIgnoresEmptyLines(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if reader.midtext { t.Errorf("expected midtext to be false, got true") } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestMidtextIncludesEmptyLines(t *testing.T) { reader := &tvReader{} reader.midtext = true reader.currentLine = 1 reader.currentTag = "Multiline" reader.currentValue = "First line\n" err := reader.readNextLine("") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if !reader.midtext { t.Errorf("expected midtext to be true, got false") } if reader.currentLine != 2 { t.Errorf("expected %d for currentLine, got %d", 2, reader.currentLine) } if reader.currentTag != "Multiline" { t.Errorf("expected %s for currentTag, got %s", "Multiline", reader.currentTag) } if reader.currentValue != "First line\n\n" { t.Errorf("expected %s for currentValue, got %s", "First line\n\n", reader.currentValue) } } func TestReadyIgnoresWhitespaceOnlyLines(t *testing.T) { reader := &tvReader{} err := reader.readNextLine(" \t\t\t ") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if reader.midtext { t.Errorf("expected midtext to be false, got true") } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestMidtextIncludesWhitespaceOnlyLines(t *testing.T) { reader := &tvReader{} reader.midtext = true reader.currentLine = 1 reader.currentTag = "Multiline" reader.currentValue = "First line\n" err := reader.readNextLine(" \t\t ") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if !reader.midtext { t.Errorf("expected midtext to be true, got false") } if reader.currentLine != 2 { t.Errorf("expected %d for currentLine, got %d", 2, reader.currentLine) } if reader.currentTag != "Multiline" { t.Errorf("expected %s for currentTag, got %s", "Multiline", reader.currentTag) } if reader.currentValue != "First line\n \t\t \n" { t.Errorf("expected %s for currentValue, got %s", "First line\n \t\t \n", reader.currentValue) } } func TestReadyIgnoresSpacesBeforeTag(t *testing.T) { reader := &tvReader{} err := reader.readNextLine(" \t Tag:value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Tag" || reader.tvList[0].value != "value" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestReadyIgnoresSpacesBeforeCommentLines(t *testing.T) { reader := &tvReader{} err := reader.readNextLine(" \t\t # this is a comment") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 0 { t.Errorf("expected empty tag/value list, got %v", reader.tvList) } if reader.midtext { t.Errorf("expected midtext to be false, got true") } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestReadyIgnoresSpacesBetweenTagAndColon(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag \t :value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Tag" || reader.tvList[0].value != "value" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestReadyIgnoresSpacesBetweenColonAndValue(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag: \t value") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Tag" || reader.tvList[0].value != "value" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } func TestReadyIgnoresSpacesAfterEndOfValue(t *testing.T) { reader := &tvReader{} err := reader.readNextLine("Tag:value \t ") if err != nil { t.Errorf("got error when calling readNextLine: %v", err) } if len(reader.tvList) != 1 || reader.tvList[0].tag != "Tag" || reader.tvList[0].value != "value" { t.Errorf("got invalid tag/value list: %v", reader.tvList) } if reader.currentLine != 1 { t.Errorf("expected %d for currentLine, got %d", 1, reader.currentLine) } if reader.currentTag != "" { t.Errorf("expected empty string for currentTag, got %s", reader.currentTag) } if reader.currentValue != "" { t.Errorf("expected empty string for currentValue, got %s", reader.currentValue) } } tools-golang-0.5.5/tagvalue/writer.go000066400000000000000000000022321463371440000176020ustar00rootroot00000000000000// Package tvsaver is used to save tools-golang data structures // as SPDX tag-value documents. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package tagvalue import ( "fmt" "io" "github.com/spdx/tools-golang/convert" "github.com/spdx/tools-golang/spdx/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" v2_1_writer "github.com/spdx/tools-golang/spdx/v2/v2_1/tagvalue/writer" "github.com/spdx/tools-golang/spdx/v2/v2_2" v2_2_writer "github.com/spdx/tools-golang/spdx/v2/v2_2/tagvalue/writer" "github.com/spdx/tools-golang/spdx/v2/v2_3" v2_3_writer "github.com/spdx/tools-golang/spdx/v2/v2_3/tagvalue/writer" ) // Write takes an io.Writer and an SPDX Document, // and writes it to the writer in tag-value format. It returns error // if any error is encountered. func Write(doc common.AnyDocument, w io.Writer) error { doc = convert.FromPtr(doc) switch doc := doc.(type) { case v2_1.Document: return v2_1_writer.RenderDocument(&doc, w) case v2_2.Document: return v2_2_writer.RenderDocument(&doc, w) case v2_3.Document: return v2_3_writer.RenderDocument(&doc, w) } return fmt.Errorf("unsupported document type: %s", convert.Describe(doc)) } tools-golang-0.5.5/testdata/000077500000000000000000000000001463371440000157415ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project1/000077500000000000000000000000001463371440000174705ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project1/emptyfile.testdata.txt000066400000000000000000000000001463371440000240250ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project1/file1.testdata.txt000066400000000000000000000000471463371440000230420ustar00rootroot00000000000000arbitrary content for testing purposes tools-golang-0.5.5/testdata/project1/file3.testdata.txt000066400000000000000000000000721463371440000230420ustar00rootroot00000000000000This file contains some sort of documentation or whatever tools-golang-0.5.5/testdata/project1/folder1/000077500000000000000000000000001463371440000210245ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project1/folder1/file4.testdata.txt000066400000000000000000000000501463371440000243730ustar00rootroot00000000000000blah blah blah this file has text in it tools-golang-0.5.5/testdata/project1/lastfile.testdata.txt000066400000000000000000000001741463371440000236460ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later somebody was kind enough to stick an spdx short-form ID in here tools-golang-0.5.5/testdata/project1/symbolic-link000077700000000000000000000000001463371440000255352file3.testdata.txtustar00rootroot00000000000000tools-golang-0.5.5/testdata/project2/000077500000000000000000000000001463371440000174715ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project2/folder/000077500000000000000000000000001463371440000207445ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project2/folder/has-one-id.py000066400000000000000000000001641463371440000232430ustar00rootroot00000000000000# a comment in the python file # SPDX-License-Identifier: MIT if __name__ == "__main__": print("Hello world!") tools-golang-0.5.5/testdata/project2/folder/has-trailing-comment-marker.c000066400000000000000000000002271463371440000264120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ # include int main(int argc, char *argv[]) { printf("Hello world!\n"); return 0; } tools-golang-0.5.5/testdata/project2/has-duplicate-ids.txt000066400000000000000000000001241463371440000235270ustar00rootroot00000000000000SPDX-License-Identifier: MIT hi SPDX-License-Identifier: MIT oops we repeated it tools-golang-0.5.5/testdata/project2/has-id.txt000066400000000000000000000001741463371440000214010ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later somebody was kind enough to stick an spdx short-form ID in here tools-golang-0.5.5/testdata/project2/has-multiple-ids.txt000066400000000000000000000004151463371440000234130ustar00rootroot00000000000000// SPDX-License-Identifier: (MIT AND BSD-3-Clause) OR ISC hey look it's a short-form ID // SPDX-License-Identifier: BSD-2-Clause well there's another one, I guess it applies to this content? // SPDX-License-Identifier: EPL-1.0+ and another one, with a + this time tools-golang-0.5.5/testdata/project2/no-id.txt000066400000000000000000000000521463371440000212350ustar00rootroot00000000000000this file doesn't have any short-form IDs tools-golang-0.5.5/testdata/project3/000077500000000000000000000000001463371440000174725ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/alsoEXCLUDEthis.txt000066400000000000000000000000001463371440000230610ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/dontscan.txt000066400000000000000000000000361463371440000220430ustar00rootroot00000000000000SPDX-License-Identifier: OOPS tools-golang-0.5.5/testdata/project3/excludedir/000077500000000000000000000000001463371440000216225ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/excludedir/no.txt000066400000000000000000000000001463371440000227650ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/ignoredir/000077500000000000000000000000001463371440000214545ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/ignoredir/notThisOne.txt000066400000000000000000000000001463371440000242750ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/ignorefile.txt000066400000000000000000000000001463371440000223440ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/keep.txt000066400000000000000000000000001463371440000211450ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/keep/000077500000000000000000000000001463371440000204165ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/keep/keep.txt000066400000000000000000000000351463371440000221010ustar00rootroot00000000000000SPDX-License-Identifier: MIT tools-golang-0.5.5/testdata/project3/subdir/000077500000000000000000000000001463371440000207625ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/subdir/ignoredir/000077500000000000000000000000001463371440000227445ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/subdir/ignoredir/notThisOneEither.txt000066400000000000000000000000001463371440000267260ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/subdir/ignorefile.txt000066400000000000000000000000001463371440000236340ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/subdir/keep/000077500000000000000000000000001463371440000217065ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project3/subdir/keep/dontscan.txt000066400000000000000000000000361463371440000242570ustar00rootroot00000000000000SPDX-License-Identifier: OOPS tools-golang-0.5.5/testdata/project3/subdir/keep/keep.txt000066400000000000000000000000351463371440000233710ustar00rootroot00000000000000SPDX-License-Identifier: MIT tools-golang-0.5.5/testdata/project4/000077500000000000000000000000001463371440000174735ustar00rootroot00000000000000tools-golang-0.5.5/testdata/project4/has-id-to-ignore.txt000066400000000000000000000003511463371440000233010ustar00rootroot00000000000000this file actually contains some code talking about IDs, and should be ignored if file.contains("SPDX-License-Identifier: GPL-2.0") { // do something... } We don't want to pick up that line as the short-form ID for this file. tools-golang-0.5.5/testdata/project4/has-id.txt000066400000000000000000000001741463371440000214030ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later somebody was kind enough to stick an spdx short-form ID in here tools-golang-0.5.5/testdata/project4/has-mix-of-ids.txt000066400000000000000000000004701463371440000227620ustar00rootroot00000000000000// SPDX-License-Identifier: MIT the short-form ID above is correct, and should be picked up. the following code is talking about IDs, and should be ignored if file.contains("SPDX-License-Identifier: GPL-2.0") { // do something... } We don't want to pick up that line as the short-form ID for this file. tools-golang-0.5.5/utils/000077500000000000000000000000001463371440000152705ustar00rootroot00000000000000tools-golang-0.5.5/utils/filesystem.go000066400000000000000000000070261463371440000200100ustar00rootroot00000000000000// Package utils contains various utility functions to support the // main tools-golang packages. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package utils import ( "crypto/md5" "crypto/sha1" "crypto/sha256" "fmt" "io" "os" "path" "path/filepath" "strings" ) // GetAllFilePaths takes a path to a directory (including an optional slice of // path patterns to ignore), and returns a slice of relative paths to all files // in that directory and its subdirectories (excluding those that are ignored). // These paths are always normalized to use URI-like forward-slashes but begin with / func GetAllFilePaths(dirRoot string, pathsIgnored []string) ([]string, error) { // paths is a _pointer_ to a slice -- not just a slice. // this is so that it can be appropriately modified by append // in the sub-function. paths := &[]string{} prefix := strings.TrimSuffix(filepath.ToSlash(dirRoot), "/") err := filepath.Walk(dirRoot, func(path string, fi os.FileInfo, err error) error { if err != nil { return err } // don't include path if it's a directory if fi.IsDir() { return nil } // don't include path if it's a symbolic link if fi.Mode()&os.ModeSymlink == os.ModeSymlink { return nil } path = filepath.ToSlash(path) shortPath := strings.TrimPrefix(path, prefix) // don't include path if it should be ignored if pathsIgnored != nil && ShouldIgnore(shortPath, pathsIgnored) { return nil } // if we got here, record the path *paths = append(*paths, shortPath) return nil }) return *paths, err } // GetHashesForFilePath takes a path to a file on disk, and returns // SHA1, SHA256 and MD5 hashes for that file as strings. func GetHashesForFilePath(p string) (string, string, string, error) { f, err := os.Open(filepath.FromSlash(p)) if err != nil { return "", "", "", err } defer f.Close() var ssha1, ssha256, smd5 string hSHA1 := sha1.New() hSHA256 := sha256.New() hMD5 := md5.New() hMulti := io.MultiWriter(hSHA1, hSHA256, hMD5) if _, err := io.Copy(hMulti, f); err != nil { f.Close() return "", "", "", err } ssha1 = fmt.Sprintf("%x", hSHA1.Sum(nil)) ssha256 = fmt.Sprintf("%x", hSHA256.Sum(nil)) smd5 = fmt.Sprintf("%x", hMD5.Sum(nil)) return ssha1, ssha256, smd5, nil } // ShouldIgnore compares a file path to a slice of file path patterns, // and determines whether that file should be ignored because it matches // any of those patterns. func ShouldIgnore(fileName string, pathsIgnored []string) bool { fDirs, fFile := path.Split(fileName) for _, pattern := range pathsIgnored { // split into dir(s) and filename patternDirs, patternFile := path.Split(pattern) patternDirStars := strings.HasPrefix(patternDirs, "**") if patternDirStars { patternDirs = patternDirs[2:] } // case 1: specific file if !patternDirStars && patternDirs == fDirs && patternFile != "" && patternFile == fFile { return true } // case 2: all files in specific directory if !patternDirStars && strings.HasPrefix(fDirs, patternDirs) && patternFile == "" { return true } // case 3: specific file in any dir if patternDirStars && patternDirs == "/" && patternFile != "" && patternFile == fFile { return true } // case 4: specific file in any matching subdir if patternDirStars && strings.Contains(fDirs, patternDirs) && patternFile != "" && patternFile == fFile { return true } // case 5: any file in any matching subdir if patternDirStars && strings.Contains(fDirs, patternDirs) && patternFile == "" { return true } } // if no match, don't ignore return false } tools-golang-0.5.5/utils/filesystem_test.go000066400000000000000000000166071463371440000210540ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package utils import ( "strings" "testing" ) // ===== Filesystem and hash functionality tests ===== func TestFilesystemCanGetSliceOfFolderContents(t *testing.T) { dirRoot := "../testdata/project1/" filePaths, err := GetAllFilePaths(dirRoot, nil) if err != nil { t.Fatalf("expected filePaths, got error: %v", err) } if filePaths == nil { t.Fatalf("expected non-nil filePaths, got nil") } // should only be 5 files // symbolic link in project1/symbolic-link should be ignored if len(filePaths) != 5 { t.Fatalf("expected %v, got %v", 5, len(filePaths)) } // should be in alphabetical order, with files prefixed with '/' if filePaths[0] != "/emptyfile.testdata.txt" { t.Errorf("expected %v, got %v", "/emptyfile.testdata.txt", filePaths[0]) } if filePaths[1] != "/file1.testdata.txt" { t.Errorf("expected %v, got %v", "/file1.testdata.txt", filePaths[1]) } if filePaths[2] != "/file3.testdata.txt" { t.Errorf("expected %v, got %v", "/file3.testdata.txt", filePaths[2]) } if filePaths[3] != "/folder1/file4.testdata.txt" { t.Errorf("expected %v, got %v", "/folder1/file4.testdata.txt", filePaths[3]) } if filePaths[4] != "/lastfile.testdata.txt" { t.Errorf("expected %v, got %v", "/lastfile.testdata.txt", filePaths[4]) } } func TestFilesystemGetAllFilePathsFailsForNonExistentDirectory(t *testing.T) { dirRoot := "./does/not/exist/" _, err := GetAllFilePaths(dirRoot, nil) if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestFilesystemCanIgnoreFilesWhenGettingFilePaths(t *testing.T) { dirRoot := "../testdata/project3/" pathsIgnored := []string{ "**/ignoredir/", "/excludedir/", "**/ignorefile.txt", "/alsoEXCLUDEthis.txt", } filePaths, err := GetAllFilePaths(dirRoot, pathsIgnored) if err != nil { t.Fatalf("expected filePaths, got error: %v", err) } if filePaths == nil { t.Fatalf("expected non-nil filePaths, got nil") } // should only be 5 files if len(filePaths) != 5 { t.Fatalf("expected %v, got %v", 5, len(filePaths)) } // should be in alphabetical order, with files prefixed with '/' if filePaths[0] != "/dontscan.txt" { t.Errorf("expected %v, got %v", "/dontscan.txt", filePaths[0]) } if filePaths[1] != "/keep/keep.txt" { t.Errorf("expected %v, got %v", "/keep/keep.txt", filePaths[1]) } if filePaths[2] != "/keep.txt" { t.Errorf("expected %v, got %v", "/keep.txt", filePaths[2]) } if filePaths[3] != "/subdir/keep/dontscan.txt" { t.Errorf("expected %v, got %v", "/subdir/keep/dontscan.txt", filePaths[3]) } if filePaths[4] != "/subdir/keep/keep.txt" { t.Errorf("expected %v, got %v", "/subdir/keep/keep.txt", filePaths[4]) } } // FIXME add test to make sure we get an error for a directory without // FIXME appropriate permissions to read its (sub)contents func TestFilesystemGetsHashesForFilePath(t *testing.T) { f := "../testdata/project1/file1.testdata.txt" ssha1, ssha256, smd5, err := GetHashesForFilePath(f) if err != nil { t.Fatalf("expected nil error, got %v", err) } if ssha1 != "024f870eb6323f532515f7a09d5646a97083b819" { t.Errorf("expected %v, got %v", "024f870eb6323f532515f7a09d5646a97083b819", ssha1) } if ssha256 != "b14e44284ca477b4c0db34b15ca4c454b2947cce7883e22321cf2984050e15bf" { t.Errorf("expected %v, got %v", "b14e44284ca477b4c0db34b15ca4c454b2947cce7883e22321cf2984050e15bf", ssha256) } if smd5 != "37c8208479dfe42d2bb29debd6e32d4a" { t.Errorf("expected %v, got %v", "37c8208479dfe42d2bb29debd6e32d4a", smd5) } } func TestFilesystemGetsErrorWhenRequestingHashesForInvalidFilePath(t *testing.T) { f := "./does/not/exist" _, _, _, err := GetHashesForFilePath(f) if err == nil { t.Errorf("expected non-nil error, got nil") } } // FIXME add test to make sure we get an error for hashes for a file without // FIXME appropriate permissions to read its contents func TestFilesystemExcludesForIgnoredPaths(t *testing.T) { // one specific file pathsIgnored := []string{"/file.txt"} fileName := "/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/fileNope.txt" if ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } // two specific files pathsIgnored = []string{"/file.txt", "/file2.txt"} fileName = "/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/fileNope.txt" if ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/file2.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } // one specific file in specific subdirectory pathsIgnored = []string{"/subdir/file.txt"} fileName = "/subdir/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/file.txt" if ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/something/subdir/file.txt" if ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } // one specific file in any directory pathsIgnored = []string{"**/file.txt"} fileName = "/subdir/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/something/subdir/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/something/fileNope.txt" if ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } // all files in one specific subdirectory (and its subdirectories) pathsIgnored = []string{"/subdir/"} fileName = "/subdir/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/file.txt" if ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/subdir/sub2/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/nope/subdir/file.txt" if ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } // all files in subdirectory with this name, wherever it appears pathsIgnored = []string{"**/subdir/"} fileName = "/subdir/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/file.txt" if ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/subdir/sub2/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } fileName = "/nope/subdir/file.txt" if !ShouldIgnore(fileName, pathsIgnored) { t.Errorf("incorrect for %v, ignoring %v", fileName, pathsIgnored) } } func FuzzShouldIgnore(f *testing.F) { f.Fuzz(func(t *testing.T, fileName string, pathsIgnored string) { ShouldIgnore(fileName, strings.Fields(pathsIgnored)) }) } tools-golang-0.5.5/utils/verification.go000066400000000000000000000030011463371440000202730ustar00rootroot00000000000000// Package utils contains various utility functions to support the // main tools-golang packages. // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package utils import ( "crypto/sha1" "fmt" "sort" "strings" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) // GetVerificationCode takes a slice of files and an optional filename // for an "excludes" file, and returns a Package Verification Code calculated // according to SPDX spec version 2.3, section 3.9.4. func GetVerificationCode(files []*spdx.File, excludeFile string) (common.PackageVerificationCode, error) { // create slice of strings - unsorted SHA1s for all files shas := []string{} for i, f := range files { if f == nil { return common.PackageVerificationCode{}, fmt.Errorf("got nil file for identifier %v", i) } if f.FileName != excludeFile { // find the SHA1 hash, if present for _, checksum := range f.Checksums { if checksum.Algorithm == common.SHA1 { shas = append(shas, checksum.Value) } } } } // sort the strings sort.Strings(shas) // concatenate them into one string, with no trailing separators shasConcat := strings.Join(shas, "") // and get its SHA1 value hsha1 := sha1.New() hsha1.Write([]byte(shasConcat)) bs := hsha1.Sum(nil) var excludedFiles []string if excludeFile != "" { excludedFiles = []string{excludeFile} } code := common.PackageVerificationCode{ Value: fmt.Sprintf("%x", bs), ExcludedFiles: excludedFiles, } return code, nil } tools-golang-0.5.5/utils/verification_test.go000066400000000000000000000105021463371440000213360ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package utils import ( "strings" "testing" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" ) func TestPackageCanGetVerificationCode(t *testing.T) { files := []*spdx.File{ { FileName: "file2.txt", FileSPDXIdentifier: "File0", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd", }, }, }, { FileName: "file1.txt", FileSPDXIdentifier: "File1", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "3333333333bbbbbbbbbbccccccccccdddddddddd", }, }, }, { FileName: "file3.txt", FileSPDXIdentifier: "File2", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "8888888888bbbbbbbbbbccccccccccdddddddddd", }, }, }, { FileName: "file5.txt", FileSPDXIdentifier: "File3", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "2222222222bbbbbbbbbbccccccccccdddddddddd", }, }, }, { FileName: "file4.txt", FileSPDXIdentifier: "File4", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "bbbbbbbbbbccccccccccddddddddddaaaaaaaaaa", }, }, }, } wantCode := common.PackageVerificationCode{Value: "ac924b375119c81c1f08c3e2722044bfbbdcd3dc"} gotCode, err := GetVerificationCode(files, "") if err != nil { t.Fatalf("expected nil error, got %v", err) } if wantCode.Value != gotCode.Value { t.Errorf("expected %v, got %v", wantCode, gotCode) } } func TestPackageCanGetVerificationCodeIgnoringExcludesFile(t *testing.T) { files := []*spdx.File{ { FileName: "file1.txt", FileSPDXIdentifier: "File0", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd", }, }, }, { FileName: "file2.txt", FileSPDXIdentifier: "File1", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "3333333333bbbbbbbbbbccccccccccdddddddddd", }, }, }, { FileName: "thisfile.spdx", FileSPDXIdentifier: "File2", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "bbbbbbbbbbccccccccccddddddddddaaaaaaaaaa", }, }, }, { FileName: "file3.txt", FileSPDXIdentifier: "File3", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "8888888888bbbbbbbbbbccccccccccdddddddddd", }, }, }, { FileName: "file4.txt", FileSPDXIdentifier: "File4", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "2222222222bbbbbbbbbbccccccccccdddddddddd", }, }, }, } wantCode := common.PackageVerificationCode{Value: "17fab1bd18fe5c13b5d3983f1c17e5f88b8ff266"} gotCode, err := GetVerificationCode(files, "thisfile.spdx") if err != nil { t.Fatalf("expected nil error, got %v", err) } if wantCode.Value != gotCode.Value { t.Errorf("expected %v, got %v", wantCode, gotCode) } } func TestPackageGetVerificationCodeFailsIfNilFileInSlice(t *testing.T) { files := []*spdx.File{ { FileName: "file2.txt", FileSPDXIdentifier: "File0", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd", }, }, }, nil, { FileName: "file3.txt", FileSPDXIdentifier: "File2", Checksums: []common.Checksum{ { Algorithm: common.SHA1, Value: "8888888888bbbbbbbbbbccccccccccdddddddddd", }, }, }, } _, err := GetVerificationCode(files, "") if err == nil { t.Fatalf("expected non-nil error, got nil") } } func FuzzPackageCanGetVerificationCode(f *testing.F) { f.Fuzz(func(t *testing.T, filename string, fileSPDXIdentifier string, checksums string) { checks := []common.Checksum{} for _, check := range strings.Fields(checksums) { checks = append(checks, common.Checksum{Algorithm: common.SHA1, Value: check}) } files := []*spdx.File{ { FileName: filename, FileSPDXIdentifier: common.ElementID(fileSPDXIdentifier), Checksums: checks, }, } _, err := GetVerificationCode(files, "") if err != nil { t.Fatal(err) } }) } tools-golang-0.5.5/yaml/000077500000000000000000000000001463371440000150725ustar00rootroot00000000000000tools-golang-0.5.5/yaml/reader.go000066400000000000000000000035211463371440000166640ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package yaml import ( "bytes" "fmt" "io" "sigs.k8s.io/yaml" "github.com/spdx/tools-golang/convert" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" "github.com/spdx/tools-golang/spdx/v2/v2_2" "github.com/spdx/tools-golang/spdx/v2/v2_3" ) // Read takes an io.Reader and returns a fully-parsed current model SPDX Document // or an error if any error is encountered. func Read(content io.Reader) (*spdx.Document, error) { doc := spdx.Document{} err := ReadInto(content, &doc) return &doc, err } // ReadInto takes an io.Reader, reads in the SPDX document at the version provided // and converts to the doc version func ReadInto(content io.Reader, doc common.AnyDocument) error { if !convert.IsPtr(doc) { return fmt.Errorf("doc to read into must be a pointer") } buf := new(bytes.Buffer) _, err := buf.ReadFrom(content) if err != nil { return err } var data interface{} err = yaml.Unmarshal(buf.Bytes(), &data) if err != nil { return err } val, ok := data.(map[string]interface{}) if !ok { return fmt.Errorf("not a valid SPDX YAML document") } version, ok := val["spdxVersion"] if !ok { return fmt.Errorf("YAML document does not contain spdxVersion field") } switch version { case v2_1.Version: var doc v2_1.Document err = yaml.Unmarshal(buf.Bytes(), &doc) if err != nil { return err } data = doc case v2_2.Version: var doc v2_2.Document err = yaml.Unmarshal(buf.Bytes(), &doc) if err != nil { return err } data = doc case v2_3.Version: var doc v2_3.Document err = yaml.Unmarshal(buf.Bytes(), &doc) if err != nil { return err } data = doc default: return fmt.Errorf("unsupported SDPX version: %s", version) } return convert.Document(data, doc) } tools-golang-0.5.5/yaml/writer.go000066400000000000000000000007061463371440000167400ustar00rootroot00000000000000// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package yaml import ( "io" "sigs.k8s.io/yaml" "github.com/spdx/tools-golang/spdx/common" ) // Write takes an SPDX Document and an io.Writer, and writes the document to the writer in YAML format. func Write(doc common.AnyDocument, w io.Writer) error { buf, err := yaml.Marshal(doc) if err != nil { return err } _, err = w.Write(buf) if err != nil { return err } return nil }