Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions plugins/scorecard/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package scorecard

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/schema"
)

func New() schema.Plugin {
return schema.Plugin{
Name: "scorecard",
Platform: schema.PlatformInfo{
Name: "OpenSSF Scorecard",
Homepage: sdk.URL("https://github.com/ossf/scorecard"),
},
Credentials: []schema.CredentialType{
SecretKey(),
},
Executables: []schema.Executable{
OpenSSFScorecardCLI(),
},
}
}
25 changes: 25 additions & 0 deletions plugins/scorecard/scorecard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package scorecard

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/needsauth"
"github.com/1Password/shell-plugins/sdk/schema"
"github.com/1Password/shell-plugins/sdk/schema/credname"
)

func OpenSSFScorecardCLI() schema.Executable {
return schema.Executable{
Name: "OpenSSF Scorecard CLI",
Runs: []string{"scorecard"},
DocsURL: sdk.URL("https://github.com/ossf/scorecard"),
NeedsAuth: needsauth.IfAll(
needsauth.NotForHelpOrVersion(),
needsauth.NotWithoutArgs(),
),
Uses: []schema.CredentialUsage{
{
Name: credname.SecretKey,
},
},
}
}
83 changes: 83 additions & 0 deletions plugins/scorecard/secret_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package scorecard

import (
"context"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/importer"
"github.com/1Password/shell-plugins/sdk/schema"
"github.com/1Password/shell-plugins/sdk/schema/credname"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func SecretKey() schema.CredentialType {
return schema.CredentialType{
Name: credname.SecretKey,
DocsURL: sdk.URL("https://github.com/ossf/scorecard?tab=readme-ov-file#authentication"),
ManagementURL: sdk.URL("https://github.com/settings/installations"),
Fields: []schema.CredentialField{
{
Name: fieldname.Key,
MarkdownDescription: "RSA private key used to authenticate GitHub App for OpenSSF Scorecard. This should be a PEM-formatted private key.",
Secret: true,
},
{
Name: "App ID",
MarkdownDescription: "GitHub App ID for authentication. This is a numeric ID.",
Secret: false,
Composition: &schema.ValueComposition{
Charset: schema.Charset{
Digits: true,
},
},
},
{
Name: "Installation ID",
MarkdownDescription: "GitHub App Installation ID for the target repository or organization. This is a numeric ID and is required.",
Secret: false,
Composition: &schema.ValueComposition{
Charset: schema.Charset{
Digits: true,
},
},
},
},
DefaultProvisioner: scorecardProvisioner{},
Importer: importer.TryAll(
importer.TryEnvVarPair(defaultEnvVarMapping),
),
}
}

var defaultEnvVarMapping = map[string]sdk.FieldName{
"GITHUB_APP_KEY_PATH": fieldname.Key,
"GITHUB_APP_ID": "App ID",
"GITHUB_APP_INSTALLATION_ID": "Installation ID",
}

type scorecardProvisioner struct{}

func (p scorecardProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) {
// Provision the private key as a file
if key, ok := in.ItemFields[fieldname.Key]; ok {
keyPath := in.FromTempDir("github-app-key.pem")
out.AddSecretFile(keyPath, []byte(key))
out.AddEnvVar("GITHUB_APP_KEY_PATH", keyPath)
}

// Provision App ID and Installation ID as environment variables
if appID, ok := in.ItemFields["App ID"]; ok && appID != "" {
out.AddEnvVar("GITHUB_APP_ID", appID)
}
if installationID, ok := in.ItemFields["Installation ID"]; ok && installationID != "" {
out.AddEnvVar("GITHUB_APP_INSTALLATION_ID", installationID)
}
}

func (p scorecardProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) {
// Files get deleted automatically by 1Password CLI and environment variables get wiped when process exits
}

func (p scorecardProvisioner) Description() string {
return "Provision GitHub App private key as file and IDs as environment variables for OpenSSF Scorecard"
}
92 changes: 92 additions & 0 deletions plugins/scorecard/secret_key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package scorecard

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"testing"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/plugintest"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func generateTestPrivateKey(t *testing.T) string {
// Generate a test RSA private key
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("Failed to generate test private key: %v", err)
}

// Encode to PEM format
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
privateKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
})

return string(privateKeyPEM)
}

func TestSecretKeyProvisioner(t *testing.T) {
testPrivateKey := generateTestPrivateKey(t)

plugintest.TestProvisioner(t, SecretKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{
"default": {
ItemFields: map[sdk.FieldName]string{
fieldname.Key: testPrivateKey,
"App ID": "123456",
"Installation ID": "7890123",
},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"GITHUB_APP_ID": "123456",
"GITHUB_APP_INSTALLATION_ID": "7890123",
"GITHUB_APP_KEY_PATH": "/tmp/github-app-key.pem",
},
Files: map[string]sdk.OutputFile{
"/tmp/github-app-key.pem": {Contents: []byte(testPrivateKey)},
},
},
},
"partial fields": {
ItemFields: map[sdk.FieldName]string{
fieldname.Key: testPrivateKey,
"App ID": "123456",
// Installation ID is missing to test handling of partial fields
},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"GITHUB_APP_ID": "123456",
"GITHUB_APP_KEY_PATH": "/tmp/github-app-key.pem",
// GITHUB_APP_INSTALLATION_ID should not be set when field is missing
},
Files: map[string]sdk.OutputFile{
"/tmp/github-app-key.pem": {Contents: []byte(testPrivateKey)},
},
},
},
})
}

func TestSecretKeyImporter(t *testing.T) {
plugintest.TestImporter(t, SecretKey().Importer, map[string]plugintest.ImportCase{
"environment": {
Environment: map[string]string{
"GITHUB_APP_KEY_PATH": "/path/to/key.pem",
"GITHUB_APP_ID": "123456",
"GITHUB_APP_INSTALLATION_ID": "7890123",
},
ExpectedCandidates: []sdk.ImportCandidate{
{
Fields: map[sdk.FieldName]string{
fieldname.Key: "/path/to/key.pem",
"App ID": "123456",
"Installation ID": "7890123",
},
},
},
},
})
}