pax_global_header00006660000000000000000000000064145563332600014521gustar00rootroot0000000000000052 comment=b708a085c99925d1b57584163f9640c814a62637 notation-plugin-framework-go-1.0.0/000077500000000000000000000000001455633326000172445ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/.github/000077500000000000000000000000001455633326000206045ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/.github/.codecov.yml000066400000000000000000000012111455633326000230220ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. coverage: status: project: default: target: 80% notation-plugin-framework-go-1.0.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001455633326000227675ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/.github/ISSUE_TEMPLATE/bug-or-issue.yaml000066400000000000000000000045411455633326000262000ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. name: ๐Ÿ› Bug or Issue description: Something is not working as expected or not working at all! Report it here! labels: [bug, triage] body: - type: markdown attributes: value: | Thank you for taking the time to fill out this issue report. ๐Ÿ›‘ Please check existing issues first before continuing: https://github.com/notaryproject/notation-plugin-framework-go/issues - type: textarea id: verbatim validations: required: true attributes: label: "What is not working as expected?" description: "In your own words, describe what the issue is." - type: textarea id: expect validations: required: true attributes: label: "What did you expect to happen?" description: "A clear and concise description of what you expected to happen." - type: textarea id: reproduce validations: required: true attributes: label: "How can we reproduce it?" description: "Detailed steps to reproduce the behavior. Commands and their outputs are always helpful, code snippets are welcome." - type: textarea id: environment validations: required: true attributes: label: Describe your environment description: "OS and Golang version" - type: textarea id: version validations: required: true attributes: label: What is the version of your notation-plugin-framework-go Library? description: "Check the `go.mod` file for the library version" - type: markdown attributes: value: | If you want to contribute to this project, we will be happy to guide you through the contribution process especially when you already have a good proposal or understanding of how to fix this issue. Join us at https://slack.cncf.io/ and choose #notary-project channel. notation-plugin-framework-go-1.0.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000013411455633326000247560ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. blank_issues_enabled: false contact_links: - name: Ask a question url: https://slack.cncf.io/ about: "Join #notary-project channel on CNCF Slack" notation-plugin-framework-go-1.0.0/.github/ISSUE_TEMPLATE/feature-request.yaml000066400000000000000000000041111455633326000267710ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. name: ๐Ÿš€ Feature Request description: Suggest an idea for this project. labels: [enhancement, triage] body: - type: markdown attributes: value: | Thank you for taking the time to suggest a useful feature for the project! - type: textarea id: problem validations: required: true attributes: label: "Is your feature request related to a problem?" description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]" - type: textarea id: solution validations: required: true attributes: label: "What solution do you propose?" description: "A clear and concise description of what you want to happen." - type: textarea id: alternatives validations: required: true attributes: label: "What alternatives have you considered?" description: "A clear and concise description of any alternative solutions or features you've considered." - type: textarea id: context validations: required: false attributes: label: "Any additional context?" description: "Add any other context or screenshots about the feature request here." - type: markdown attributes: value: | If you want to contribute to this project, we will be happy to guide you through the contribution process especially when you already have a good proposal or understanding of how to improve the functionality. Join us at https://slack.cncf.io/ and choose #notary-project channel. notation-plugin-framework-go-1.0.0/.github/dependabot.yml000066400000000000000000000023611455633326000234360ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gomod" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" - package-ecosystem: "github-actions" # Workflow files stored in the # default location of `.github/workflows` directory: "/" schedule: interval: "weekly" notation-plugin-framework-go-1.0.0/.github/licenserc.yml000066400000000000000000000026601455633326000233020ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. header: license: spdx-id: Apache-2.0 content: | Copyright The Notary Project Authors. 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. paths-ignore: - '**/*.md' - 'CODEOWNERS' - 'LICENSE' - 'MAINTAINERS' - 'go.mod' - 'go.sum' - '**/testdata/**' comment: on-failure dependency: files: - go.mod notation-plugin-framework-go-1.0.0/.github/workflows/000077500000000000000000000000001455633326000226415ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/.github/workflows/build.yml000066400000000000000000000025171455633326000244700ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. name: Build on: push: pull_request: permissions: contents: read jobs: build: runs-on: ubuntu-latest strategy: matrix: go-version: [ "1.20", "1.21" ] fail-fast: true steps: - name: Check out code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Go ${{ matrix.go-version }} uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ matrix.go-version }} check-latest: true - name: Run unit tests run: make test - name: Run e2e tests run: make e2e - name: Upload coverage to codecov.io uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # v3.1.5 notation-plugin-framework-go-1.0.0/.github/workflows/codeql.yml000066400000000000000000000014421455633326000246340ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. name: "CodeQL" on: push: branches: main pull_request: branches: main schedule: - cron: '29 2 * * 5' jobs: analyze: uses: notaryproject/notation-core-go/.github/workflows/reusable-codeql.yml@main notation-plugin-framework-go-1.0.0/.github/workflows/license-checker.yml000066400000000000000000000015121455633326000264070ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. name: License Checker on: push: branches: main pull_request: branches: main permissions: contents: write pull-requests: write jobs: check-license: uses: notaryproject/notation-core-go/.github/workflows/reusable-license-checker.yml@main notation-plugin-framework-go-1.0.0/.gitignore000066400000000000000000000011741455633326000212370ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. # VS Code .vscode # Custom bin/ vendor/ test/e2e/bin/ notation-plugin-framework-go-1.0.0/CODEOWNERS000066400000000000000000000003651455633326000206430ustar00rootroot00000000000000# Owners (in alphabetical order) # For Org level maintainers, refer to https://github.com/notaryproject/.github/blob/main/CODEOWNERS * @gokarnm @JeyJeyGao @justincormack @niazfk @priteshbandi @rgnote @shizhMSFT @stevelasker @toddysm @Two-Hearts notation-plugin-framework-go-1.0.0/LICENSE000066400000000000000000000261351455633326000202600ustar00rootroot00000000000000 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. notation-plugin-framework-go-1.0.0/MAINTAINERS000066400000000000000000000014641455633326000207460ustar00rootroot00000000000000# Org-Level Maintainers (in alphabetical order) # Pattern: [First Name] [Last Name] <[Email Address]> ([GitHub Handle]) Justin Cormack (@justincormack) Niaz Khan (@niazfk) Pritesh Bandi (@priteshbandi) Shiwei Zhang (@shizhMSFT) Steve Lasker (@stevelasker) Toddy Mladenov (@toddysm) # Repo-Level Maintainers (in alphabetical order) # Pattern: [First Name] [Last Name] <[Email Address]> ([GitHub Handle]) # Note: This is for the notaryproject/notation-plugin-framework-go repo Junjie Gao (@JeyJeyGao) Milind Gokarn (@gokarnm) Patrick Zheng (@Two-Hearts) Rakesh Gariganti (@rgnote) notation-plugin-framework-go-1.0.0/Makefile000066400000000000000000000024101455633326000207010ustar00rootroot00000000000000# Copyright The Notary Project Authors. # 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. .PHONY: help help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}' .PHONY: all all: test .PHONY: test test: check-line-endings ## run unit tests go test -race -v -coverprofile=coverage.txt -covermode=atomic ./... .PHONY: e2e e2e: cd ./test/e2e && ./run.sh; .PHONY: clean clean: git status --ignored --short | grep '^!! ' | sed 's/!! //' | xargs rm -rf .PHONY: check-line-endings check-line-endings: ## check line endings ! find . -name "*.go" -type f -exec file "{}" ";" | grep CRLF .PHONY: fix-line-endings fix-line-endings: ## fix line endings find . -type f -name "*.go" -exec sed -i -e "s/\r//g" {} + notation-plugin-framework-go-1.0.0/README.md000066400000000000000000000034311455633326000205240ustar00rootroot00000000000000# notation-plugin-framework-go [![Build Status](https://github.com/notaryproject/notation-plugin-framework-go/actions/workflows/build.yml/badge.svg?event=push&branch=main)](https://github.com/notaryproject/notation-plugin-framework-go/actions/workflows/build.yml?query=workflow%3Abuild+event%3Apush+branch%3Amain) [![Codecov](https://codecov.io/gh/notaryproject/notation-plugin-framework-go/branch/main/graph/badge.svg)](https://codecov.io/gh/notaryproject/notation-plugin-framework-go) [![Go Reference](https://pkg.go.dev/badge/github.com/notaryproject/notation-plugin-framework-go.svg)](https://pkg.go.dev/github.com/notaryproject/notation-plugin-framework-go@main) notation-plugin-framework-go contains framework or library required to create notation plugins as per [Notary Project specifications](https://github.com/notaryproject/specifications). Please visit [README](https://github.com/notaryproject/.github/blob/main/README.md) to know more about Notary Project. > [!NOTE] > The Notary Project documentation is available [here](https://notaryproject.dev/docs/). ## Table of Contents - [Documentation](#documentation) - [Example Plugins](#example-plugins) - [Code of Conduct](#code-of-conduct) - [License](#license) ## Documentation Library documentation is available at [Go Reference](https://pkg.go.dev/github.com/notaryproject/notation-plugin-framework-go). ## Example Plugins - [Signature generator](example/signaturegenerator/) - [Envelope generator](example/envelopegenerator/) ## Code of Conduct This project has adopted the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for further details. ## License This project is covered under the Apache 2.0 license. You can read the license [here](LICENSE). notation-plugin-framework-go-1.0.0/cli/000077500000000000000000000000001455633326000200135ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/cli/cli.go000066400000000000000000000136051455633326000211160ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. // Package cli provides boilerplate code required to generate plugin executable. // At high level it performs following tasks // 1. Validate command arguments // 2. Read and unmarshal input // 3. Execute relevant plugin functions // 4. marshals output package cli import ( "context" "encoding/json" "errors" "fmt" "os" "reflect" "github.com/notaryproject/notation-plugin-framework-go/internal/slices" "github.com/notaryproject/notation-plugin-framework-go/log" "github.com/notaryproject/notation-plugin-framework-go/plugin" ) // CLI struct is used to create an executable for plugin. type CLI struct { pl plugin.Plugin logger log.Logger } // New creates a new CLI using given plugin func New(pl plugin.Plugin) (*CLI, error) { return NewWithLogger(pl, &discardLogger{}) } // NewWithLogger creates a new CLI using given plugin and logger func NewWithLogger(pl plugin.Plugin, l log.Logger) (*CLI, error) { if pl == nil { return nil, errors.New("plugin cannot be nil") } return &CLI{ pl: pl, logger: l, }, nil } // Execute is main controller that reads/validates commands, parses input, executes relevant plugin functions // and returns corresponding output. func (c *CLI) Execute(ctx context.Context, args []string) { c.validateArgs(ctx, args) rescueStdOut := deferStdout() command := args[1] var resp any var err error switch plugin.Command(command) { case plugin.CommandGetMetadata: var request plugin.GetMetadataRequest err = c.unmarshalRequest(&request) if err == nil { c.logger.Debugf("executing %s plugin's GetMetadata function", reflect.TypeOf(c.pl)) resp, err = c.pl.GetMetadata(ctx, &request) } case plugin.CommandGenerateEnvelope: var request plugin.GenerateEnvelopeRequest err = c.unmarshalRequest(&request) if err == nil { c.logger.Debugf("executing %s plugin's GenerateEnvelope function", reflect.TypeOf(c.pl)) resp, err = c.pl.GenerateEnvelope(ctx, &request) } case plugin.CommandVerifySignature: var request plugin.VerifySignatureRequest err = c.unmarshalRequest(&request) if err == nil { c.logger.Debugf("executing %s plugin's VerifySignature function", reflect.TypeOf(c.pl)) resp, err = c.pl.VerifySignature(ctx, &request) } case plugin.CommandDescribeKey: var request plugin.DescribeKeyRequest err = c.unmarshalRequest(&request) if err == nil { c.logger.Debugf("executing %s plugin's DescribeKey function", reflect.TypeOf(c.pl)) resp, err = c.pl.DescribeKey(ctx, &request) } case plugin.CommandGenerateSignature: var request plugin.GenerateSignatureRequest err = c.unmarshalRequest(&request) if err == nil { c.logger.Debugf("executing %s plugin's GenerateSignature function", reflect.TypeOf(c.pl)) resp, err = c.pl.GenerateSignature(ctx, &request) } case plugin.Version: rescueStdOut() c.printVersion(ctx) return default: // should never happen rescueStdOut() deliverError(plugin.NewGenericError("something went wrong").Error()) } op, pluginErr := c.marshalResponse(resp, err) rescueStdOut() if pluginErr != nil { deliverError(pluginErr.Error()) } fmt.Print(op) } // printVersion prints version of executable func (c *CLI) printVersion(ctx context.Context) { md := c.getMetadata(ctx, c.pl) fmt.Printf("%s - %s\nVersion: %s\n", md.Name, md.Description, md.Version) } // validateArgs validate commands/arguments passed to executable. func (c *CLI) validateArgs(ctx context.Context, args []string) { md := c.getMetadata(ctx, c.pl) if !(len(args) == 2 && slices.Contains(getValidArgs(md), args[1])) { deliverError(fmt.Sprintf("Invalid command, valid commands are: %s", getValidArgsString(md))) } } // unmarshalRequest reads input from std.in and unmarshal it into given request struct func (c *CLI) unmarshalRequest(request plugin.Request) error { if err := json.NewDecoder(os.Stdin).Decode(request); err != nil { c.logger.Errorf("%s unmarshalling error: %v", reflect.TypeOf(request), err) return plugin.NewJSONParsingError(plugin.ErrorMsgMalformedInput) } if err := request.Validate(); err != nil { c.logger.Errorf("%s validation error: %v", reflect.TypeOf(request), err) var plError *plugin.Error if errors.As(err, &plError) { return plugin.NewValidationErrorf("%s: %s", plugin.ErrorMsgMalformedInput, plError.Message) } return plugin.NewValidationErrorf("%s", plugin.ErrorMsgMalformedInput) } return nil } func (c *CLI) getMetadata(ctx context.Context, p plugin.Plugin) *plugin.GetMetadataResponse { md, err := p.GetMetadata(ctx, &plugin.GetMetadataRequest{}) if err != nil { c.logger.Errorf("GetMetadataRequest error: %v", err) deliverError("Error: Failed to get plugin metadata.") } return md } // marshalResponse marshals the given response struct into json func (c *CLI) marshalResponse(response any, err error) (string, *plugin.Error) { if err != nil { c.logger.Errorf("%s error: %v", reflect.TypeOf(response), err) if plgErr, ok := err.(*plugin.Error); ok { return "", plgErr } return "", plugin.NewGenericError(err.Error()) } c.logger.Debug("marshalling response") jsonResponse, err := json.Marshal(response) if err != nil { c.logger.Errorf("%s marshalling error: %v", reflect.TypeOf(response), err) return "", plugin.NewGenericErrorf(plugin.ErrorMsgMalformedOutputFmt, err.Error()) } c.logger.Debugf("%s response: %s", reflect.TypeOf(response), jsonResponse) return string(jsonResponse), nil } notation-plugin-framework-go-1.0.0/cli/cli_test.go000066400000000000000000000163371455633326000221620ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package cli import ( "context" "fmt" "io" "log" "os" "os/exec" "reflect" "strings" "testing" "github.com/notaryproject/notation-plugin-framework-go/internal/mock" "github.com/notaryproject/notation-plugin-framework-go/plugin" ) var cli, _ = New(mock.NewPlugin(false)) var errorCli, _ = New(mock.NewPlugin(true)) func TestNewWithLogger(t *testing.T) { _, err := NewWithLogger(nil, &discardLogger{}) if err == nil { t.Errorf("NewWithLogger() expected error but not found") } } func TestMarshalResponse(t *testing.T) { res := plugin.GenerateEnvelopeResponse{ SignatureEnvelope: []byte("envelope"), Annotations: map[string]string{"key": "value"}, SignatureEnvelopeType: "envelopeType", } op, err := cli.marshalResponse(res, nil) if err != nil { t.Errorf("Error found in marshalResponse: %v", err) } expected := "{\"signatureEnvelope\":\"ZW52ZWxvcGU=\",\"signatureEnvelopeType\":\"envelopeType\",\"annotations\":{\"key\":\"value\"}}" if !strings.EqualFold("{\"signatureEnvelope\":\"ZW52ZWxvcGU=\",\"signatureEnvelopeType\":\"envelopeType\",\"annotations\":{\"key\":\"value\"}}", op) { t.Errorf("Not equal: \n expected: %s\n actual : %s", expected, op) } } func TestMarshalResponseError(t *testing.T) { _, err := cli.marshalResponse(nil, fmt.Errorf("expected error thrown")) assertErr(t, err, plugin.ErrorCodeGeneric) _, err = cli.marshalResponse(nil, plugin.NewValidationError("expected validation error thrown")) assertErr(t, err, plugin.ErrorCodeValidation) _, err = cli.marshalResponse(make(chan int), nil) assertErr(t, err, plugin.ErrorCodeGeneric) } func TestUnmarshalRequest(t *testing.T) { content := "{\"contractVersion\":\"1.0\",\"keyId\":\"someKeyId\",\"pluginConfig\":{\"pc1\":\"pk1\"}}" closer := setupReader(content) defer closer() var request plugin.DescribeKeyRequest if err := cli.unmarshalRequest(&request); err != nil { t.Errorf("unmarshalRequest() failed with error: %v", err) } if request.ContractVersion != "1.0" || request.KeyID != "someKeyId" || request.PluginConfig["pc1"] != "pk1" { t.Errorf("unmarshalRequest() returned incorrect struct") } } func TestUnmarshalRequestError(t *testing.T) { closer := setupReader("InvalidJson") defer closer() var request plugin.DescribeKeyRequest err := cli.unmarshalRequest(&request) if err == nil { t.Errorf("unmarshalRequest() expected error but not found") } plgErr, ok := err.(*plugin.Error) if !ok { t.Errorf("unmarshalRequest() expected error of type plugin.Error but found %s", reflect.TypeOf(err)) } expectedErrStr := "{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"Input is not a valid JSON\"}" if plgErr.Error() != expectedErrStr { t.Errorf("unmarshalRequest() expected error string to be %s but found %s", expectedErrStr, plgErr.Error()) } } func TestGetMetadataError(t *testing.T) { if os.Getenv("TEST_OS_EXIT") == "1" { ctx := context.Background() errorCli.Execute(ctx, []string{string(plugin.CommandGetMetadata)}) return } cmd := exec.Command(os.Args[0], "-test.run=TestGetMetadataError") cmd.Env = append(os.Environ(), "TEST_OS_EXIT=1") err := cmd.Run() if e, ok := err.(*exec.ExitError); ok && !e.Success() { return } t.Errorf("process ran with err %v, want exit status 1", err) } func TestExecuteSuccess(t *testing.T) { sigGenCli, _ := New(mock.NewSigGeneratorPlugin(false)) tests := map[string]struct { c *CLI in string op string }{ string(plugin.CommandGetMetadata): { c: cli, in: "{}", op: "{\"name\":\"Example Plugin\",\"description\":\"This is an description of example plugin. ๐Ÿบ\",\"version\":\"1.0.0\",\"url\":\"https://example.com/notation/plugin\",\"capabilities\":[\"SIGNATURE_VERIFIER.TRUSTED_IDENTITY\",\"SIGNATURE_VERIFIER.REVOCATION_CHECK\",\"SIGNATURE_GENERATOR.ENVELOPE\"]}", }, string(plugin.Version): { c: cli, in: "", op: "Example Plugin - This is an description of example plugin. ๐Ÿบ\nVersion: 1.0.0\n", }, string(plugin.CommandGenerateEnvelope): { c: cli, in: "{\"contractVersion\":\"1.0\",\"keyId\":\"someKeyId\",\"payloadType\":\"somePT\",\"signatureEnvelopeType\":\"someSET\",\"payload\":\"em9w\"}", op: "{\"signatureEnvelope\":\"\",\"signatureEnvelopeType\":\"someSET\",\"annotations\":{\"manifestAnntnKey1\":\"value1\"}}", }, string(plugin.CommandVerifySignature): { c: cli, in: "{\"contractVersion\":\"1.0\",\"signature\":{\"criticalAttributes\":{\"contentType\":\"someCT\",\"signingScheme\":\"someSigningScheme\"},\"unprocessedAttributes\":null,\"certificateChain\":[\"emFw\",\"em9w\"]},\"trustPolicy\":{\"trustedIdentities\":null,\"signatureVerification\":[\"SIGNATURE_GENERATOR.RAW\"]}}", op: "{\"verificationResults\":{\"SIGNATURE_VERIFIER.REVOCATION_CHECK\":{\"success\":true,\"reason\":\"Not revoked\"},\"SIGNATURE_VERIFIER.TRUSTED_IDENTITY\":{\"success\":true,\"reason\":\"Valid trusted Identity\"}},\"processedAttributes\":[]}", }, string(plugin.CommandGenerateSignature): { c: sigGenCli, in: "{\"contractVersion\":\"1.0\",\"keyId\":\"someKeyId\",\"keySpec\":\"EC-384\",\"hashAlgorithm\":\"SHA-384\",\"payload\":\"em9w\"}", op: "{\"keyId\":\"someKeyId\",\"signature\":\"YWJjZA==\",\"signingAlgorithm\":\"RSASSA-PSS-SHA-256\",\"certificateChain\":[\"YWJjZA==\",\"d3h5eg==\"]}", }, string(plugin.CommandDescribeKey): { c: sigGenCli, in: "{\"contractVersion\":\"1.0\",\"keyId\":\"someKeyId\"}", op: "{\"keyId\":\"someKeyId\",\"keySpec\":\"RSA-2048\"}", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { closer := setupReader(test.in) defer closer() op := captureStdOut(func() { test.c.Execute(context.Background(), []string{"notation", name}) }) if op != test.op { t.Errorf("Execute() with '%s' args, expected '%s' but got '%s'", name, test.op, op) } }) } } func setupReader(content string) func() { tmpfile, err := os.CreateTemp("", "example") if err != nil { log.Fatal(err) } if _, err := tmpfile.Write([]byte(content)); err != nil { log.Fatal(err) } if _, err := tmpfile.Seek(0, 0); err != nil { log.Fatal(err) } oldStdin := os.Stdin os.Stdin = tmpfile return func() { os.Remove(tmpfile.Name()) os.Stdin = oldStdin // Restore original Stdin tmpfile.Close() } } func captureStdOut(f func()) string { orig := os.Stdout r, w, _ := os.Pipe() os.Stdout = w f() os.Stdout = orig w.Close() out, _ := io.ReadAll(r) return string(out) } func assertErr(t *testing.T, err error, code plugin.ErrorCode) { if plgErr, ok := err.(*plugin.Error); ok { if reflect.DeepEqual(code, plgErr.ErrCode) { return } t.Errorf("mismatch in error code: \n expected: %s\n actual : %s", code, plgErr.ErrCode) } t.Errorf("expected error of type PluginError but found %s", reflect.TypeOf(err)) } notation-plugin-framework-go-1.0.0/cli/utils.go000066400000000000000000000064231455633326000215070ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package cli import ( "fmt" "os" "sort" "strings" "github.com/notaryproject/notation-plugin-framework-go/internal/slices" "github.com/notaryproject/notation-plugin-framework-go/plugin" ) func getValidArgsString(md *plugin.GetMetadataResponse) string { return fmt.Sprintf(`<%s>`, strings.Join(getValidArgs(md), "|")) } // getValidArgs returns list of valid arguments depending upon the plugin capabilities func getValidArgs(md *plugin.GetMetadataResponse) []string { args := []string{ string(plugin.CommandGetMetadata), string(plugin.Version), } if slices.Contains(md.Capabilities, plugin.CapabilitySignatureGenerator) { args = append(args, string(plugin.CommandGenerateSignature), string(plugin.CommandDescribeKey)) } if slices.Contains(md.Capabilities, plugin.CapabilityEnvelopeGenerator) { args = append(args, string(plugin.CommandGenerateEnvelope)) } if slices.Contains(md.Capabilities, plugin.CapabilityTrustedIdentityVerifier) || slices.Contains(md.Capabilities, plugin.CapabilityRevocationCheckVerifier) { args = append(args, string(plugin.CommandVerifySignature)) } sort.Strings(args) return args } // deliverError print to standard error and then return nonzero exit code func deliverError(message string) { _, _ = fmt.Fprint(os.Stderr, message) os.Exit(1) } // deferStdout is used to make sure that nothing get emitted to stdout and stderr until intentionally rescued. // This is required to make sure that the plugin or its dependency doesn't interfere with notation <-> plugin communication func deferStdout() func() { // Ignoring error because we don't want plugin to fail if `os.DevNull` is misconfigured. null, _ := os.Open(os.DevNull) sout := os.Stdout serr := os.Stderr os.Stdout = null os.Stderr = null return func() { err := null.Close() if err != nil { return } os.Stdout = sout os.Stderr = serr } } // discardLogger implements Logger but logs nothing. It is used when user // disenabled logging option in notation, i.e. loggerKey is not in the context. type discardLogger struct{} func (dl *discardLogger) Debug(_ ...interface{}) { } func (dl *discardLogger) Debugf(_ string, _ ...interface{}) { } func (dl *discardLogger) Debugln(_ ...interface{}) { } func (dl *discardLogger) Info(_ ...interface{}) { } func (dl *discardLogger) Infof(_ string, _ ...interface{}) { } func (dl *discardLogger) Infoln(_ ...interface{}) { } func (dl *discardLogger) Warn(_ ...interface{}) { } func (dl *discardLogger) Warnf(_ string, _ ...interface{}) { } func (dl *discardLogger) Warnln(_ ...interface{}) { } func (dl *discardLogger) Error(_ ...interface{}) { } func (dl *discardLogger) Errorf(_ string, _ ...interface{}) { } func (dl *discardLogger) Errorln(_ ...interface{}) { } notation-plugin-framework-go-1.0.0/cli/utils_test.go000066400000000000000000000053571455633326000225530ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package cli import ( "reflect" "strings" "testing" "github.com/notaryproject/notation-plugin-framework-go/plugin" ) func TestGetValidArgsString(t *testing.T) { mdResp := plugin.GetMetadataResponse{ Name: "Example Plugin", Description: "This is an description of example plugin. ๐Ÿบ", URL: "https://example.com/notation/plugin", Version: "1.0.0", Capabilities: []plugin.Capability{ plugin.CapabilityEnvelopeGenerator, plugin.CapabilityTrustedIdentityVerifier, plugin.CapabilityRevocationCheckVerifier}, } s := getValidArgsString(&mdResp) expected := "" if !strings.EqualFold(s, expected) { t.Errorf("Expected %s but found %s", expected, s) } } func TestGetValidArgs(t *testing.T) { tests := map[string]struct { caps []plugin.Capability args []string }{ "sigGeneratorOnly": { caps: []plugin.Capability{plugin.CapabilitySignatureGenerator}, args: []string{"describe-key", "generate-signature", "get-plugin-metadata", "version"}, }, "envGeneratorOnly": { caps: []plugin.Capability{plugin.CapabilityEnvelopeGenerator}, args: []string{"generate-envelope", "get-plugin-metadata", "version"}, }, "verificationOnly": { caps: []plugin.Capability{plugin.CapabilityTrustedIdentityVerifier, plugin.CapabilityRevocationCheckVerifier}, args: []string{"get-plugin-metadata", "verify-signature", "version"}, }, "envGenerator+verification": { caps: []plugin.Capability{plugin.CapabilityEnvelopeGenerator, plugin.CapabilityTrustedIdentityVerifier, plugin.CapabilityRevocationCheckVerifier}, args: []string{"generate-envelope", "get-plugin-metadata", "verify-signature", "version"}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { mdResp := plugin.GetMetadataResponse{ Name: "Example Plugin", Description: "This is an description of example plugin. ๐Ÿบ", URL: "https://example.com/notation/plugin", Version: "1.0.0", Capabilities: test.caps, } s := getValidArgs(&mdResp) if !reflect.DeepEqual(s, test.args) { t.Errorf("Expected %s but found %s", test.args, s) } }) } } notation-plugin-framework-go-1.0.0/example/000077500000000000000000000000001455633326000206775ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/example/README.md000066400000000000000000000002461455633326000221600ustar00rootroot00000000000000This package contains examples for creating plugins in a way that enables the creation of executable binaries and also facilitates its use as a library in notation-gonotation-plugin-framework-go-1.0.0/example/envelopegenerator/000077500000000000000000000000001455633326000244235ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/example/envelopegenerator/main.go000066400000000000000000000021301455633326000256720ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package main import ( "context" "fmt" "os" "github.com/notaryproject/notation-plugin-framework-go/cli" ) func main() { ctx := context.Background() // Initialize plugin plugin, err := NewExamplePlugin() if err != nil { _, _ = fmt.Fprintf(os.Stderr, "failed to initialize plugin: %v\n", err) os.Exit(2) } // Create executable pluginCli, err := cli.New(plugin) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "failed to create executable: %v\n", err) os.Exit(3) } pluginCli.Execute(ctx, os.Args) } notation-plugin-framework-go-1.0.0/example/envelopegenerator/plugin.go000066400000000000000000000143021455633326000262500ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package main import ( "context" "github.com/notaryproject/notation-plugin-framework-go/plugin" ) type ExamplePlugin struct { } func NewExamplePlugin() (*ExamplePlugin, error) { return &ExamplePlugin{}, nil } func (p *ExamplePlugin) DescribeKey(_ context.Context, _ *plugin.DescribeKeyRequest) (*plugin.DescribeKeyResponse, error) { return nil, plugin.NewUnsupportedError("DescribeKey operation is not implemented by example plugin") } func (p *ExamplePlugin) GenerateSignature(_ context.Context, _ *plugin.GenerateSignatureRequest) (*plugin.GenerateSignatureResponse, error) { return nil, plugin.NewUnsupportedError("GenerateSignature operation is not implemented by example plugin") } func (p *ExamplePlugin) GenerateEnvelope(_ context.Context, _ *plugin.GenerateEnvelopeRequest) (*plugin.GenerateEnvelopeResponse, error) { sig := "eyJwYXlsb2FkIjoiZXlKMFlYSm5aWFJCY25ScFptRmpkQ0k2ZXlKa2FXZGxjM1FpT2lKemFHRXlOVFk2Wm1VM1pUa3pNek16T1RVd05qQmpNbVkxWlRZelkyWXpObUV6" + "T0daaVlURXdNVGMyWmpFNE0ySTBNVFl6WVRVM09UUmxNRGd4WVRRNE1HRmlZbUUxWmlJc0ltMWxaR2xoVkhsd1pTSTZJbUZ3Y0d4cFkyRjBhVzl1TDNadVpDNWtiMk5yWlh" + "JdVpHbHpkSEpwWW5WMGFXOXVMbTFoYm1sbVpYTjBMbll5SzJwemIyNGlMQ0p6YVhwbElqbzVOREo5ZlEiLCJwcm90ZWN0ZWQiOiJleUpoYkdjaU9pSlFVekkxTmlJc0ltTn" + "lhWFFpT2xzaWFXOHVZMjVqWmk1dWIzUmhjbmt1YzJsbmJtbHVaMU5qYUdWdFpTSXNJbWx2TG1OdVkyWXVibTkwWVhKNUxuWmxjbWxtYVdOaGRHbHZibEJzZFdkcGJrMXBib" + "FpsY25OcGIyNGlMQ0pwYnk1amJtTm1MbTV2ZEdGeWVTNTJaWEpwWm1sallYUnBiMjVRYkhWbmFXNGlYU3dpWTNSNUlqb2lZWEJ3YkdsallYUnBiMjR2ZG01a0xtTnVZMll1" + "Ym05MFlYSjVMbkJoZVd4dllXUXVkakVyYW5OdmJpSXNJbWx2TG1OdVkyWXVibTkwWVhKNUxuTnBaMjVwYm1kVFkyaGxiV1VpT2lKdWIzUmhjbmt1ZURVd09TSXNJbWx2TG1" + "OdVkyWXVibTkwWVhKNUxuTnBaMjVwYm1kVWFXMWxJam9pTWpBeU15MHdNUzB4T1ZReE16b3dNem95TXkwd09Eb3dNQ0lzSW1sdkxtTnVZMll1Ym05MFlYSjVMblpsY21sbW" + "FXTmhkR2x2YmxCc2RXZHBiaUk2SW1sdkxtTnVZMll1Ym05MFlYSjVMbkJzZFdkcGJpNTFibWwwZEdWemRDNXRiMk5ySWl3aWFXOHVZMjVqWmk1dWIzUmhjbmt1ZG1WeWFXW" + "nBZMkYwYVc5dVVHeDFaMmx1VFdsdVZtVnljMmx2YmlJNklqRXVNQzR3TFdGc2NHaGhMbUpsZEdFaWZRIiwiaGVhZGVyIjp7Ing1YyI6WyJNSUlEVmpDQ0FqNmdBd0lCQWdJ" + "QlVUQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQk1DVjBFeEVEQU9CZ05WQkFjVEIxTmxZWFIwYkdVeER6QU5CZ05WQkFvVEJ" + "rNXZkR0Z5ZVRFYk1Ca0dBMVVFQXhNU2QyRmlZbWwwTFc1bGRIZHZjbXR6TG1sdk1CNFhEVEl6TURFeE9UQTRNVGt3TjFvWERUTXpNREV4T1RBNE1Ua3dOMW93V2pFTE1Ba0" + "dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ1RBbGRCTVJBd0RnWURWUVFIRXdkVFpXRjBkR3hsTVE4d0RRWURWUVFLRXdaT2IzUmhjbmt4R3pBWkJnTlZCQU1URW5kaFltSnBkQ" + "zF1WlhSM2IzSnJjeTVwYnpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTkhobFArU2lZN2hzR2xmMm1BRE96SlcvSjlzaXFNa2lRdlNPeDBP" + "U00yeXhldGZWUUwvYWJpNGlxQ1hNNndrU3h2aUJlTndJb1lFczR0aE1BOE5HRWJuS29Ya3R5aDl2bWlMQjFGVzdISHI0UUx3amdMemdXSktJUVR5MUptREJlY1haaDU2ZDB" + "mM3czWWoxSURUdmtJU2NYQ05JKzV2LzA4R1VRS2h5Qnd2N0ZxOU1ZcG8ybGZYU0k3VjMzQktLZGRYSXhQR1ZXd0tHdlBFMHNnMlZWN1dNODRaWkxkREt6Mm1xMFB0UFRIcl" + "N3ZzNobEsvbWpuK2JsZzNnc1lRNGg5LzdaNm5OYUY5WDBTZHlFU2w4NDFaV3J0TWhBT0ZwSXpMYno5ZXRlOE5SZDNiWUNSQklyNWdzY0hXVGY2bHlVZ3k0eHpzU3dNSFBzR" + "0xNNEErWjAwQ0F3RUFBYU1uTUNVd0RnWURWUjBQQVFIL0JBUURBZ2VBTUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVGQndNRE1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQWJO" + "MEVydTU2dVRRU0MyOFpUZjhEN1Z5Q2tZcnJXTFlpSk1ZZE9LQnp6S1Y5bUthTTBPR0YydXlXd0RhUHhwOUtUZExYbUJwOUVGcTVTWFhBckZBK25SUzdLaW5EQWUyTzdBLzl" + "TdGQyWGpLaTkyN3JrQTJjajIzOWQ1bFJzaldYcUpYZjl2QU1WOWEyRmpVTS9pbjJFZXZscTdidmpGRTNsMjZWWENLdE9zOUVybWZ4ckwrNkVUUktTVllPT0cvclNIRnYvU0" + "IyTWxxRGc1UXNYQzlsWmp6TDUvWC9pb2UycVpLaHA2WDVEUHBhZDFxMVE0SXRLZFROKzJFWHlNeW9IbjFCSktOYmE3Q1VVdlhmMDNFSmViVC9JbStxb3pmRWtzSmVaSlVTb" + "FN1akFOVVBvQ3BzRVlHV1dReDVHK1ZpRzA1U3FzKzZwcEtydXQrUCtEVlBvIl0sImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdBZ2VudCI6Ik5vdGF0aW9uLzEuMC4wIn0sInNp" + "Z25hdHVyZSI6ImlKdGh0cWJ6ME81bkZ1bzVaOW5SZGRFanlacDNSRy1LT1k2U1NCM3NjOEFnREJkVDVGanA5eWx0SW9xVGwtQkxaaHJHT0FGZU8wVF8xSlZzUGJaWk14ekp" + "xNGZiM2dQYUlQSXRyZW5ka3BpdDFtMlJhQjhmSzFEX0k2VnF1MV9yR2lZYXhEY05wYXFuMVRfaXN4cjRNVlJla2NMU05RbkczaU1kSjBrLUF0dGY4SmRDWEUwRVdLeUxCU3" + "RNVkFmbzBKMzlTaEZjd3lJTXZPMHZtMl9UUkRWYmNLb3ZwWTB2RnJmeUUycEZJQ2huSkVDbWl2SW1kS21CTUlXNzh2RXRONnFCcktza0kzSHpBOU4xWGp4R1k0R09BdTMwa" + "XF0TlJhbk82NW5aR25nMGxxcEpkMTViQXdVYXFqLUtEX0JBWklVVDlUMnFDZjJDT0Y5R0t2YzNOUSJ9Cg==" return &plugin.GenerateEnvelopeResponse{ SignatureEnvelope: []byte(sig), SignatureEnvelopeType: "application/jose+json", Annotations: map[string]string{"manifestAnntnKey1": "value1"}, }, nil } func (p *ExamplePlugin) VerifySignature(_ context.Context, req *plugin.VerifySignatureRequest) (*plugin.VerifySignatureResponse, error) { upAttrs := req.Signature.UnprocessedAttributes pAttrs := make([]interface{}, len(upAttrs)) for i := range upAttrs { pAttrs[i] = upAttrs[i] } return &plugin.VerifySignatureResponse{ ProcessedAttributes: pAttrs, VerificationResults: map[plugin.Capability]*plugin.VerificationResult{ plugin.CapabilityTrustedIdentityVerifier: { Success: true, Reason: "Valid trusted Identity", }, plugin.CapabilityRevocationCheckVerifier: { Success: true, Reason: "Not revoked", }, }, }, nil } func (p *ExamplePlugin) GetMetadata(_ context.Context, _ *plugin.GetMetadataRequest) (*plugin.GetMetadataResponse, error) { return &plugin.GetMetadataResponse{ SupportedContractVersions: []string{plugin.ContractVersion}, Name: "com.example.plugin", Description: "This is an description of example plugin", URL: "https://example.com/notation/plugin", Version: "1.0.0", Capabilities: []plugin.Capability{ plugin.CapabilityEnvelopeGenerator, plugin.CapabilityTrustedIdentityVerifier, plugin.CapabilityRevocationCheckVerifier}, }, nil } notation-plugin-framework-go-1.0.0/example/go.mod000066400000000000000000000003661455633326000220120ustar00rootroot00000000000000module github.com/notaryproject/notation-plugin-framework-go/example go 1.20 require github.com/notaryproject/notation-plugin-framework-go v0.0.0-00010101000000-000000000000 replace github.com/notaryproject/notation-plugin-framework-go => ../ notation-plugin-framework-go-1.0.0/example/signaturegenerator/000077500000000000000000000000001455633326000246075ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/example/signaturegenerator/main.go000066400000000000000000000021301455633326000260560ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package main import ( "context" "fmt" "os" "github.com/notaryproject/notation-plugin-framework-go/cli" ) func main() { ctx := context.Background() // Initialize plugin plugin, err := NewExamplePlugin() if err != nil { _, _ = fmt.Fprintf(os.Stderr, "failed to initialize plugin: %v\n", err) os.Exit(2) } // Create executable pluginCli, err := cli.New(plugin) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "failed to create executable: %v\n", err) os.Exit(3) } pluginCli.Execute(ctx, os.Args) } notation-plugin-framework-go-1.0.0/example/signaturegenerator/plugin.go000066400000000000000000000056661455633326000264510ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package main import ( "context" "github.com/notaryproject/notation-plugin-framework-go/plugin" ) type ExamplePlugin struct { } func NewExamplePlugin() (*ExamplePlugin, error) { return &ExamplePlugin{}, nil } func (p *ExamplePlugin) DescribeKey(_ context.Context, req *plugin.DescribeKeyRequest) (*plugin.DescribeKeyResponse, error) { return &plugin.DescribeKeyResponse{ KeyID: req.KeyID, KeySpec: plugin.KeySpecRSA3072, }, nil } func (p *ExamplePlugin) GenerateSignature(_ context.Context, req *plugin.GenerateSignatureRequest) (*plugin.GenerateSignatureResponse, error) { return &plugin.GenerateSignatureResponse{ KeyID: req.KeyID, Signature: []byte("generatedMockSignature"), SigningAlgorithm: plugin.SignatureAlgorithmRSASSA_PSS_SHA384, CertificateChain: [][]byte{[]byte("mockCert1"), []byte("mockCert2")}, }, nil } func (p *ExamplePlugin) GenerateEnvelope(_ context.Context, _ *plugin.GenerateEnvelopeRequest) (*plugin.GenerateEnvelopeResponse, error) { return nil, plugin.NewUnsupportedError("GenerateSignature operation is not implemented by example plugin") } func (p *ExamplePlugin) VerifySignature(_ context.Context, req *plugin.VerifySignatureRequest) (*plugin.VerifySignatureResponse, error) { upAttrs := req.Signature.UnprocessedAttributes pAttrs := make([]interface{}, len(upAttrs)) for i := range upAttrs { pAttrs[i] = upAttrs[i] } return &plugin.VerifySignatureResponse{ ProcessedAttributes: pAttrs, VerificationResults: map[plugin.Capability]*plugin.VerificationResult{ plugin.CapabilityTrustedIdentityVerifier: { Success: true, Reason: "Valid trusted Identity", }, plugin.CapabilityRevocationCheckVerifier: { Success: true, Reason: "Not revoked", }, }, }, nil } func (p *ExamplePlugin) GetMetadata(_ context.Context, _ *plugin.GetMetadataRequest) (*plugin.GetMetadataResponse, error) { return &plugin.GetMetadataResponse{ SupportedContractVersions: []string{plugin.ContractVersion}, Name: "com.example.plugin", Description: "This is an description of example plugin", URL: "https://example.com/notation/plugin", Version: "1.0.0", Capabilities: []plugin.Capability{ plugin.CapabilitySignatureGenerator, plugin.CapabilityTrustedIdentityVerifier, plugin.CapabilityRevocationCheckVerifier}, }, nil } notation-plugin-framework-go-1.0.0/go.mod000066400000000000000000000001061455633326000203470ustar00rootroot00000000000000module github.com/notaryproject/notation-plugin-framework-go go 1.20 notation-plugin-framework-go-1.0.0/go.sum000066400000000000000000000000001455633326000203650ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/internal/000077500000000000000000000000001455633326000210605ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/internal/mock/000077500000000000000000000000001455633326000220115ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/internal/mock/plugin.go000066400000000000000000000074121455633326000236420ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. // Package mock contains various mock structures required for testing. package mock import ( "context" "fmt" "github.com/notaryproject/notation-plugin-framework-go/plugin" ) // Mock plugin used only for testing. type mockPlugin struct { fail bool envGenerator bool } func NewPlugin(failPlugin bool) *mockPlugin { return &mockPlugin{ fail: failPlugin, envGenerator: true, } } func NewSigGeneratorPlugin(failPlugin bool) *mockPlugin { return &mockPlugin{ fail: failPlugin, envGenerator: false, } } func (p *mockPlugin) DescribeKey(ctx context.Context, req *plugin.DescribeKeyRequest) (*plugin.DescribeKeyResponse, error) { if p.fail || p.envGenerator { return nil, fmt.Errorf("DescribeKey() expected error") } return &plugin.DescribeKeyResponse{ KeyID: "someKeyId", KeySpec: plugin.KeySpecRSA2048, }, nil } func (p *mockPlugin) GenerateSignature(ctx context.Context, req *plugin.GenerateSignatureRequest) (*plugin.GenerateSignatureResponse, error) { if p.fail || p.envGenerator { return nil, fmt.Errorf("GenerateSignature() expected error") } return &plugin.GenerateSignatureResponse{ KeyID: "someKeyId", Signature: []byte("abcd"), SigningAlgorithm: plugin.SignatureAlgorithmRSASSA_PSS_SHA256, CertificateChain: [][]byte{[]byte("abcd"), []byte("wxyz")}, }, nil } func (p *mockPlugin) GenerateEnvelope(ctx context.Context, req *plugin.GenerateEnvelopeRequest) (*plugin.GenerateEnvelopeResponse, error) { if p.fail || !p.envGenerator { return nil, fmt.Errorf("GenerateEnvelope() expected error") } return &plugin.GenerateEnvelopeResponse{ SignatureEnvelope: []byte(""), SignatureEnvelopeType: req.SignatureEnvelopeType, Annotations: map[string]string{"manifestAnntnKey1": "value1"}, }, nil } func (p *mockPlugin) VerifySignature(ctx context.Context, req *plugin.VerifySignatureRequest) (*plugin.VerifySignatureResponse, error) { if p.fail { return nil, fmt.Errorf("VerifySignature() expected error") } upAttrs := req.Signature.UnprocessedAttributes pAttrs := make([]interface{}, len(upAttrs)) for i := range upAttrs { pAttrs[i] = upAttrs[i] } return &plugin.VerifySignatureResponse{ ProcessedAttributes: pAttrs, VerificationResults: map[plugin.Capability]*plugin.VerificationResult{ plugin.CapabilityTrustedIdentityVerifier: { Success: true, Reason: "Valid trusted Identity", }, plugin.CapabilityRevocationCheckVerifier: { Success: true, Reason: "Not revoked", }, }, }, nil } func (p *mockPlugin) GetMetadata(ctx context.Context, req *plugin.GetMetadataRequest) (*plugin.GetMetadataResponse, error) { if p.fail { return nil, fmt.Errorf("GetMetadata() expected error") } cap := []plugin.Capability{ plugin.CapabilityTrustedIdentityVerifier, plugin.CapabilityRevocationCheckVerifier, } if p.envGenerator { cap = append(cap, plugin.CapabilityEnvelopeGenerator) } else { cap = append(cap, plugin.CapabilitySignatureGenerator) } return &plugin.GetMetadataResponse{ Name: "Example Plugin", Description: "This is an description of example plugin. ๐Ÿบ", URL: "https://example.com/notation/plugin", Version: "1.0.0", Capabilities: cap, }, nil } notation-plugin-framework-go-1.0.0/internal/slices/000077500000000000000000000000001455633326000223425ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/internal/slices/slices.go000066400000000000000000000014141455633326000241530ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package slices // Contains reports whether v is present in s. func Contains[E comparable](s []E, v E) bool { for _, vs := range s { if v == vs { return true } } return false } notation-plugin-framework-go-1.0.0/log/000077500000000000000000000000001455633326000200255ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/log/log.go000066400000000000000000000040241455633326000211350ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. // Package log provides logging functionality. // 3rd party loggers that implement log.Logger: github.com/uber-go/zap.SugaredLogger // and github.com/sirupsen/logrus.Logger. package log // Logger is implemented by users and/or 3rd party loggers. // For example, github.com/uber-go/zap.SugaredLogger // and github.com/sirupsen/logrus.Logger. type Logger interface { // Debug logs a debug level message. Debug(args ...interface{}) // Debugf logs a debug level message with format. Debugf(format string, args ...interface{}) // Debugln logs a debug level message. Spaces are always added between // operands. Debugln(args ...interface{}) // Info logs an info level message. Info(args ...interface{}) // Infof logs an info level message with format. Infof(format string, args ...interface{}) // Infoln logs an info level message. Spaces are always added between // operands. Infoln(args ...interface{}) // Warn logs a warn level message. Warn(args ...interface{}) // Warnf logs a warn level message with format. Warnf(format string, args ...interface{}) // Warnln logs a warn level message. Spaces are always added between // operands. Warnln(args ...interface{}) // Error logs an error level message. Error(args ...interface{}) // Errorf logs an error level message with format. Errorf(format string, args ...interface{}) // Errorln logs an error level message. Spaces are always added between // operands. Errorln(args ...interface{}) } notation-plugin-framework-go-1.0.0/plugin/000077500000000000000000000000001455633326000205425ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/plugin/algorithm.go000066400000000000000000000042211455633326000230560ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin // KeySpec is type of the signing algorithm, including algorithm and size. type KeySpec string // KeySpec supported by notation. // // https://github.com/notaryproject/notaryproject/blob/main/specs/signature-specification.md#algorithm-selection const ( KeySpecRSA2048 KeySpec = "RSA-2048" KeySpecRSA3072 KeySpec = "RSA-3072" KeySpecRSA4096 KeySpec = "RSA-4096" KeySpecEC256 KeySpec = "EC-256" KeySpecEC384 KeySpec = "EC-384" KeySpecEC521 KeySpec = "EC-521" ) // HashAlgorithm supported by notation. type HashAlgorithm string // one of the following supported hash algorithm names. // // https://github.com/notaryproject/notaryproject/blob/main/specs/signature-specification.md#algorithm-selection const ( HashAlgorithmSHA256 HashAlgorithm = "SHA-256" HashAlgorithmSHA384 HashAlgorithm = "SHA-384" HashAlgorithmSHA512 HashAlgorithm = "SHA-512" ) // SignatureAlgorithm supported by notation type SignatureAlgorithm string // one of the following supported signing algorithm names. // // https://github.com/notaryproject/notaryproject/blob/main/specs/signature-specification.md#algorithm-selection const ( SignatureAlgorithmECDSA_SHA256 SignatureAlgorithm = "ECDSA-SHA-256" SignatureAlgorithmECDSA_SHA384 SignatureAlgorithm = "ECDSA-SHA-384" SignatureAlgorithmECDSA_SHA512 SignatureAlgorithm = "ECDSA-SHA-512" SignatureAlgorithmRSASSA_PSS_SHA256 SignatureAlgorithm = "RSASSA-PSS-SHA-256" SignatureAlgorithmRSASSA_PSS_SHA384 SignatureAlgorithm = "RSASSA-PSS-SHA-384" SignatureAlgorithmRSASSA_PSS_SHA512 SignatureAlgorithm = "RSASSA-PSS-SHA-512" ) notation-plugin-framework-go-1.0.0/plugin/describekey.go000066400000000000000000000031361455633326000233650ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin // DescribeKeyRequest contains the parameters passed in a describe-key request. type DescribeKeyRequest struct { ContractVersion string `json:"contractVersion"` KeyID string `json:"keyId"` PluginConfig map[string]string `json:"pluginConfig,omitempty"` } func (DescribeKeyRequest) Command() Command { return CommandDescribeKey } // Validate validates DescribeKeyRequest struct func (r DescribeKeyRequest) Validate() error { if r.ContractVersion == "" { return NewValidationError("contractVersion cannot be empty") } if r.KeyID == "" { return NewValidationError("keyId cannot be empty") } return nil } // DescribeKeyResponse is the response of a describe-key request. type DescribeKeyResponse struct { // The same key id as passed in the request. KeyID string `json:"keyId"` // One of following supported key types: // https://github.com/notaryproject/notaryproject/blob/main/specs/signature-specification.md#algorithm-selection KeySpec KeySpec `json:"keySpec"` } notation-plugin-framework-go-1.0.0/plugin/describekey_test.go000066400000000000000000000042561455633326000244300ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin import ( "fmt" "testing" ) func TestDescribeKeyRequest_Validate(t *testing.T) { reqs := []DescribeKeyRequest{ getDescribeKeyRequest(ContractVersion, "someKeyId"), { ContractVersion: ContractVersion, KeyID: "someKeyId", PluginConfig: map[string]string{"someKey": "someValue"}}, } for _, req := range reqs { if err := req.Validate(); err != nil { t.Errorf("VerifySignatureRequest#Validate failed with error: %+v", err) } } } func TestDescribeKeyRequest_Validate_Error(t *testing.T) { testCases := []struct { name string req DescribeKeyRequest }{ {name: "contractVersion", req: getDescribeKeyRequest("", "someKeyId")}, {name: "keyId", req: getDescribeKeyRequest(ContractVersion, "")}, } for _, testcase := range testCases { t.Run(testcase.name, func(t *testing.T) { if err := testcase.req.Validate(); err != nil { expMsg := fmt.Sprintf("{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"%s cannot be empty\"}", testcase.name) if err.Error() != expMsg { t.Errorf("expected error message '%s' but got '%s'", expMsg, err.Error()) } } else { t.Error("DescribeKeyRequest#Validate didn't returned error") } }) } } func TestDescribeKeyRequest_Command(t *testing.T) { req := getDescribeKeyRequest(ContractVersion, "someKeyId") if cmd := req.Command(); cmd != CommandDescribeKey { t.Errorf("DescribeKeyRequest#Command, expected %s but returned %s", CommandDescribeKey, cmd) } } func getDescribeKeyRequest(cv, kid string) DescribeKeyRequest { return DescribeKeyRequest{ ContractVersion: cv, KeyID: kid, } } notation-plugin-framework-go-1.0.0/plugin/errors.go000066400000000000000000000050711455633326000224100ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin import ( "encoding/json" "fmt" ) type ErrorCode string const ( ErrorCodeValidation ErrorCode = "VALIDATION_ERROR" ErrorCodeUnsupportedContractVersion ErrorCode = "UNSUPPORTED_CONTRACT_VERSION" ErrorCodeAccessDenied ErrorCode = "ACCESS_DENIED" ErrorCodeTimeout ErrorCode = "TIMEOUT" ErrorCodeThrottled ErrorCode = "THROTTLED" ErrorCodeGeneric ErrorCode = "ERROR" ) const ( ErrorMsgMalformedInput string = "Input is not a valid JSON" ErrorMsgMalformedOutputFmt string = "Failed to generate response. Error: %s" ) // Error is used to return a well-formed error response as per NotaryProject specification. type Error struct { ErrCode ErrorCode `json:"errorCode"` Message string `json:"errorMessage,omitempty"` Metadata map[string]string `json:"errorMetadata,omitempty"` } func NewError(code ErrorCode, msg string) *Error { return &Error{ ErrCode: code, Message: msg, } } func NewGenericError(msg string) *Error { return NewError(ErrorCodeGeneric, msg) } func NewGenericErrorf(format string, msg ...any) *Error { return NewError(ErrorCodeGeneric, fmt.Sprintf(format, msg...)) } func NewUnsupportedError(msg string) *Error { return NewError(ErrorCodeValidation, msg+" is not supported") } func NewValidationError(msg string) *Error { return NewError(ErrorCodeValidation, msg) } func NewValidationErrorf(format string, msg ...any) *Error { return NewError(ErrorCodeValidation, fmt.Sprintf(format, msg...)) } func NewUnsupportedContractVersionError(version string) *Error { return NewError(ErrorCodeUnsupportedContractVersion, fmt.Sprintf("%q is not a supported notary plugin contract version", version)) } func NewJSONParsingError(msg string) *Error { return NewValidationError(msg) } // Error returns the formatted error message. func (e *Error) Error() string { op, err := json.Marshal(e) if err != nil { return "something went wrong" } return string(op) } notation-plugin-framework-go-1.0.0/plugin/errors_test.go000066400000000000000000000041261455633326000234470ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin import ( "testing" ) func TestNewError(t *testing.T) { msg := "someMSg" err := NewError(ErrorCodeValidation, msg) if err != nil { if ErrorCodeValidation != err.ErrCode { t.Errorf("NewError, expected errorCode '%s' but found'%s'", ErrorCodeValidation, err.ErrCode) } if msg != err.Message { t.Errorf("NewError, expected message'%s' but found '%s'", msg, err.Message) } if err.Metadata != nil { t.Errorf("NewError, expected metadata to be nil but found '%s'", err.Metadata) } expError := "{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"someMSg\"}" if expError != err.Error() { t.Errorf("NewError#Error, expected error to be '%s' but found '%s'", expError, err.Error()) } } else { t.Error("NewError didn't return an error") } } func TestErrorCodes(t *testing.T) { testCases := []struct { err *Error errCode ErrorCode }{ {err: NewValidationError(""), errCode: ErrorCodeValidation}, {err: NewValidationErrorf("%s", ""), errCode: ErrorCodeValidation}, {err: NewUnsupportedError(""), errCode: ErrorCodeValidation}, {err: NewGenericError(""), errCode: ErrorCodeGeneric}, {err: NewGenericErrorf("%s", ""), errCode: ErrorCodeGeneric}, {err: NewJSONParsingError(""), errCode: ErrorCodeValidation}, {err: NewUnsupportedContractVersionError(""), errCode: ErrorCodeUnsupportedContractVersion}, } for _, test := range testCases { if test.errCode != test.err.ErrCode { t.Errorf("Expected errorCode %s but found %s", test.errCode, test.err.ErrCode) } } } notation-plugin-framework-go-1.0.0/plugin/metadata.go000066400000000000000000000033711455633326000226550ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin // GetMetadataRequest contains the parameters passed in a get-plugin-metadata // request. type GetMetadataRequest struct { PluginConfig map[string]string `json:"pluginConfig,omitempty"` } func (GetMetadataRequest) Command() Command { return CommandGetMetadata } // Validate validates GetMetadataRequest struct func (GetMetadataRequest) Validate() error { return nil } // GetMetadataResponse provided by the plugin. type GetMetadataResponse struct { Name string `json:"name"` Description string `json:"description"` Version string `json:"version"` URL string `json:"url"` SupportedContractVersions []string `json:"supportedContractVersions,omitempty"` Capabilities []Capability `json:"capabilities"` } // HasCapability return true if the metadata states that the // capability is supported. // Returns true if capability is empty. func (resp *GetMetadataResponse) HasCapability(capability Capability) bool { if capability == "" { return true } for _, c := range resp.Capabilities { if c == capability { return true } } return false } notation-plugin-framework-go-1.0.0/plugin/metadata_test.go000066400000000000000000000037001455633326000237100ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin import ( "testing" ) func TestGetMetadataResponseHasCapability(t *testing.T) { type args struct { capability Capability } tests := []struct { name string m *GetMetadataResponse args args want bool }{ {"empty capabilities", &GetMetadataResponse{}, args{"cap"}, false}, {"other capabilities", &GetMetadataResponse{Capabilities: []Capability{"foo", "baz"}}, args{"cap"}, false}, {"empty target capability", &GetMetadataResponse{Capabilities: []Capability{"foo", "baz"}}, args{""}, true}, {"found", &GetMetadataResponse{Capabilities: []Capability{"foo", "baz"}}, args{"baz"}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.m.HasCapability(tt.args.capability); got != tt.want { t.Errorf("GetMetadataResponse.HasCapability() = %v, want %v", got, tt.want) } }) } } func TestGetMetadataRequest_Validate(t *testing.T) { reqs := []GetMetadataRequest{ {}, {PluginConfig: map[string]string{"key1": "value1"}}, } for _, req := range reqs { if err := req.Validate(); err != nil { t.Errorf("GetMetadataRequest#Validate failed with error: %+v", err) } } } func TestGetMetadataRequest_Command(t *testing.T) { req := GetMetadataRequest{} if cmd := req.Command(); cmd != CommandGetMetadata { t.Errorf("DescribeKeyRequest#Command, expected %s but returned %s", CommandGetMetadata, cmd) } } notation-plugin-framework-go-1.0.0/plugin/plugin.go000066400000000000000000000037231455633326000223740ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. // Package plugin provides the tooling to use the notation plugin. // // includes a CLIManager and a CLIPlugin implementation. package plugin import ( "context" ) // GenericPlugin is the base requirement to be a plugin. type GenericPlugin interface { // GetMetadata returns the metadata information of the plugin. GetMetadata(ctx context.Context, req *GetMetadataRequest) (*GetMetadataResponse, error) } // SignPlugin defines the required methods to be a SignPlugin. type SignPlugin interface { GenericPlugin // DescribeKey returns the KeySpec of a key. DescribeKey(ctx context.Context, req *DescribeKeyRequest) (*DescribeKeyResponse, error) // GenerateSignature generates the raw signature based on the request. GenerateSignature(ctx context.Context, req *GenerateSignatureRequest) (*GenerateSignatureResponse, error) // GenerateEnvelope generates the Envelope with signature based on the // request. GenerateEnvelope(ctx context.Context, req *GenerateEnvelopeRequest) (*GenerateEnvelopeResponse, error) } // VerifyPlugin defines the required method to be a VerifyPlugin. type VerifyPlugin interface { GenericPlugin // VerifySignature validates the signature based on the request. VerifySignature(ctx context.Context, req *VerifySignatureRequest) (*VerifySignatureResponse, error) } // Plugin defines required methods to be a Plugin. type Plugin interface { SignPlugin VerifyPlugin } notation-plugin-framework-go-1.0.0/plugin/proto.go000066400000000000000000000060761455633326000222450ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. // Package plugin defines the protocol layer for communication between notation // and notation external plugin. package plugin // BinaryPrefix is the prefix required on all plugin binary names. const BinaryPrefix = "notation-" // ContractVersion is the . version of the plugin contract. const ContractVersion = "1.0" // Capability is a feature available in the plugin contract. type Capability string const ( // CapabilitySignatureGenerator is the name of the capability // for a plugin to support generating raw signatures. CapabilitySignatureGenerator Capability = "SIGNATURE_GENERATOR.RAW" // CapabilityEnvelopeGenerator is the name of the capability // for a plugin to support generating envelope signatures. CapabilityEnvelopeGenerator Capability = "SIGNATURE_GENERATOR.ENVELOPE" // CapabilityTrustedIdentityVerifier is the name of the // capability for a plugin to support verifying trusted identities. CapabilityTrustedIdentityVerifier Capability = "SIGNATURE_VERIFIER.TRUSTED_IDENTITY" // CapabilityRevocationCheckVerifier is the name of the // capability for a plugin to support verifying revocation checks. CapabilityRevocationCheckVerifier Capability = "SIGNATURE_VERIFIER.REVOCATION_CHECK" ) // Command is a CLI command available in the plugin contract. type Command string // Request defines a plugin request, which is always associated to a command. type Request interface { Command() Command Validate() error } const ( // CommandGetMetadata is the name of the plugin command // which must be supported by every plugin and returns the // plugin metadata. CommandGetMetadata Command = "get-plugin-metadata" // CommandDescribeKey is the name of the plugin command // which must be supported by every plugin that has the // SIGNATURE_GENERATOR.RAW capability. CommandDescribeKey Command = "describe-key" // CommandGenerateSignature is the name of the plugin command // which must be supported by every plugin that has the // SIGNATURE_GENERATOR.RAW capability. CommandGenerateSignature Command = "generate-signature" // CommandGenerateEnvelope is the name of the plugin command // which must be supported by every plugin that has the // SIGNATURE_GENERATOR.ENVELOPE capability. CommandGenerateEnvelope Command = "generate-envelope" // CommandVerifySignature is the name of the plugin command // which must be supported by every plugin that has // any SIGNATURE_VERIFIER.* capability CommandVerifySignature Command = "verify-signature" Version Command = "version" ) notation-plugin-framework-go-1.0.0/plugin/sign.go000066400000000000000000000073021455633326000220330ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin // GenerateSignatureRequest contains the parameters passed in a // generate-signature request. type GenerateSignatureRequest struct { ContractVersion string `json:"contractVersion"` KeyID string `json:"keyId"` KeySpec KeySpec `json:"keySpec"` Hash HashAlgorithm `json:"hashAlgorithm"` Payload []byte `json:"payload"` PluginConfig map[string]string `json:"pluginConfig,omitempty"` } func (GenerateSignatureRequest) Command() Command { return CommandGenerateSignature } // Validate validates GenerateSignatureRequest struct func (r GenerateSignatureRequest) Validate() error { if r.ContractVersion == "" { return NewValidationError("contractVersion cannot be empty") } if r.KeyID == "" { return NewValidationError("keyId cannot be empty") } if r.KeySpec == "" { return NewValidationError("keySpec cannot be empty") } if r.Hash == "" { return NewValidationError("hashAlgorithm cannot be empty") } if len(r.Payload) == 0 { return NewValidationError("payload cannot be empty") } return nil } // GenerateSignatureResponse is the response of a generate-signature request. type GenerateSignatureResponse struct { KeyID string `json:"keyId"` Signature []byte `json:"signature"` SigningAlgorithm SignatureAlgorithm `json:"signingAlgorithm"` // Ordered list of certificates starting with leaf certificate // and ending with root certificate. CertificateChain [][]byte `json:"certificateChain"` } // GenerateEnvelopeRequest contains the parameters passed in a generate-envelope // request. type GenerateEnvelopeRequest struct { ContractVersion string `json:"contractVersion"` KeyID string `json:"keyId"` PayloadType string `json:"payloadType"` SignatureEnvelopeType string `json:"signatureEnvelopeType"` Payload []byte `json:"payload"` ExpiryDurationInSeconds uint64 `json:"expiryDurationInSeconds,omitempty"` PluginConfig map[string]string `json:"pluginConfig,omitempty"` } func (GenerateEnvelopeRequest) Command() Command { return CommandGenerateEnvelope } // Validate validates GenerateEnvelopeRequest struct func (r GenerateEnvelopeRequest) Validate() error { if r.ContractVersion == "" { return NewValidationError("contractVersion cannot be empty") } if r.KeyID == "" { return NewValidationError("keyId cannot be empty") } if r.PayloadType == "" { return NewValidationError("payloadType cannot be empty") } if r.SignatureEnvelopeType == "" { return NewValidationError("signatureEnvelopeType cannot be empty") } if len(r.Payload) == 0 { return NewValidationError("payload cannot be empty") } return nil } // GenerateEnvelopeResponse is the response of a generate-envelope request. type GenerateEnvelopeResponse struct { SignatureEnvelope []byte `json:"signatureEnvelope"` SignatureEnvelopeType string `json:"signatureEnvelopeType"` Annotations map[string]string `json:"annotations,omitempty"` } notation-plugin-framework-go-1.0.0/plugin/sign_test.go000066400000000000000000000137161455633326000231000ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin import ( "fmt" "testing" ) func TestGenerateSignatureRequest_Validate(t *testing.T) { reqs := []GenerateSignatureRequest{ getGenerateSignatureRequest(ContractVersion, "someKeyId", string(KeySpecEC384), string(HashAlgorithmSHA384), []byte("zop")), { ContractVersion: ContractVersion, KeyID: "someKeyId", KeySpec: KeySpecEC384, Hash: HashAlgorithmSHA384, Payload: []byte("somePayload"), PluginConfig: map[string]string{"key1": "value1"}, }, } for _, req := range reqs { if err := req.Validate(); err != nil { t.Errorf("VerifySignatureRequest#Validate failed with error: %+v", err) } } } func TestGenerateSignatureRequest_Validate_Error(t *testing.T) { testCases := []struct { name string req GenerateSignatureRequest }{ {name: "contractVersion", req: getGenerateSignatureRequest("", "someKeyId", string(KeySpecEC384), string(HashAlgorithmSHA384), []byte("zop"))}, {name: "keyId", req: getGenerateSignatureRequest(ContractVersion, "", string(KeySpecEC384), string(HashAlgorithmSHA384), []byte("zop"))}, {name: "keySpec", req: getGenerateSignatureRequest(ContractVersion, "someKeyId", "", string(HashAlgorithmSHA384), []byte("zop"))}, {name: "hashAlgorithm", req: getGenerateSignatureRequest(ContractVersion, "someKid", string(KeySpecEC384), "", []byte("zop"))}, {name: "payload", req: getGenerateSignatureRequest(ContractVersion, "someKeyId", string(KeySpecEC384), string(HashAlgorithmSHA384), []byte(""))}, {name: "payload", req: getGenerateSignatureRequest(ContractVersion, "someKeyId", string(KeySpecEC384), string(HashAlgorithmSHA384), nil)}, {name: "keySpec", req: getGenerateSignatureRequest(ContractVersion, "someKeyId", "", string(HashAlgorithmSHA384), []byte("zop"))}, } for _, testcase := range testCases { t.Run(testcase.name, func(t *testing.T) { if err := testcase.req.Validate(); err != nil { expMsg := fmt.Sprintf("{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"%s cannot be empty\"}", testcase.name) if err.Error() != expMsg { t.Errorf("expected error message '%s' but got '%s'", expMsg, err.Error()) } } else { t.Error("VerifySignatureRequest#Validate didn't returned error") } }) } } func TestGenerateSignatureRequest_Command(t *testing.T) { req := getGenerateSignatureRequest(ContractVersion, "someKeyId", string(KeySpecEC384), string(HashAlgorithmSHA384), []byte("zop")) if cmd := req.Command(); cmd != CommandGenerateSignature { t.Errorf("DescribeKeyRequest#Command, expected %s but returned %s", CommandGenerateSignature, cmd) } } func TestGenerateEnvelopeRequest_Validate(t *testing.T) { reqs := []GenerateEnvelopeRequest{ getGenerateEnvelopeRequest(ContractVersion, "someKeyId", "someSET", "somePT", []byte("zop")), { ContractVersion: ContractVersion, KeyID: "someKeyId", SignatureEnvelopeType: "someType", ExpiryDurationInSeconds: 1, Payload: []byte("zap"), PayloadType: ContractVersion, PluginConfig: map[string]string{"key1": "value1"}, }, } for _, req := range reqs { if err := req.Validate(); err != nil { t.Errorf("GenerateEnvelopeRequest#Validate failed with error: %+v", err) } } } func TestGenerateEnvelopeRequest_Validate_Error(t *testing.T) { testCases := []struct { name string req GenerateEnvelopeRequest }{ {name: "contractVersion", req: getGenerateEnvelopeRequest("", "someKeyId", "someSET", "somePT", []byte("zop"))}, {name: "keyId", req: getGenerateEnvelopeRequest(ContractVersion, "", "someSET", "somePT", []byte("zop"))}, {name: "signatureEnvelopeType", req: getGenerateEnvelopeRequest(ContractVersion, "someKeyId", "", "somePT", []byte("zop"))}, {name: "payloadType", req: getGenerateEnvelopeRequest(ContractVersion, "someKeyId", "someSET", "", []byte("zop"))}, {name: "payload", req: getGenerateEnvelopeRequest(ContractVersion, "someKeyId", "someSET", "somePT", []byte(""))}, {name: "payload", req: getGenerateEnvelopeRequest(ContractVersion, "someKeyId", "someSET", "somePT", nil)}, } for _, testcase := range testCases { t.Run(testcase.name, func(t *testing.T) { if err := testcase.req.Validate(); err != nil { expMsg := fmt.Sprintf("{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"%s cannot be empty\"}", testcase.name) if err.Error() != expMsg { t.Errorf("expected error message '%s' but got '%s'", expMsg, err.Error()) } } else { t.Error("GenerateEnvelopeRequest#Validate didn't returned error") } }) } } func TestGenerateEnvelopeRequest_Command(t *testing.T) { req := getGenerateEnvelopeRequest(ContractVersion, "someKeyId", string(KeySpecEC384), string(HashAlgorithmSHA384), []byte("zop")) if cmd := req.Command(); cmd != CommandGenerateEnvelope { t.Errorf("DescribeKeyRequest#Command, expected %s but returned %s", CommandGenerateEnvelope, cmd) } } func getGenerateSignatureRequest(cv, kid, ks, ha string, pl []byte) GenerateSignatureRequest { return GenerateSignatureRequest{ ContractVersion: cv, KeyID: kid, KeySpec: KeySpec(ks), Hash: HashAlgorithm(ha), Payload: pl, } } func getGenerateEnvelopeRequest(cv, kid, set, pt string, pl []byte) GenerateEnvelopeRequest { return GenerateEnvelopeRequest{ ContractVersion: cv, KeyID: kid, SignatureEnvelopeType: set, PayloadType: pt, Payload: pl, } } notation-plugin-framework-go-1.0.0/plugin/verify.go000066400000000000000000000073541455633326000224060ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin import ( "reflect" "time" ) // VerifySignatureRequest contains the parameters passed in a verify-signature // request. type VerifySignatureRequest struct { ContractVersion string `json:"contractVersion"` Signature Signature `json:"signature"` TrustPolicy TrustPolicy `json:"trustPolicy"` PluginConfig map[string]string `json:"pluginConfig,omitempty"` } func (VerifySignatureRequest) Command() Command { return CommandVerifySignature } // Validate validates VerifySignatureRequest struct func (r VerifySignatureRequest) Validate() error { if r.ContractVersion == "" { return NewValidationError("contractVersion cannot be empty") } if reflect.DeepEqual(r.Signature, Signature{}) { return NewValidationError("signature cannot be empty") } if reflect.DeepEqual(r.Signature.CriticalAttributes, CriticalAttributes{}) { return NewValidationError("signature's criticalAttributes cannot be empty") } if r.Signature.CriticalAttributes.ContentType == "" { return NewValidationError("signature's criticalAttributes's contentType cannot be empty") } if r.Signature.CriticalAttributes.SigningScheme == "" { return NewValidationError("signature's criticalAttributes's signingScheme cannot be empty") } if len(r.Signature.CertificateChain) == 0 { return NewValidationError("signature's criticalAttributes's certificateChain cannot be empty") } if reflect.DeepEqual(r.TrustPolicy, TrustPolicy{}) { return NewValidationError("signature's trustPolicy cannot be empty") } if len(r.TrustPolicy.SignatureVerification) == 0 { return NewValidationError("signature's trustPolicy's signatureVerification cannot be empty") } return nil } // Signature represents a signature pulled from the envelope type Signature struct { CriticalAttributes CriticalAttributes `json:"criticalAttributes"` UnprocessedAttributes []string `json:"unprocessedAttributes"` CertificateChain [][]byte `json:"certificateChain"` } // CriticalAttributes contains all critical attributes and // their values in the signature envelope type CriticalAttributes struct { ContentType string `json:"contentType"` SigningScheme string `json:"signingScheme"` Expiry *time.Time `json:"expiry,omitempty"` AuthenticSigningTime *time.Time `json:"authenticSigningTime,omitempty"` ExtendedAttributes map[string]interface{} `json:"extendedAttributes,omitempty"` } // TrustPolicy represents trusted identities that sign the artifacts type TrustPolicy struct { TrustedIdentities []string `json:"trustedIdentities"` SignatureVerification []Capability `json:"signatureVerification"` } // VerifySignatureResponse is the response of a verify-signature request. type VerifySignatureResponse struct { VerificationResults map[Capability]*VerificationResult `json:"verificationResults"` ProcessedAttributes []interface{} `json:"processedAttributes"` } // VerificationResult is the result of a verification performed by the plugin type VerificationResult struct { Success bool `json:"success"` Reason string `json:"reason,omitempty"` } notation-plugin-framework-go-1.0.0/plugin/verify_test.go000066400000000000000000000114071455633326000234370ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package plugin import ( "fmt" "testing" ) var mockCertChain = [][]byte{[]byte("zap"), []byte("zop")} func TestVerifySignatureRequest_Validate(t *testing.T) { reqs := []VerifySignatureRequest{ getVerifySignatureRequest(ContractVersion, "someCT", "someSigningScheme", mockCertChain, []Capability{CapabilitySignatureGenerator}), { ContractVersion: "2.0", Signature: Signature{ CriticalAttributes: CriticalAttributes{ ContentType: "someCT", SigningScheme: "someSigningScheme", Expiry: nil, AuthenticSigningTime: nil, ExtendedAttributes: nil, }, UnprocessedAttributes: []string{"upa1", "upa2"}, CertificateChain: mockCertChain, }, TrustPolicy: TrustPolicy{ TrustedIdentities: []string{"trustedIdentity1", "trustedIdentity2"}, SignatureVerification: []Capability{CapabilitySignatureGenerator, CapabilityRevocationCheckVerifier}, }, PluginConfig: map[string]string{"someKey": "someValue"}, }, } for _, req := range reqs { if err := req.Validate(); err != nil { t.Errorf("VerifySignatureRequest#Validate failed with error: %+v", err) } } } func TestVerifySignatureRequest_Validate_Error(t *testing.T) { reqWithoutSignature := getVerifySignatureRequest("1.0", "someCT", "someSigningScheme", mockCertChain, []Capability{CapabilitySignatureGenerator}) reqWithoutSignature.Signature = Signature{} reqWithoutCriticalAttr := getVerifySignatureRequest("1.0", "someCT", "someSigningScheme", mockCertChain, []Capability{CapabilitySignatureGenerator}) reqWithoutCriticalAttr.Signature.CriticalAttributes = CriticalAttributes{} testCases := []struct { name string req VerifySignatureRequest }{ {name: "contractVersion", req: getVerifySignatureRequest("", "someCT", "someSigningScheme", mockCertChain, []Capability{CapabilitySignatureGenerator})}, {name: "signature's criticalAttributes's contentType", req: getVerifySignatureRequest(ContractVersion, "", "someSigningScheme", mockCertChain, []Capability{CapabilitySignatureGenerator})}, {name: "signature's criticalAttributes's signingScheme", req: getVerifySignatureRequest(ContractVersion, "someCT", "", mockCertChain, []Capability{CapabilitySignatureGenerator})}, {name: "signature's criticalAttributes's certificateChain", req: getVerifySignatureRequest(ContractVersion, "someCT", "someSigningScheme", [][]byte{}, []Capability{CapabilitySignatureGenerator})}, {name: "signature's criticalAttributes's certificateChain", req: getVerifySignatureRequest(ContractVersion, "someCT", "someSigningScheme", nil, []Capability{CapabilitySignatureGenerator})}, {name: "signature's trustPolicy", req: getVerifySignatureRequest(ContractVersion, "someCT", "someSigningScheme", mockCertChain, nil)}, {name: "signature's trustPolicy's signatureVerification", req: getVerifySignatureRequest(ContractVersion, "someCT", "someSigningScheme", mockCertChain, []Capability{})}, {name: "signature", req: reqWithoutSignature}, {name: "signature's criticalAttributes", req: reqWithoutCriticalAttr}, } for _, testcase := range testCases { t.Run(testcase.name, func(t *testing.T) { if err := testcase.req.Validate(); err != nil { expMsg := fmt.Sprintf("{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"%s cannot be empty\"}", testcase.name) if err.Error() != expMsg { t.Errorf("expected error message '%s' but got '%s'", expMsg, err.Error()) } } else { t.Error("VerifySignatureRequest#Validate didn't returned error") } }) } } func TestVerifySignatureRequest_Command(t *testing.T) { req := getVerifySignatureRequest(ContractVersion, "someCT", "someSigningScheme", mockCertChain, []Capability{CapabilitySignatureGenerator}) if cmd := req.Command(); cmd != CommandVerifySignature { t.Errorf("DescribeKeyRequest#Command, expected %s but returned %s", CommandVerifySignature, cmd) } } func getVerifySignatureRequest(cv, ct, ss string, cc [][]byte, sv []Capability) VerifySignatureRequest { return VerifySignatureRequest{ ContractVersion: cv, Signature: Signature{ CriticalAttributes: CriticalAttributes{ ContentType: ct, SigningScheme: ss, }, CertificateChain: cc, }, TrustPolicy: TrustPolicy{ SignatureVerification: sv, }, } } notation-plugin-framework-go-1.0.0/test/000077500000000000000000000000001455633326000202235ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/test/e2e/000077500000000000000000000000001455633326000206765ustar00rootroot00000000000000notation-plugin-framework-go-1.0.0/test/e2e/cli_test.go000066400000000000000000000271751455633326000230470ustar00rootroot00000000000000// Copyright The Notary Project Authors. // 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. package e2e import ( "encoding/json" "flag" "io" "os/exec" "reflect" "strings" "testing" ) var sigGenPluginPath = flag.String("sig_gen_plugin", "./bin/signaturegenerator/com.example.plugin", "dir of package containing embedded files") var envGenPluginPath = flag.String("env_gen_plugin", "./bin/envelopegenerator/com.example.plugin", "dir of package containing embedded files") func TestSuccess(t *testing.T) { tests := map[string]struct { pluginPath *string stdin string expectedStdout string }{ "generate-envelope": { pluginPath: envGenPluginPath, stdin: "{\"contractVersion\":\"1.0\",\"keyId\":\"arn:aws:signer:us-west-2:951584113157:/signing-profiles/ECR\",\"payloadType\":\"application/vnd.cncf.notary.payload.v1+json\",\"signatureEnvelopeType\":\"application/jose+json\",\"payload\":\"eyJ0YXJnZXRBcnRpZmFjdCI6eyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC5saXN0LnYyK2pzb24iLCJkaWdlc3QiOiJzaGEyNTY6ZGEyN2I3NDAwOGJmYTQ5YTYyNWZhODVmNjZkMTJhMmY3YzE3ZGM0OTY4ZmJhZTZjOWRiNmU2N2ZkZDRmMjM4MiIsInNpemUiOjY4M319\"}", expectedStdout: "{\"signatureEnvelope\":\"ZXlKd1lYbHNiMkZrSWpvaVpYbEtNRmxZU201YVdGSkNZMjVTY0ZwdFJtcGtRMGsyWlhsS2EyRlhaR3hqTTFGcFQybEtlbUZIUlhsT1ZGazJXbTFWTTFwVWEzcE5lazE2VDFSVmQwNXFRbXBOYlZreFdsUlplbGt5V1hwT2JVVjZUMGRhYVZsVVJYZE5WR015V21wRk5FMHlTVEJOVkZsNldWUlZNMDlVVW14TlJHZDRXVlJSTkUxSFJtbFpiVVV4V21sSmMwbHRNV3hhUjJ4b1ZraHNkMXBUU1RaSmJVWjNZMGQ0Y0ZreVJqQmhWemwxVEROYWRWcEROV3RpTWs1eVdsaEpkVnBIYkhwa1NFcHdXVzVXTUdGWE9YVk1iVEZvWW0xc2JWcFlUakJNYmxsNVN6SndlbUl5TkdsTVEwcDZZVmh3YkVscWJ6Vk9SRW81WmxFaUxDSndjbTkwWldOMFpXUWlPaUpsZVVwb1lrZGphVTlwU2xGVmVra3hUbWxKYzBsdFRubGhXRkZwVDJ4emFXRlhPSFZaTWpWcVdtazFkV0l6VW1oamJtdDFZekpzYm1KdGJIVmFNVTVxWVVkV2RGcFRTWE5KYld4MlRHMU9kVmt5V1hWaWJUa3dXVmhLTlV4dVdteGpiV3h0WVZkT2FHUkhiSFppYkVKelpGZGtjR0pyTVhCaWJGcHNZMjVPY0dJeU5HbE1RMHB3WW5rMWFtSnRUbTFNYlRWMlpFZEdlV1ZUTlRKYVdFcHdXbTFzYWxsWVVuQmlNalZSWWtoV2JtRlhOR2xZVTNkcFdUTlNOVWxxYjJsWldFSjNZa2RzYWxsWVVuQmlNalIyWkcwMWEweHRUblZaTWxsMVltMDVNRmxZU2pWTWJrSm9aVmQ0ZGxsWFVYVmtha1Z5WVc1T2RtSnBTWE5KYld4MlRHMU9kVmt5V1hWaWJUa3dXVmhLTlV4dVRuQmFNalZ3WW0xa1ZGa3lhR3hpVjFWcFQybEtkV0l6VW1oamJtdDFaVVJWZDA5VFNYTkpiV3gyVEcxT2RWa3lXWFZpYlRrd1dWaEtOVXh1VG5CYU1qVndZbTFrVldGWE1XeEphbTlwVFdwQmVVMTVNSGROVXpCNFQxWlJlRTE2YjNkTmVtOTVUWGt3ZDA5RWIzZE5RMGx6U1cxc2RreHRUblZaTWxsMVltMDVNRmxZU2pWTWJscHNZMjFzYldGWFRtaGtSMngyWW14Q2MyUlhaSEJpYVVrMlNXMXNka3h0VG5WWk1sbDFZbTA1TUZsWVNqVk1ia0p6WkZka2NHSnBOVEZpYld3d1pFZFdlbVJETlhSaU1rNXlTV2wzYVdGWE9IVlpNalZxV21rMWRXSXpVbWhqYm10MVpHMVdlV0ZYV25CWk1rWXdZVmM1ZFZWSGVERmFNbXgxVkZkc2RWWnRWbmxqTW14MlltbEpOa2xxUlhWTlF6UjNURmRHYzJOSGFHaE1iVXBzWkVkRmFXWlJJaXdpYUdWaFpHVnlJanA3SW5nMVl5STZXeUpOU1VsRVZtcERRMEZxTm1kQmQwbENRV2RKUWxWVVFVNUNaMnR4YUd0cFJ6bDNNRUpCVVhOR1FVUkNZVTFSYzNkRFVWbEVWbEZSUjBWM1NsWlZla1ZNVFVGclIwRXhWVVZEUWsxRFZqQkZlRVZFUVU5Q1owNVdRa0ZqVkVJeFRteFpXRkl3WWtkVmVFUjZRVTVDWjA1V1FrRnZWRUpyTlhaa1IwWjVaVlJGWWsxQ2EwZEJNVlZGUVhoTlUyUXlSbWxaYld3d1RGYzFiR1JJWkhaamJYUjZURzFzZGsxQ05GaEVWRWw2VFVSRmVFOVVRVFJOVkd0M1RqRnZXRVJVVFhwTlJFVjRUMVJCTkUxVWEzZE9NVzkzVjJwRlRFMUJhMGRCTVZWRlFtaE5RMVpXVFhoRGVrRktRbWRPVmtKQloxUkJiR1JDVFZKQmQwUm5XVVJXVVZGSVJYZGtWRnBYUmpCa1IzaHNUVkU0ZDBSUldVUldVVkZMUlhkYVQySXpVbWhqYm10NFIzcEJXa0puVGxaQ1FVMVVSVzVrYUZsdFNuQmtRekYxV2xoU00ySXpTbkpqZVRWd1lucERRMEZUU1hkRVVWbEtTMjlhU1doMlkwNUJVVVZDUWxGQlJHZG5SVkJCUkVORFFWRnZRMmRuUlVKQlRraG9iRkFyVTJsWk4yaHpSMnhtTW0xQlJFOTZTbGN2U2psemFYRk5hMmxSZGxOUGVEQlBVMDB5ZVhobGRHWldVVXd2WVdKcE5HbHhRMWhOTm5kclUzaDJhVUpsVG5kSmIxbEZjelIwYUUxQk9FNUhSV0p1UzI5WWEzUjVhRGwyYldsTVFqRkdWemRJU0hJMFVVeDNhbWRNZW1kWFNrdEpVVlI1TVVwdFJFSmxZMWhhYURVMlpEQm1NM2N6V1dveFNVUlVkbXRKVTJOWVEwNUpLelYyTHpBNFIxVlJTMmg1UW5kMk4wWnhPVTFaY0c4eWJHWllVMGszVmpNelFrdExaR1JZU1hoUVIxWlhkMHRIZGxCRk1ITm5NbFpXTjFkTk9EUmFXa3hrUkV0Nk1tMXhNRkIwVUZSSWNsTjNaek5vYkVzdmJXcHVLMkpzWnpObmMxbFJOR2c1THpkYU5tNU9ZVVk1V0RCVFpIbEZVMnc0TkRGYVYzSjBUV2hCVDBad1NYcE1Zbm81WlhSbE9FNVNaRE5pV1VOU1FrbHlOV2R6WTBoWFZHWTJiSGxWWjNrMGVIcHpVM2ROU0ZCelIweE5ORUVyV2pBd1EwRjNSVUZCWVUxdVRVTlZkMFJuV1VSV1VqQlFRVkZJTDBKQlVVUkJaMlZCVFVKTlIwRXhWV1JLVVZGTlRVRnZSME5EYzBkQlVWVkdRbmROUkUxQk1FZERVM0ZIVTBsaU0wUlJSVUpEZDFWQlFUUkpRa0ZSUVdKT01FVnlkVFUyZFZSUlUwTXlPRnBVWmpoRU4xWjVRMnRaY25KWFRGbHBTazFaWkU5TFFucDZTMVk1YlV0aFRUQlBSMFl5ZFhsWGQwUmhVSGh3T1V0VVpFeFliVUp3T1VWR2NUVlRXRmhCY2taQksyNVNVemRMYVc1RVFXVXlUemRCTHpsVGRHUXlXR3BMYVRreU4zSnJRVEpqYWpJek9XUTFiRkp6YWxkWWNVcFlaamwyUVUxV09XRXlSbXBWVFM5cGJqSkZaWFpzY1RkaWRtcEdSVE5zTWpaV1dFTkxkRTl6T1VWeWJXWjRja3dyTmtWVVVrdFRWbGxQVDBjdmNsTklSbll2VTBJeVRXeHhSR2MxVVhOWVF6bHNXbXA2VERVdldDOXBiMlV5Y1ZwTGFIQTJXRFZFVUhCaFpERnhNVkUwU1hSTFpGUk9LekpGV0hsTmVXOUliakZDU2t0T1ltRTNRMVZWZGxobU1ETkZTbVZpVkM5SmJTdHhiM3BtUld0elNtVmFTbFZUYkZOMWFrRk9WVkJ2UTNCelJWbEhWMWRSZURWSEsxWnBSekExVTNGekt6WndjRXR5ZFhRclVDdEVWbEJ2SWwwc0ltbHZMbU51WTJZdWJtOTBZWEo1TG5OcFoyNXBibWRCWjJWdWRDSTZJazV2ZEdGMGFXOXVMekV1TUM0d0luMHNJbk5wWjI1aGRIVnlaU0k2SW1sS2RHaDBjV0o2TUU4MWJrWjFielZhT1c1U1pHUkZhbmxhY0ROU1J5MUxUMWsyVTFOQ00zTmpPRUZuUkVKa1ZEVkdhbkE1ZVd4MFNXOXhWR3d0UWt4YWFISkhUMEZHWlU4d1ZGOHhTbFp6VUdKYVdrMTRla3B4TkdaaU0yZFFZVWxRU1hSeVpXNWthM0JwZERGdE1sSmhRamhtU3pGRVgwazJWbkYxTVY5eVIybFpZWGhFWTA1d1lYRnVNVlJmYVhONGNqUk5WbEpsYTJOTVUwNVJia2N6YVUxa1NqQnJMVUYwZEdZNFNtUkRXRVV3UlZkTGVVeENVM1JOVmtGbWJ6QktNemxUYUVaamQzbEpUWFpQTUhadE1sOVVVa1JXWW1OTGIzWndXVEIyUm5KbWVVVXljRVpKUTJodVNrVkRiV2wyU1cxa1MyMUNUVWxYTnpoMlJYUk9ObkZDY2t0emEwa3pTSHBCT1U0eFdHcDRSMWswUjA5QmRUTXdhWEYwVGxKaGJrODJOVzVhUjI1bk1HeHhjRXBrTVRWaVFYZFZZWEZxTFV0RVgwSkJXa2xWVkRsVU1uRkRaakpEVDBZNVIwdDJZek5PVVNKOUNnPT0=\",\"signatureEnvelopeType\":\"application/jose+json\",\"annotations\":{\"manifestAnntnKey1\":\"value1\"}}", }, "get-plugin-metadata": { pluginPath: envGenPluginPath, stdin: "{}", expectedStdout: "{\"name\":\"com.example.plugin\",\"description\":\"This is an description of example plugin\",\"version\":\"1.0.0\",\"url\":\"https://example.com/notation/plugin\",\"supportedContractVersions\":[\"1.0\"],\"capabilities\":[\"SIGNATURE_GENERATOR.ENVELOPE\",\"SIGNATURE_VERIFIER.TRUSTED_IDENTITY\",\"SIGNATURE_VERIFIER.REVOCATION_CHECK\"]}"}, "verify-signature": { pluginPath: envGenPluginPath, stdin: "{\"contractVersion\":\"1.0\",\"signature\":{\"criticalAttributes\":{\"contentType\":\"someCT\",\"signingScheme\":\"someSigningScheme\"},\"unprocessedAttributes\":null,\"certificateChain\":[\"emFw\",\"em9w\"]},\"trustPolicy\":{\"trustedIdentities\":null,\"signatureVerification\":[\"SIGNATURE_GENERATOR.RAW\"]}}", expectedStdout: "{\"verificationResults\":{\"SIGNATURE_VERIFIER.REVOCATION_CHECK\":{\"success\":true,\"reason\":\"Not revoked\"},\"SIGNATURE_VERIFIER.TRUSTED_IDENTITY\":{\"success\":true,\"reason\":\"Valid trusted Identity\"}},\"processedAttributes\":[]}"}, "version": { pluginPath: envGenPluginPath, stdin: "", expectedStdout: "com.example.plugin - This is an description of example plugin\nVersion: 1.0.0\n", }, "generate-signature": { pluginPath: sigGenPluginPath, stdin: "{\"contractVersion\":\"1.0\",\"keyId\":\"someKeyId\",\"keySpec\":\"EC-384\",\"hashAlgorithm\":\"SHA-384\",\"payload\":\"em9w\"}", expectedStdout: "{\"keyId\":\"someKeyId\",\"signature\":\"Z2VuZXJhdGVkTW9ja1NpZ25hdHVyZQ==\",\"signingAlgorithm\":\"RSASSA-PSS-SHA-384\",\"certificateChain\":[\"bW9ja0NlcnQx\",\"bW9ja0NlcnQy\"]}", }, "describe-key": { pluginPath: sigGenPluginPath, stdin: "{\"contractVersion\":\"1.0\",\"keyId\":\"someKeyId\"}", expectedStdout: "{\"keyId\":\"someKeyId\",\"keySpec\":\"RSA-3072\"}", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { validateSuccess(*test.pluginPath, name, test.stdin, test.expectedStdout, t) }) } } func TestFailure(t *testing.T) { cmds := []string{"generate-envelope", "verify-signature", "get-plugin-metadata"} stdInputs := []string{"", "\n", "invalidjson", "๐Ÿบ ยข ยง"} expectedValidationErr := "{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"Input is not a valid JSON\"}" for _, cmd := range cmds { for _, input := range stdInputs { t.Run(cmd+"_"+input, func(t *testing.T) { validateFailure(*envGenPluginPath, cmd, input, expectedValidationErr, t) }) } } // get-plugin-metadata input has all of its keys as optional so ommiting it cmds = cmds[:len(cmds)-1] input := "{\"sad\":\"bad\"}" expectedValidationErr = "{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"Input is not a valid JSON: contractVersion cannot be empty\"}" for _, cmd := range cmds { t.Run(cmd+"_{}"+input, func(t *testing.T) { validateFailure(*envGenPluginPath, cmd, input, expectedValidationErr, t) }) } cmds = []string{"generate-signature", "describe-key"} stdInputs = []string{"", "\n", "invalidjson", "๐Ÿบ ยข ยง"} expectedValidationErr = "{\"errorCode\":\"VALIDATION_ERROR\",\"errorMessage\":\"Input is not a valid JSON\"}" for _, cmd := range cmds { for _, input := range stdInputs { t.Run(cmd+"_"+input, func(t *testing.T) { validateFailure(*sigGenPluginPath, cmd, input, expectedValidationErr, t) }) } } } func execute(exe, arg, stdInput string, t *testing.T) (string, string, error) { cmd := exec.Command(exe, arg) if stdInput != "" { stdin, err := cmd.StdinPipe() if err != nil { t.Errorf("something went wrong when trying to invoke example plugin by cli: %+v", err) } go func() { defer stdin.Close() io.WriteString(stdin, stdInput) }() } var stdOut strings.Builder var stdErr strings.Builder cmd.Stdout = &stdOut cmd.Stderr = &stdErr err := cmd.Run() return stdOut.String(), stdErr.String(), err } func validateSuccess(exe, arg, stdInput, expectedStdOut string, t *testing.T) { stdOut, stdErr, err := execute(exe, arg, stdInput, t) if err != nil { t.Logf("standard output: %s", stdOut) t.Logf("standard Err: %s", stdErr) t.Fatalf("'%s' command failed with error: %+v", arg, err) } if stdOut == "" { t.Errorf("For '%s' command's standard out must not be empty", arg) } if stdErr != "" { t.Errorf("For '%s' command's standard error must be empty", arg) } res, err := jsonEquals(stdOut, expectedStdOut) if err == nil { if !res { t.Errorf("For '%s' command, expected standard out to be '%s' but found '%s'", arg, expectedStdOut, stdOut) } } else { if expectedStdOut != stdOut { t.Errorf("For '%s' command, expected standard standard to be '%s' but found '%s'", arg, expectedStdOut, stdOut) } } } func validateFailure(exe, arg, stdInput, expectedStdError string, t *testing.T) { stdOut, stdErr, err := execute(exe, arg, stdInput, t) if err == nil { t.Fatalf("expected '%s' command fail with error but it didnt", arg) t.Logf("standard output: %s", stdOut) t.Logf("standard Err: %s", stdErr) } if stdErr == "" { t.Errorf("For '%s' command's standard error must not be empty", arg) } if stdOut != "" { t.Errorf("For '%s' command's standard out must be empty", arg) } if stdErr != expectedStdError { t.Errorf("For '%s' command, expected standard error to be '%s' but found '%s'", arg, expectedStdError, stdErr) } } // JSONEqual compares the JSON from two Readers. func jsonEquals(x, y string) (bool, error) { var x1, y1 interface{} if err := json.Unmarshal([]byte(x), &x1); err != nil { return false, err } if err := json.Unmarshal([]byte(y), &y1); err != nil { return false, err } return reflect.DeepEqual(x1, y1), nil } notation-plugin-framework-go-1.0.0/test/e2e/go.mod000066400000000000000000000001171455633326000220030ustar00rootroot00000000000000module github.com/notaryproject/notation-plugin-framework-go/test/e2e go 1.20 notation-plugin-framework-go-1.0.0/test/e2e/run.sh000077500000000000000000000023501455633326000220410ustar00rootroot00000000000000#!/bin/bash -e # Copyright The Notary Project Authors. # 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. # build example plugins for e2e tests echo "building example plugins..." echo "==============================" CWD=$(pwd) PLUGIN_NAME=com.example.plugin plugin_directories=( envelopegenerator signaturegenerator ) for plugin_directory in "${plugin_directories[@]}" do (cd "../../example/${plugin_directory}" && go build -o "$CWD/bin/${plugin_directory}/$PLUGIN_NAME" . && echo "e2e ${plugin_directory} plugin built") done # run e2e tests echo "running e2e tests..." echo "==============================" go test -race -v ./... -args -env_gen_plugin="./bin/${plugin_directories[0]}/$PLUGIN_NAME" -sig_gen_plugin="./bin/${plugin_directories[1]}/$PLUGIN_NAME"