-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit introduces a new lockfiles attestor to capture and attest the contents of common lockfiles in the project. The changes include: - Add new file attestation/lockfiles/lockfiles.go implementing the lockfiles attestor - Update imports.go to include the new lockfiles package The lockfiles attestor captures contents of various lockfiles such as Gemfile.lock, package-lock.json, yarn.lock, and others. It stores the information in a slice of LockfileInfo structs, allowing for flexible handling of multiple lockfiles. This feature enhances the project's capability to track and verify dependency information as part of the attestation process." Signed-off-by: Frederick F. Kautz IV <frederick@testifysec.com>
- Loading branch information
Showing
3 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Copyright 2024 The Witness Contributors | ||
// | ||
// 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 lockfiles | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/invopop/jsonschema" | ||
|
||
"github.com/in-toto/go-witness/attestation" | ||
) | ||
|
||
const ( | ||
Name = "lockfiles" | ||
Type = "https://witness.dev/attestations/lockfiles/v0.1" | ||
RunType = attestation.PreMaterialRunType | ||
) | ||
|
||
func init() { | ||
attestation.RegisterAttestation(Name, Type, RunType, func() attestation.Attestor { | ||
return NewLockfilesAttestor() | ||
}) | ||
} | ||
|
||
func NewLockfilesAttestor() attestation.Attestor { | ||
return &Attestor{ | ||
Lockfiles: []LockfileInfo{}, | ||
} | ||
} | ||
|
||
// Attestor implements the lockfiles attestation type | ||
type Attestor struct { | ||
Lockfiles []LockfileInfo `json:"lockfiles"` | ||
} | ||
|
||
// LockfileInfo stores information about a lockfile | ||
type LockfileInfo struct { | ||
Filename string `json:"filename"` | ||
Content string `json:"content"` | ||
} | ||
|
||
// Name returns the name of the attestation type | ||
func (a *Attestor) Name() string { | ||
return "lockfiles" | ||
} | ||
|
||
// Attest captures the contents of common lockfiles | ||
func (a *Attestor) Attest(ctx *attestation.AttestationContext) error { | ||
lockfilePatterns := []string{ | ||
"Gemfile.lock", // Ruby | ||
"package-lock.json", // Node.js (npm) | ||
"yarn.lock", // Node.js (Yarn) | ||
"Cargo.lock", // Rust | ||
"poetry.lock", // Python (Poetry) | ||
"Pipfile.lock", // Python (Pipenv) | ||
"composer.lock", // PHP | ||
"go.sum", // Go | ||
"Podfile.lock", // iOS/macOS (CocoaPods) | ||
"gradle.lockfile", // Gradle | ||
"pnpm-lock.yaml", // Node.js (pnpm) | ||
} | ||
|
||
a.Lockfiles = []LockfileInfo{} | ||
|
||
for _, pattern := range lockfilePatterns { | ||
matches, err := filepath.Glob(pattern) | ||
if err != nil { | ||
return fmt.Errorf("error searching for %s: %w", pattern, err) | ||
} | ||
|
||
for _, match := range matches { | ||
content, err := os.ReadFile(match) | ||
if err != nil { | ||
return fmt.Errorf("error reading %s: %w", match, err) | ||
} | ||
a.Lockfiles = append(a.Lockfiles, LockfileInfo{ | ||
Filename: filepath.Base(match), | ||
Content: string(content), | ||
}) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// RunType implements attestation.Attestor. | ||
func (o *Attestor) RunType() attestation.RunType { | ||
return RunType | ||
} | ||
|
||
// // Schema implements attestation.Attestor. | ||
func (o *Attestor) Schema() *jsonschema.Schema { | ||
return jsonschema.Reflect(&o) | ||
} | ||
|
||
// Type implements attestation.Attestor. | ||
func (o *Attestor) Type() string { | ||
return Type | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright 2024 The Witness Contributors | ||
// | ||
// 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 lockfiles | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/in-toto/go-witness/attestation" | ||
) | ||
|
||
func TestAttestor_Attest(t *testing.T) { | ||
// Create a temporary directory for test files | ||
tempDir, err := os.MkdirTemp("", "lockfiles_test") | ||
if err != nil { | ||
t.Fatalf("Failed to create temp dir: %v", err) | ||
} | ||
defer os.RemoveAll(tempDir) | ||
|
||
// Create test lockfiles | ||
testFiles := map[string]string{ | ||
"Gemfile.lock": "test content for Gemfile.lock", | ||
"package-lock.json": "test content for package-lock.json", | ||
} | ||
|
||
for filename, content := range testFiles { | ||
err := os.WriteFile(filepath.Join(tempDir, filename), []byte(content), 0644) | ||
if err != nil { | ||
t.Fatalf("Failed to create test file %s: %v", filename, err) | ||
} | ||
} | ||
|
||
// Change to the temp directory | ||
oldWd, err := os.Getwd() | ||
if err != nil { | ||
t.Fatalf("Failed to get current working directory: %v", err) | ||
} | ||
defer os.Chdir(oldWd) | ||
|
||
err = os.Chdir(tempDir) | ||
if err != nil { | ||
t.Fatalf("Failed to change to temp directory: %v", err) | ||
} | ||
|
||
// Create an Attestor and AttestationContext | ||
attestor := &Attestor{} | ||
ctx := &attestation.AttestationContext{} | ||
|
||
// Run the Attest method | ||
err = attestor.Attest(ctx) | ||
if err != nil { | ||
t.Fatalf("Attest failed: %v", err) | ||
} | ||
|
||
// Check if the lockfiles were captured correctly | ||
if len(attestor.Lockfiles) != len(testFiles) { | ||
t.Errorf("Expected %d lockfiles, but got %d", len(testFiles), len(attestor.Lockfiles)) | ||
} | ||
|
||
for _, lockfile := range attestor.Lockfiles { | ||
expectedContent, ok := testFiles[lockfile.Filename] | ||
if !ok { | ||
t.Errorf("Unexpected lockfile %s found in attestation", lockfile.Filename) | ||
} else if lockfile.Content != expectedContent { | ||
t.Errorf("Lockfile %s content mismatch. Got %s, want %s", lockfile.Filename, lockfile.Content, expectedContent) | ||
} | ||
delete(testFiles, lockfile.Filename) | ||
} | ||
|
||
if len(testFiles) > 0 { | ||
for filename := range testFiles { | ||
t.Errorf("Expected lockfile %s not found in attestation", filename) | ||
} | ||
} | ||
} | ||
|
||
func TestAttestor_Name(t *testing.T) { | ||
attestor := &Attestor{} | ||
if name := attestor.Name(); name != "lockfiles" { | ||
t.Errorf("Incorrect attestor name. Got %s, want lockfiles", name) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters