diff --git a/src/packagedcode/__init__.py b/src/packagedcode/__init__.py index 9cc46d0e09..fb3b09fd08 100644 --- a/src/packagedcode/__init__.py +++ b/src/packagedcode/__init__.py @@ -15,6 +15,7 @@ from packagedcode import build_gradle from packagedcode import cargo from packagedcode import chef +from packagedcode import componentjs from packagedcode import debian from packagedcode import debian_copyright from packagedcode import distro @@ -81,7 +82,7 @@ conan.ConanDataHandler, cran.CranDescriptionFileHandler, - + componentjs.ComponentJSONMetadataHandler, debian_copyright.DebianCopyrightFileInPackageHandler, debian_copyright.StandaloneDebianCopyrightFileHandler, debian.DebianDscFileHandler, diff --git a/src/packagedcode/componentjs.py b/src/packagedcode/componentjs.py new file mode 100644 index 0000000000..3d8c7f13aa --- /dev/null +++ b/src/packagedcode/componentjs.py @@ -0,0 +1,154 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# ScanCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/nexB/scancode-toolkit for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +import json +from packagedcode import models +from packageurl import PackageURL +import yaml + +class ComponentJSONMetadataHandler(models.NonAssemblableDatafileHandler): + """ + Handle component JSON metadata files for package analysis. + """ + datasource_id = "component_json_metadata" + path_patterns = ("*component.json",) + default_package_type = "library" + description = "component JSON package metadata file" + + @classmethod + def parse(cls, location, package_only=False): + """ + Parse the JSON metadata file at `location` and yield PackageData. + """ + with open(location, "r", encoding="utf-8") as f: + data = json.load(f) + + name = data.get('name') or data.get('repo', '').split('/')[-1] + if not name: + return + + namespace = None + if 'repo' in data and '/' in data['repo']: + namespace, name = data['repo'].split('/', 1) + + package_data = dict( + datasource_id=cls.datasource_id, + type=cls.default_package_type, + name=name, + namespace=namespace, + version=data.get('version'), + description=data.get('description', ''), + homepage_url=cls._extract_homepage(data), + keywords=data.get('keywords', []), + dependencies=cls._process_dependencies(data), + extracted_license_statement=cls._extract_license_statement(data), + extra_data=cls._extract_extra_data(data) + ) + + if namespace and name: + package_data['purl'] = PackageURL( + type='generic', + namespace=namespace, + name=name, + version=package_data.get('version') + ).to_string() + + + yield models.PackageData.from_data(package_data, package_only) + + @staticmethod + def _extract_homepage(data): + """ + Extract homepage URL from various possible sources. + """ + if data.get('homepage'): + return data['homepage'] + + if data.get('repo'): + return f'https://github.com/{data["repo"]}' + + desc = data.get('description', '') + if 'http' in desc: + urls = [word for word in desc.split() if word.startswith('http')] + return urls[0] if urls else None + + return None + + @staticmethod + def _process_dependencies(data): + """ + Process dependencies into DependentPackage objects. + """ + dependencies = [] + + for dep_name, dep_version in data.get('dependencies', {}).items(): + try: + if '/' in dep_name: + namespace, name = dep_name.split('/', 1) + else: + namespace, name = None, dep_name + + purl = PackageURL( + type='generic', + namespace=namespace, + name=name, + version=dep_version + ).to_string() + + dependencies.append( + models.DependentPackage( + purl=purl, + scope='runtime', + is_runtime=True, + is_optional=False + ) + ) + except Exception: + continue + + return dependencies + + @classmethod + def _extract_license_statement(cls, data): + """ + Extract license statement. + + """ + license_field = data.get('license') + if not license_field: + return None + + if isinstance(license_field, str): + return yaml.dump({"type": license_field.strip()}).strip() + + if isinstance(license_field, list): + license_statements = [ + yaml.dump({"type": lic.strip()}).strip() + for lic in license_field + if lic.strip() + ] + return "\n".join(license_statements) if license_statements else None + + return None + + @staticmethod + def _extract_extra_data(data): + """ + Extract additional metadata not in core package data. + """ + extra_fields = [ + 'main', 'scripts', 'styles', 'bin', + 'repository', 'private', 'dev', 'development' + ] + + return { + field: data[field] + for field in extra_fields + if field in data + } diff --git a/tests/packagedcode/data/componentjs/angular-ui-sortable/component.json b/tests/packagedcode/data/componentjs/angular-ui-sortable/component.json new file mode 100644 index 0000000000..282aa5d737 --- /dev/null +++ b/tests/packagedcode/data/componentjs/angular-ui-sortable/component.json @@ -0,0 +1,25 @@ +{ + "name": "angular-ui-sortable", + "version": "0.0.1", + "description": "This directive allows you to jQueryUI Sortable.", + "author": "https://github.com/angular-ui/ui-sortable/graphs/contributors", + "license": "MIT", + "homepage": "http://angular-ui.github.com", + "main": "./src/sortable.js", + "ignore": [ + "**/.*", + "node_modules", + "components", + "test*", + "demo*", + "gruntFile.js", + "package.json" + ], + "dependencies": { + "angular": "~1.x", + "jquery-ui": ">= 1.9" + }, + "devDependencies": { + "angular-mocks": "~1.x" + } + } \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/angular-ui-sortable/expectedoutput.json b/tests/packagedcode/data/componentjs/angular-ui-sortable/expectedoutput.json new file mode 100644 index 0000000000..39d51f3708 --- /dev/null +++ b/tests/packagedcode/data/componentjs/angular-ui-sortable/expectedoutput.json @@ -0,0 +1,106 @@ +{ + "packages": [], + "dependencies": [], + "files": [ + { + "path": "component.json", + "type": "file", + "package_data": [ + { + "type": "generic", + "namespace": null, + "name": "angular-ui-sortable", + "version": "0.0.1", + "qualifiers": {}, + "subpath": null, + "primary_language": null, + "description": "This directive allows you to jQueryUI Sortable.", + "release_date": null, + "parties": [], + "keywords": [], + "homepage_url": "http://angular-ui.github.com", + "download_url": null, + "size": null, + "sha1": null, + "md5": null, + "sha256": null, + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "holder": null, + "declared_license_expression": "mit", + "declared_license_expression_spdx": "MIT", + "license_detections": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "matches": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "from_file": "component.json", + "start_line": 1, + "end_line": 1, + "matcher": "1-hash", + "score": 16.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 16, + "rule_identifier": "mit_1301.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_1301.RULE", + "matched_text": "license type: MIT" + } + ], + "identifier": "mit-1c9cba21-81d2-7522-ac3e-dfde6630f8d1" + } + ], + "other_license_expression": null, + "other_license_expression_spdx": null, + "other_license_detections": [], + "extracted_license_statement": "type: MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "is_private": false, + "is_virtual": false, + "extra_data": { + "main": "./src/sortable.js" + }, + "dependencies": [ + { + "purl": "pkg:generic/angular@~1.x", + "extracted_requirement": null, + "scope": "runtime", + "is_runtime": true, + "is_optional": false, + "is_pinned": false, + "is_direct": true, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:generic/jquery-ui@%3E%3D%201.9", + "extracted_requirement": null, + "scope": "runtime", + "is_runtime": true, + "is_optional": false, + "is_pinned": false, + "is_direct": true, + "resolved_package": {}, + "extra_data": {} + } + ], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": null, + "datasource_id": "component_json_metadata", + "purl": "pkg:generic/angular-ui-sortable@0.0.1" + } + ], + "for_packages": [], + "scan_errors": [] + } + ] +} \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/chai/component.json b/tests/packagedcode/data/componentjs/chai/component.json new file mode 100644 index 0000000000..660ea92d2f --- /dev/null +++ b/tests/packagedcode/data/componentjs/chai/component.json @@ -0,0 +1,51 @@ +{ + "name": "chai" + , "repo": "chaijs/chai" + , "version": "2.1.2" + , "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic." + , "license": "MIT" + , "keywords": [ + "test" + , "assertion" + , "assert" + , "testing" + , "chai" + ] + , "main": "index.js" + , "scripts": [ + "index.js" + , "lib/chai.js" + , "lib/chai/assertion.js" + , "lib/chai/config.js" + , "lib/chai/core/assertions.js" + , "lib/chai/interface/assert.js" + , "lib/chai/interface/expect.js" + , "lib/chai/interface/should.js" + , "lib/chai/utils/addChainableMethod.js" + , "lib/chai/utils/addMethod.js" + , "lib/chai/utils/addProperty.js" + , "lib/chai/utils/flag.js" + , "lib/chai/utils/getActual.js" + , "lib/chai/utils/getEnumerableProperties.js" + , "lib/chai/utils/getMessage.js" + , "lib/chai/utils/getName.js" + , "lib/chai/utils/getPathValue.js" + , "lib/chai/utils/getPathInfo.js" + , "lib/chai/utils/hasProperty.js" + , "lib/chai/utils/getProperties.js" + , "lib/chai/utils/index.js" + , "lib/chai/utils/inspect.js" + , "lib/chai/utils/objDisplay.js" + , "lib/chai/utils/overwriteMethod.js" + , "lib/chai/utils/overwriteProperty.js" + , "lib/chai/utils/overwriteChainableMethod.js" + , "lib/chai/utils/test.js" + , "lib/chai/utils/transferFlags.js" + , "lib/chai/utils/type.js" + ] + , "dependencies": { + "chaijs/assertion-error": "1.0.0" + , "chaijs/deep-eql": "0.1.3" + } + , "development": {} +} \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/chai/expectedoutput.json b/tests/packagedcode/data/componentjs/chai/expectedoutput.json new file mode 100644 index 0000000000..3a73ad2448 --- /dev/null +++ b/tests/packagedcode/data/componentjs/chai/expectedoutput.json @@ -0,0 +1,144 @@ +{ + "packages": [], + "dependencies": [], + "files": [ + { + "path": "component.json", + "type": "file", + "package_data": [ + { + "type": "generic", + "namespace": "chaijs", + "name": "chai", + "version": "2.1.2", + "qualifiers": {}, + "subpath": null, + "primary_language": null, + "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", + "release_date": null, + "parties": [], + "keywords": [ + "test", + "assertion", + "assert", + "testing", + "chai" + ], + "homepage_url": "https://github.com/chaijs/chai", + "download_url": null, + "size": null, + "sha1": null, + "md5": null, + "sha256": null, + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "holder": null, + "declared_license_expression": "mit", + "declared_license_expression_spdx": "MIT", + "license_detections": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "matches": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "from_file": "component.json", + "start_line": 1, + "end_line": 1, + "matcher": "1-hash", + "score": 16.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 16, + "rule_identifier": "mit_1301.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_1301.RULE", + "matched_text": "license type: MIT" + } + ], + "identifier": "mit-1c9cba21-81d2-7522-ac3e-dfde6630f8d1" + } + ], + "other_license_expression": null, + "other_license_expression_spdx": null, + "other_license_detections": [], + "extracted_license_statement": "type: MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "is_private": false, + "is_virtual": false, + "extra_data": { + "main": "index.js", + "scripts": [ + "index.js", + "lib/chai.js", + "lib/chai/assertion.js", + "lib/chai/config.js", + "lib/chai/core/assertions.js", + "lib/chai/interface/assert.js", + "lib/chai/interface/expect.js", + "lib/chai/interface/should.js", + "lib/chai/utils/addChainableMethod.js", + "lib/chai/utils/addMethod.js", + "lib/chai/utils/addProperty.js", + "lib/chai/utils/flag.js", + "lib/chai/utils/getActual.js", + "lib/chai/utils/getEnumerableProperties.js", + "lib/chai/utils/getMessage.js", + "lib/chai/utils/getName.js", + "lib/chai/utils/getPathValue.js", + "lib/chai/utils/getPathInfo.js", + "lib/chai/utils/hasProperty.js", + "lib/chai/utils/getProperties.js", + "lib/chai/utils/index.js", + "lib/chai/utils/inspect.js", + "lib/chai/utils/objDisplay.js", + "lib/chai/utils/overwriteMethod.js", + "lib/chai/utils/overwriteProperty.js", + "lib/chai/utils/overwriteChainableMethod.js", + "lib/chai/utils/test.js", + "lib/chai/utils/transferFlags.js", + "lib/chai/utils/type.js" + ], + "development": {} + }, + "dependencies": [ + { + "purl": "pkg:generic/chaijs/assertion-error@1.0.0", + "extracted_requirement": null, + "scope": "runtime", + "is_runtime": true, + "is_optional": false, + "is_pinned": false, + "is_direct": true, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:generic/chaijs/deep-eql@0.1.3", + "extracted_requirement": null, + "scope": "runtime", + "is_runtime": true, + "is_optional": false, + "is_pinned": false, + "is_direct": true, + "resolved_package": {}, + "extra_data": {} + } + ], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": null, + "datasource_id": "component_json_metadata", + "purl": "pkg:generic/chaijs/chai@2.1.2" + } + ], + "for_packages": [], + "scan_errors": [] + } + ] +} \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/jszip/component.json b/tests/packagedcode/data/componentjs/jszip/component.json new file mode 100644 index 0000000000..aa8da9c196 --- /dev/null +++ b/tests/packagedcode/data/componentjs/jszip/component.json @@ -0,0 +1,16 @@ +{ + "name": "jszip", + "repo": "Stuk/jszip", + "description": "Create, read and edit .zip files with JavaScript http://stuartk.com/jszip", + "version": "3.2.0", + "keywords": [ + "zip", + "deflate", + "inflate" + ], + "main": "dist/jszip.js", + "license": "MIT or GPLv3", + "scripts": [ + "dist/jszip.js" + ] + } \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/jszip/expectedoutput.json b/tests/packagedcode/data/componentjs/jszip/expectedoutput.json new file mode 100644 index 0000000000..e245a594f5 --- /dev/null +++ b/tests/packagedcode/data/componentjs/jszip/expectedoutput.json @@ -0,0 +1,90 @@ +{ + "packages": [], + "dependencies": [], + "files": [ + { + "path": "component.json", + "type": "file", + "package_data": [ + { + "type": "generic", + "namespace": "Stuk", + "name": "jszip", + "version": "3.2.0", + "qualifiers": {}, + "subpath": null, + "primary_language": null, + "description": "Create, read and edit .zip files with JavaScript http://stuartk.com/jszip", + "release_date": null, + "parties": [], + "keywords": [ + "zip", + "deflate", + "inflate" + ], + "homepage_url": "https://github.com/Stuk/jszip", + "download_url": null, + "size": null, + "sha1": null, + "md5": null, + "sha256": null, + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "holder": null, + "declared_license_expression": "mit OR gpl-3.0", + "declared_license_expression_spdx": "MIT OR GPL-3.0-only", + "license_detections": [ + { + "license_expression": "mit OR gpl-3.0", + "license_expression_spdx": "MIT OR GPL-3.0-only", + "matches": [ + { + "license_expression": "mit OR gpl-3.0", + "license_expression_spdx": "MIT OR GPL-3.0-only", + "from_file": "component.json", + "start_line": 1, + "end_line": 1, + "matcher": "2-aho", + "score": 100.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 100, + "rule_identifier": "mit_or_gpl-3.0_1.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_or_gpl-3.0_1.RULE", + "matched_text": "type: MIT or GPLv3" + } + ], + "identifier": "mit_or_gpl_3_0-9dbb60be-81c9-331c-96a6-8e6723aa5ce9" + } + ], + "other_license_expression": null, + "other_license_expression_spdx": null, + "other_license_detections": [], + "extracted_license_statement": "type: MIT or GPLv3", + "notice_text": null, + "source_packages": [], + "file_references": [], + "is_private": false, + "is_virtual": false, + "extra_data": { + "main": "dist/jszip.js", + "scripts": [ + "dist/jszip.js" + ] + }, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": null, + "datasource_id": "component_json_metadata", + "purl": "pkg:generic/Stuk/jszip@3.2.0" + } + ], + "for_packages": [], + "scan_errors": [] + } + ] +} \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/knockback/component.json b/tests/packagedcode/data/componentjs/knockback/component.json new file mode 100644 index 0000000000..cd0716092d --- /dev/null +++ b/tests/packagedcode/data/componentjs/knockback/component.json @@ -0,0 +1,18 @@ +{ + "name": "knockback", + "author": "Kevin Malakoff (https://github.com/kmalakoff)", + "version": "1.2.3", + "description": "Knockback.js provides Knockout.js magic for Backbone.js Models and Collections", + "keywords" : ["knockback", "knockbackjs", "backbone", "backbonejs", "knockout", "knockoutjs"], + "repo": "kmalakoff/knockback", + "dependencies": { + "jashkenas/underscore": "*", + "jashkenas/backbone": "*", + "kmalakoff/knockout": "*" + }, + "main": "knockback.js", + "scripts": [ + "knockback.js" + ], + "license": "MIT" + } \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/knockback/expectedoutput.json b/tests/packagedcode/data/componentjs/knockback/expectedoutput.json new file mode 100644 index 0000000000..d86c74c756 --- /dev/null +++ b/tests/packagedcode/data/componentjs/knockback/expectedoutput.json @@ -0,0 +1,127 @@ +{ + "packages": [], + "dependencies": [], + "files": [ + { + "path": "component.json", + "type": "file", + "package_data": [ + { + "type": "generic", + "namespace": "kmalakoff", + "name": "knockback", + "version": "1.2.3", + "qualifiers": {}, + "subpath": null, + "primary_language": null, + "description": "Knockback.js provides Knockout.js magic for Backbone.js Models and Collections", + "release_date": null, + "parties": [], + "keywords": [ + "knockback", + "knockbackjs", + "backbone", + "backbonejs", + "knockout", + "knockoutjs" + ], + "homepage_url": "https://github.com/kmalakoff/knockback", + "download_url": null, + "size": null, + "sha1": null, + "md5": null, + "sha256": null, + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "holder": null, + "declared_license_expression": "mit", + "declared_license_expression_spdx": "MIT", + "license_detections": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "matches": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "from_file": "component.json", + "start_line": 1, + "end_line": 1, + "matcher": "1-hash", + "score": 16.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 16, + "rule_identifier": "mit_1301.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_1301.RULE", + "matched_text": "license type: MIT" + } + ], + "identifier": "mit-1c9cba21-81d2-7522-ac3e-dfde6630f8d1" + } + ], + "other_license_expression": null, + "other_license_expression_spdx": null, + "other_license_detections": [], + "extracted_license_statement": "type: MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "is_private": false, + "is_virtual": false, + "extra_data": { + "main": "knockback.js", + "scripts": [ + "knockback.js" + ] + }, + "dependencies": [ + { + "purl": "pkg:generic/jashkenas/underscore@%2A", + "extracted_requirement": null, + "scope": "runtime", + "is_runtime": true, + "is_optional": false, + "is_pinned": false, + "is_direct": true, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:generic/jashkenas/backbone@%2A", + "extracted_requirement": null, + "scope": "runtime", + "is_runtime": true, + "is_optional": false, + "is_pinned": false, + "is_direct": true, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:generic/kmalakoff/knockout@%2A", + "extracted_requirement": null, + "scope": "runtime", + "is_runtime": true, + "is_optional": false, + "is_pinned": false, + "is_direct": true, + "resolved_package": {}, + "extra_data": {} + } + ], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": null, + "datasource_id": "component_json_metadata", + "purl": "pkg:generic/kmalakoff/knockback@1.2.3" + } + ], + "for_packages": [], + "scan_errors": [] + } + ] +} \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/seedrandom/component.json b/tests/packagedcode/data/componentjs/seedrandom/component.json new file mode 100644 index 0000000000..67ab013594 --- /dev/null +++ b/tests/packagedcode/data/componentjs/seedrandom/component.json @@ -0,0 +1,10 @@ +{ + "name": "seedrandom", + "version": "2.3.10", + "description": "Seeded random number generator for Javascript", + "repository": "davidbau/seedrandom", + "main": "seedrandom.js", + "scripts": [ "seedrandom.js" ], + "keywords": [ "random", "seed", "crypto" ], + "license": "MIT" + } \ No newline at end of file diff --git a/tests/packagedcode/data/componentjs/seedrandom/expectedoutput.json b/tests/packagedcode/data/componentjs/seedrandom/expectedoutput.json new file mode 100644 index 0000000000..28ab8a2690 --- /dev/null +++ b/tests/packagedcode/data/componentjs/seedrandom/expectedoutput.json @@ -0,0 +1,91 @@ +{ + "packages": [], + "dependencies": [], + "files": [ + { + "path": "component.json", + "type": "file", + "package_data": [ + { + "type": "generic", + "namespace": null, + "name": "seedrandom", + "version": "2.3.10", + "qualifiers": {}, + "subpath": null, + "primary_language": null, + "description": "Seeded random number generator for Javascript", + "release_date": null, + "parties": [], + "keywords": [ + "random", + "seed", + "crypto" + ], + "homepage_url": null, + "download_url": null, + "size": null, + "sha1": null, + "md5": null, + "sha256": null, + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "holder": null, + "declared_license_expression": "mit", + "declared_license_expression_spdx": "MIT", + "license_detections": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "matches": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "from_file": "component.json", + "start_line": 1, + "end_line": 1, + "matcher": "1-hash", + "score": 16.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 16, + "rule_identifier": "mit_1301.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_1301.RULE", + "matched_text": "license type: MIT" + } + ], + "identifier": "mit-1c9cba21-81d2-7522-ac3e-dfde6630f8d1" + } + ], + "other_license_expression": null, + "other_license_expression_spdx": null, + "other_license_detections": [], + "extracted_license_statement": "type: MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "is_private": false, + "is_virtual": false, + "extra_data": { + "main": "seedrandom.js", + "scripts": [ + "seedrandom.js" + ], + "repository": "davidbau/seedrandom" + }, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": null, + "datasource_id": "component_json_metadata", + "purl": "pkg:generic/seedrandom@2.3.10" + } + ], + "for_packages": [], + "scan_errors": [] + } + ] +} \ No newline at end of file diff --git a/tests/packagedcode/test_componentjs.py b/tests/packagedcode/test_componentjs.py new file mode 100644 index 0000000000..14248ec5de --- /dev/null +++ b/tests/packagedcode/test_componentjs.py @@ -0,0 +1,371 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# ScanCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/nexB/scancode-toolkit for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +import os +from packagedcode import models +from commoncode.testcase import FileBasedTesting +from packages_test_utils import compare_package_results +from scancode.cli_test_utils import check_json_scan +from scancode.cli_test_utils import run_scan_click +from scancode_config import REGEN_TEST_FIXTURES +from packagedcode import componentjs + +class TestComponentJSON(FileBasedTesting): + test_data_dir = os.path.join(os.path.dirname(__file__), 'data') + + def test_scanworks_on_component_jszip(self): + test_file = self.get_test_loc('componentjs/jszip/component.json') + expected_file = self.get_test_loc('componentjs/jszip/expectedoutput.json') + result_file = self.get_temp_file('results.json') + run_scan_click(['--package', test_file, '--json-pp', result_file]) + check_json_scan(expected_file, result_file, regen=REGEN_TEST_FIXTURES) + + def test_scanworks_on_component_knockback(self): + test_file = self.get_test_loc('componentjs/knockback/component.json') + expected_file = self.get_test_loc('componentjs/knockback/expectedoutput.json') + result_file = self.get_temp_file('results.json') + run_scan_click(['--package', test_file, '--json-pp', result_file]) + check_json_scan(expected_file, result_file, regen=REGEN_TEST_FIXTURES) + + def test_scanworks_on_component_angular_ui_sortable(self): + test_file = self.get_test_loc('componentjs/angular-ui-sortable/component.json') + expected_file = self.get_test_loc('componentjs/angular-ui-sortable/expectedoutput.json') + result_file = self.get_temp_file('results.json') + run_scan_click(['--package', test_file, '--json-pp', result_file]) + check_json_scan(expected_file, result_file, regen=REGEN_TEST_FIXTURES) + + def test_scanworks_on_component_seedrandom(self): + test_file = self.get_test_loc('componentjs/seedrandom/component.json') + expected_file = self.get_test_loc('componentjs/seedrandom/expectedoutput.json') + result_file = self.get_temp_file('results.json') + run_scan_click(['--package', test_file, '--json-pp', result_file]) + check_json_scan(expected_file, result_file, regen=REGEN_TEST_FIXTURES) + + def test_scanworks_on_component_chai(self): + test_file = self.get_test_loc('componentjs/chai/component.json') + expected_file = self.get_test_loc('componentjs/chai/expectedoutput.json') + result_file = self.get_temp_file('results.json') + run_scan_click(['--package', test_file, '--json-pp', result_file]) + check_json_scan(expected_file, result_file, regen=REGEN_TEST_FIXTURES) + + def test_parse_jszip_component_json(self): + test_file = self.get_test_loc('componentjs/jszip/component.json') + result_packages = list(componentjs.ComponentJSONMetadataHandler.parse(test_file)) + expected_packages = [ + models.PackageData( + type=componentjs.ComponentJSONMetadataHandler.default_package_type, + datasource_id=componentjs.ComponentJSONMetadataHandler.datasource_id, + declared_license_expression= "mit OR gpl-3.0", + declared_license_expression_spdx= "MIT OR GPL-3.0-only", + name="jszip", + namespace="Stuk", + version="3.2.0", + description="Create, read and edit .zip files with JavaScript http://stuartk.com/jszip", + homepage_url="https://github.com/Stuk/jszip", + keywords=["zip", "deflate", "inflate"], + extracted_license_statement="type: MIT or GPLv3", + license_detections= [ + { + "license_expression": "mit OR gpl-3.0", + "license_expression_spdx": "MIT OR GPL-3.0-only", + "matches": [ + { + "license_expression": "mit OR gpl-3.0", + "license_expression_spdx": "MIT OR GPL-3.0-only", + "from_file": None, + "start_line": 1, + "end_line": 1, + "matcher": "2-aho", + "score": 100.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 100, + "rule_identifier": "mit_or_gpl-3.0_1.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_or_gpl-3.0_1.RULE", + "matched_text": "type: MIT or GPLv3" + } + ], + "identifier": "mit_or_gpl_3_0-9dbb60be-81c9-331c-96a6-8e6723aa5ce9" + } + ], + extra_data={ + "main": "dist/jszip.js", + "scripts": ["dist/jszip.js"] + } + ) + ] + compare_package_results(expected_packages, result_packages) + + def test_parse_knockback_component_json(self): + test_file = self.get_test_loc('componentjs/knockback/component.json') + result_packages = list(componentjs.ComponentJSONMetadataHandler.parse(test_file)) + expected_packages = [ + models.PackageData( + type=componentjs.ComponentJSONMetadataHandler.default_package_type, + datasource_id=componentjs.ComponentJSONMetadataHandler.datasource_id, + declared_license_expression= "mit", + declared_license_expression_spdx= "MIT", + name="knockback", + namespace="kmalakoff", + version="1.2.3", + description="Knockback.js provides Knockout.js magic for Backbone.js Models and Collections", + homepage_url="https://github.com/kmalakoff/knockback", + keywords=["knockback", "knockbackjs", "backbone", "backbonejs", "knockout", "knockoutjs"], + extracted_license_statement="type: MIT", + license_detections= [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "matches": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "from_file": None, + "start_line": 1, + "end_line": 1, + "matcher": "1-hash", + "score": 16.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 16, + "rule_identifier": "mit_1301.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_1301.RULE", + "matched_text": "license type: MIT" + } + ], + "identifier": "mit-1c9cba21-81d2-7522-ac3e-dfde6630f8d1" + } + ], + dependencies=[ + models.DependentPackage( + purl="pkg:generic/jashkenas/underscore@%2A", + scope="runtime", + is_runtime=True, + is_optional=False + ), + models.DependentPackage( + purl="pkg:generic/jashkenas/backbone@%2A", + scope="runtime", + is_runtime=True, + is_optional=False + ), + models.DependentPackage( + purl="pkg:generic/kmalakoff/knockout@%2A", + scope="runtime", + is_runtime=True, + is_optional=False + ) + ], + extra_data={ + "main": "knockback.js", + "scripts": ["knockback.js"] + } + ) + ] + compare_package_results(expected_packages, result_packages) + + def test_parse_angular_ui_sortable_component_json(self): + test_file = self.get_test_loc('componentjs/angular-ui-sortable/component.json') + result_packages = list(componentjs.ComponentJSONMetadataHandler.parse(test_file)) + expected_packages = [ + models.PackageData( + type=componentjs.ComponentJSONMetadataHandler.default_package_type, + datasource_id=componentjs.ComponentJSONMetadataHandler.datasource_id, + declared_license_expression= "mit", + declared_license_expression_spdx= "MIT", + name="angular-ui-sortable", + version="0.0.1", + description="This directive allows you to jQueryUI Sortable.", + homepage_url="http://angular-ui.github.com", + dependencies=[ + models.DependentPackage( + purl="pkg:generic/angular@~1.x", + scope="runtime", + is_runtime=True, + is_optional=False + ), + models.DependentPackage( + purl="pkg:generic/jquery-ui@%3E%3D%201.9", + scope="runtime", + is_runtime=True, + is_optional=False + ) + ], + extracted_license_statement="type: MIT", + license_detections= [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "matches": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "from_file": None, + "start_line": 1, + "end_line": 1, + "matcher": "1-hash", + "score": 16.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 16, + "rule_identifier": "mit_1301.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_1301.RULE", + "matched_text": "license type: MIT" + } + ], + "identifier": "mit-1c9cba21-81d2-7522-ac3e-dfde6630f8d1" + } + ], + extra_data={ + "main": "./src/sortable.js" + } + ) + ] + compare_package_results(expected_packages, result_packages) + + def test_parse_seedrandom_component_json(self): + test_file = self.get_test_loc('componentjs/seedrandom/component.json') + result_packages = list(componentjs.ComponentJSONMetadataHandler.parse(test_file)) + expected_packages = [ + models.PackageData( + type=componentjs.ComponentJSONMetadataHandler.default_package_type, + datasource_id=componentjs.ComponentJSONMetadataHandler.datasource_id, + declared_license_expression= "mit", + declared_license_expression_spdx= "MIT", + name="seedrandom", + version="2.3.10", + description="Seeded random number generator for Javascript", + homepage_url=None, + keywords=["random", "seed", "crypto"], + license_detections= [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "matches": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "from_file": None, + "start_line": 1, + "end_line": 1, + "matcher": "1-hash", + "score": 16.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 16, + "rule_identifier": "mit_1301.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_1301.RULE", + "matched_text": "license type: MIT" + } + ], + "identifier": "mit-1c9cba21-81d2-7522-ac3e-dfde6630f8d1" + } + ], + extracted_license_statement="type: MIT", + extra_data={ + "main": "seedrandom.js", + "scripts": ["seedrandom.js"], + "repository": "davidbau/seedrandom" + } + ) + ] + compare_package_results(expected_packages, result_packages) + + def test_parse_chai_component_json(self): + test_file = self.get_test_loc('componentjs/chai/component.json') + result_packages = list(componentjs.ComponentJSONMetadataHandler.parse(test_file)) + expected_packages = [ + models.PackageData( + type=componentjs.ComponentJSONMetadataHandler.default_package_type, + datasource_id=componentjs.ComponentJSONMetadataHandler.datasource_id, + declared_license_expression= "mit", + declared_license_expression_spdx= "MIT", + name="chai", + namespace="chaijs", + version="2.1.2", + description="BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", + homepage_url="https://github.com/chaijs/chai", + keywords=["test", "assertion", "assert", "testing", "chai"], + dependencies=[ + models.DependentPackage( + purl="pkg:generic/chaijs/assertion-error@1.0.0", + scope="runtime", + is_runtime=True, + is_optional=False + ), + models.DependentPackage( + purl="pkg:generic/chaijs/deep-eql@0.1.3", + scope="runtime", + is_runtime=True, + is_optional=False + ) + ], + extracted_license_statement="type: MIT", + license_detections= [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "matches": [ + { + "license_expression": "mit", + "license_expression_spdx": "MIT", + "from_file": None, + "start_line": 1, + "end_line": 1, + "matcher": "1-hash", + "score": 16.0, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 16, + "rule_identifier": "mit_1301.RULE", + "rule_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mit_1301.RULE", + "matched_text": "license type: MIT" + } + ], + "identifier": "mit-1c9cba21-81d2-7522-ac3e-dfde6630f8d1" + } + ], + extra_data={ + "main": "index.js", + "scripts": [ + "index.js", + "lib/chai.js", + "lib/chai/assertion.js", + "lib/chai/config.js", + "lib/chai/core/assertions.js", + "lib/chai/interface/assert.js", + "lib/chai/interface/expect.js", + "lib/chai/interface/should.js", + "lib/chai/utils/addChainableMethod.js", + "lib/chai/utils/addMethod.js", + "lib/chai/utils/addProperty.js", + "lib/chai/utils/flag.js", + "lib/chai/utils/getActual.js", + "lib/chai/utils/getEnumerableProperties.js", + "lib/chai/utils/getMessage.js", + "lib/chai/utils/getName.js", + "lib/chai/utils/getPathValue.js", + "lib/chai/utils/getPathInfo.js", + "lib/chai/utils/hasProperty.js", + "lib/chai/utils/getProperties.js", + "lib/chai/utils/index.js", + "lib/chai/utils/inspect.js", + "lib/chai/utils/objDisplay.js", + "lib/chai/utils/overwriteMethod.js", + "lib/chai/utils/overwriteProperty.js", + "lib/chai/utils/overwriteChainableMethod.js", + "lib/chai/utils/test.js", + "lib/chai/utils/transferFlags.js", + "lib/chai/utils/type.js" + ], + "development": {} + } + ) + ] + compare_package_results(expected_packages, result_packages) \ No newline at end of file