Read delivery tokens from files and check selected paths before release

This commit is contained in:
2026-11-01 15:39:36 +00:00
parent 6650b639e1
commit 792bfc4eca
17 changed files with 361 additions and 23 deletions

View File

@@ -27,6 +27,8 @@ const (
MarkdownLinks Check = "markdown-links"
// SharedFiles identifies a requested shared file that is unavailable.
SharedFiles Check = "shared-files"
// UntrackedFiles identifies selected paths present in the committed source.
UntrackedFiles Check = "untracked-files"
)
// Finding describes a validation observation at an optional source location.

View File

@@ -0,0 +1,24 @@
package validation
import (
"path"
"strings"
)
func checkUntrackedFiles(tree *CandidateTree, names []string) error {
var findings []Finding
for _, name := range names {
clean := path.Clean(name)
if name == "" || path.IsAbs(name) || clean == "." || clean == ".." || strings.HasPrefix(clean, "../") {
findings = append(findings, Finding{Path: name, Message: "expected a repository-relative file path"})
continue
}
if _, exists := tree.Entry(clean); exists {
findings = append(findings, Finding{Path: clean, Message: "file must be absent from the committed candidate"})
}
}
if len(findings) == 0 {
return nil
}
return &Failure{Check: UntrackedFiles, Findings: findings}
}

View File

@@ -0,0 +1,49 @@
package validation
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
)
func TestUntrackedFilesUsesCapturedRepositoryPaths(t *testing.T) {
repository := newTreeRepository(t)
writeTreeFile(t, repository, "go.mod", []byte("module example.com/root\n"))
writeTreeFile(t, repository, "nested/go.mod", []byte("module example.com/nested\n"))
writeTreeFile(t, repository, "nested/token", []byte("fixture-secret\n"))
writeTreeFile(t, repository, ".gitignore", []byte(".releasekit/\n"))
gitTree(t, repository, "add", ".")
gitTree(t, repository, "commit", "-m", "capture a committed credential path")
commit := strings.TrimSpace(gitTree(t, repository, "rev-parse", "HEAD"))
writeTreeFile(t, repository, ".releasekit/token", []byte("local-secret\n"))
if err := os.Remove(filepath.Join(repository, "nested/token")); err != nil {
t.Fatal(err)
}
installGoShim(t, "", false)
request := Request{RepositoryDir: repository, Commit: commit}
if err := Run(context.Background(), request); err != nil {
t.Fatal(err)
}
request.Settings.UntrackedFiles = []string{".releasekit/token", "absent-token"}
if err := Run(context.Background(), request); err != nil {
t.Fatal(err)
}
request.Settings.UntrackedFiles = []string{"nested/./token"}
err := Run(context.Background(), request)
failure, ok := err.(*Failure)
if !ok || failure.Check != UntrackedFiles || len(failure.Findings) != 1 || failure.Findings[0].Path != "nested/token" {
t.Fatalf("untracked-files error = %v, want committed nested path despite worktree deletion", err)
}
if strings.Contains(err.Error(), "fixture-secret") {
t.Fatal("validation exposed committed token bytes")
}
for _, invalid := range []string{"", "../token", "/token"} {
request.Settings.UntrackedFiles = []string{invalid}
if err := Run(context.Background(), request); err == nil {
t.Fatalf("invalid path %q was accepted", invalid)
}
}
}

View File

@@ -21,6 +21,7 @@ type Settings struct {
FullVet bool
CompileTargets []Target
SharedFiles []string
UntrackedFiles []string
GoFormat bool
Markdown *MarkdownSettings
ModuleDistribution ModuleDistributionSettings
@@ -134,7 +135,7 @@ func Run(ctx context.Context, request Request) (result error) {
}
}
needsSharedSelection := len(request.Settings.SharedFiles) != 0
if request.Settings.GoFormat || request.Settings.ModuleDistribution.WorkspaceFiles || request.Settings.ModuleDistribution.LocalReplacements || request.Settings.FileSize != nil || request.Settings.Markdown != nil || needsSharedSelection {
if request.Settings.GoFormat || request.Settings.ModuleDistribution.WorkspaceFiles || request.Settings.ModuleDistribution.LocalReplacements || request.Settings.FileSize != nil || request.Settings.Markdown != nil || needsSharedSelection || len(request.Settings.UntrackedFiles) != 0 {
tree, err := request.ReadCandidateTree(ctx)
if err != nil {
var failure *Failure
@@ -154,6 +155,9 @@ func Run(ctx context.Context, request Request) (result error) {
if request.Settings.GoFormat {
check = GoFormat
}
if len(request.Settings.UntrackedFiles) != 0 {
check = UntrackedFiles
}
return &Failure{Check: check, Cause: err}
}
lastContentCheck := Check("")
@@ -166,6 +170,9 @@ func Run(ctx context.Context, request Request) (result error) {
}
}
}()
if err := checkUntrackedFiles(tree, request.Settings.UntrackedFiles); err != nil {
return err
}
if request.Settings.GoFormat {
readsBefore := tree.blobReads
if err := checkGoFormat(ctx, tree); err != nil {