Skip to content

Commit 3bf13c6

Browse files
leodidoona-agent
authored andcommitted
fix(yarn): add --frozen-lockfile to default install command
Ensures reproducible builds by preventing yarn from updating yarn.lock during install. The build will fail if package.json and yarn.lock are out of sync, rather than silently resolving new versions. Security: Prevents dependency confusion attacks where a malicious package version could be pulled if package.json allows version ranges. Co-authored-by: Ona <[email protected]>
1 parent be3e577 commit 3bf13c6

File tree

1 file changed

+20
-20
lines changed

1 file changed

+20
-20
lines changed

pkg/leeway/build.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -820,14 +820,14 @@ func printBuildSummary(ctx *buildContext, targetPkg *Package, allpkg []*Package,
820820
inNewlyBuilt := newlyBuiltMap[p.FullName()]
821821
inPkgsToDownload := pkgsToDownloadMap[p.FullName()]
822822
status := statusAfterDownload[p]
823-
823+
824824
log.WithFields(log.Fields{
825825
"package": p.FullName(),
826826
"inNewlyBuilt": inNewlyBuilt,
827827
"inPkgsToDownload": inPkgsToDownload,
828828
"status": status,
829829
}).Debug("Categorizing package for build summary")
830-
830+
831831
if inNewlyBuilt {
832832
// Package was built during this build
833833
builtLocally++
@@ -1532,7 +1532,7 @@ func (p *Package) buildYarn(buildctx *buildContext, wd, result string) (bld *pac
15321532
}
15331533
yarnCache := filepath.Join(buildctx.BuildDir(), fmt.Sprintf("yarn-cache-%s", buildctx.buildID))
15341534
if len(cfg.Commands.Install) == 0 {
1535-
commands[PackageBuildPhasePull] = append(commands[PackageBuildPhasePull], []string{"yarn", "install", "--mutex", yarnMutex, "--cache-folder", yarnCache})
1535+
commands[PackageBuildPhasePull] = append(commands[PackageBuildPhasePull], []string{"yarn", "install", "--frozen-lockfile", "--mutex", yarnMutex, "--cache-folder", yarnCache})
15361536
} else {
15371537
commands[PackageBuildPhasePull] = append(commands[PackageBuildPhasePull], cfg.Commands.Install)
15381538
}
@@ -2032,7 +2032,7 @@ func (p *Package) buildDocker(buildctx *buildContext, wd, result string) (res *p
20322032
// Normal build (load to daemon for pushing)
20332033
buildcmd = []string{"docker", "build", "--pull", "-t", version}
20342034
}
2035-
2035+
20362036
for arg, val := range cfg.BuildArgs {
20372037
buildcmd = append(buildcmd, "--build-arg", fmt.Sprintf("%s=%s", arg, val))
20382038
}
@@ -2286,12 +2286,12 @@ func (p *Package) buildDocker(buildctx *buildContext, wd, result string) (res *p
22862286
if err != nil {
22872287
return fmt.Errorf("failed to get deterministic mtime: %w", err)
22882288
}
2289-
2289+
22902290
// Create metadata
22912291
if err := createDockerExportMetadata(buildDir, version, cfg, mtime); err != nil {
22922292
return err
22932293
}
2294-
2294+
22952295
// Set up subjects function with buildDir for OCI layout extraction
22962296
res.Subjects = createOCILayoutSubjectsFunction(version, cfg, buildDir)
22972297
return nil
@@ -2559,19 +2559,19 @@ func (p *Package) getDeterministicMtime() (int64, error) {
25592559
}
25602560
return timestamp, nil
25612561
}
2562-
2562+
25632563
// Check if we're in a test environment
25642564
if isTestEnvironment() {
25652565
// Test fixtures don't have git - use epoch for determinism
25662566
return 0, nil
25672567
}
2568-
2568+
25692569
// Production build without git is an error - prevents cache pollution
2570-
return 0, fmt.Errorf("no git commit available for deterministic mtime. "+
2571-
"Ensure repository is properly cloned with git history, or set SOURCE_DATE_EPOCH environment variable. "+
2570+
return 0, fmt.Errorf("no git commit available for deterministic mtime. " +
2571+
"Ensure repository is properly cloned with git history, or set SOURCE_DATE_EPOCH environment variable. " +
25722572
"Building from source tarballs without git metadata will cause cache inconsistencies")
25732573
}
2574-
2574+
25752575
timestamp, err := GetCommitTimestamp(context.Background(), p.C.Git())
25762576
if err != nil {
25772577
return 0, fmt.Errorf("failed to get deterministic timestamp for tar mtime: %w. "+
@@ -2588,12 +2588,12 @@ func isTestEnvironment() bool {
25882588
if strings.HasSuffix(os.Args[0], ".test") {
25892589
return true
25902590
}
2591-
2591+
25922592
// Check for explicit test mode environment variable
25932593
if os.Getenv("LEEWAY_TEST_MODE") == "true" {
25942594
return true
25952595
}
2596-
2596+
25972597
return false
25982598
}
25992599

@@ -2740,18 +2740,18 @@ func executeCommandsForPackage(buildctx *buildContext, p *Package, wd string, co
27402740

27412741
env := append(os.Environ(), p.Environment...)
27422742
env = append(env, fmt.Sprintf("%s=%s", EnvvarWorkspaceRoot, p.C.W.Origin))
2743-
2743+
27442744
// Export SOURCE_DATE_EPOCH for reproducible builds
27452745
// BuildKit (Docker >= v23.0) automatically uses this for deterministic image timestamps
27462746
mtime, err := p.getDeterministicMtime()
27472747
if err == nil {
27482748
env = append(env, fmt.Sprintf("SOURCE_DATE_EPOCH=%d", mtime))
27492749
}
2750-
2750+
27512751
// Enable BuildKit to ensure SOURCE_DATE_EPOCH is used for Docker builds
27522752
// BuildKit is default since Docker v23.0, but we set it explicitly for older versions
27532753
env = append(env, "DOCKER_BUILDKIT=1")
2754-
2754+
27552755
for _, cmd := range commands {
27562756
if len(cmd) == 0 {
27572757
continue // Skip empty commands
@@ -2858,7 +2858,7 @@ func checkImageExists(imageName string) (bool, error) {
28582858
// checkOCILayoutExists checks if an OCI layout image.tar exists and is valid
28592859
func checkOCILayoutExists(buildDir string) (bool, error) {
28602860
imageTarPath := filepath.Join(buildDir, "image.tar")
2861-
2861+
28622862
// Check if image.tar exists
28632863
info, err := os.Stat(imageTarPath)
28642864
if err != nil {
@@ -2867,16 +2867,16 @@ func checkOCILayoutExists(buildDir string) (bool, error) {
28672867
}
28682868
return false, xerrors.Errorf("failed to stat image.tar: %w", err)
28692869
}
2870-
2870+
28712871
// Check if it's a regular file and not empty
28722872
if !info.Mode().IsRegular() {
28732873
return false, xerrors.Errorf("image.tar is not a regular file")
28742874
}
2875-
2875+
28762876
if info.Size() == 0 {
28772877
return false, xerrors.Errorf("image.tar is empty")
28782878
}
2879-
2879+
28802880
return true, nil
28812881
}
28822882

0 commit comments

Comments
 (0)