Skip to content
Merged
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
10 changes: 5 additions & 5 deletions internal/adapter/repogen/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ func validateGenerateRequest(ctx context.Context, generator *Generator, request
if err := validateOwnerOnlyFile(generator.apkSigningKey); err != nil {
return err
}
keyName := filepath.Base(generator.apkSigningKey)
if keyName == "." || keyName == string(filepath.Separator) || strings.ContainsAny(keyName, ":,\n\r") {
return fmt.Errorf("APK signing key basename %q is unsafe", keyName)
keyName := request.APKSigningKeyName
if keyName == "" || keyName == "." || filepath.Base(keyName) != keyName ||
strings.ContainsAny(keyName, "/\\:,\n\r") {
return fmt.Errorf("APK signing key name %q is unsafe", keyName)
}

return nil
Expand Down Expand Up @@ -246,8 +247,7 @@ chown -R "$owner" /repo/rpm
// generateAPK runs apk index and abuild-sign with a fixed source epoch.
func (g *Generator) generateAPK(ctx context.Context, request pkgrepo.GenerateRequest) error {
epoch := strconv.FormatInt(request.ReleaseTime.Unix(), 10)
keyName := filepath.Base(g.apkSigningKey)
containerKey := path.Join(containerKeys, keyName)
containerKey := path.Join(containerKeys, request.APKSigningKeyName)
script := `set -eu
owner="$(stat -c '%u:%g' /repo)"
export SOURCE_DATE_EPOCH=` + epoch + `
Expand Down
37 changes: 28 additions & 9 deletions internal/adapter/repogen/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@ func TestGenerateRejectsExposedAPKPrivateKey(t *testing.T) {
assert.Contains(t, err.Error(), "allow group or other access")
}

// TestGenerateRejectsUnsafeAPKSigningKeyName keeps the key mount inside its fixed directory.
func TestGenerateRejectsUnsafeAPKSigningKeyName(t *testing.T) {
skipWindows(t)
t.Parallel()

fixture := newGeneratorFixture(t)
request := fixture.request()
request.APKSigningKeyName = "../outside.rsa"

err := fixture.generator.Generate(context.Background(), request)
require.Error(t, err)
assert.Contains(t, err.Error(), "APK signing key name")
}

// generatorFixture owns one fake three-format generation run.
type generatorFixture struct {
// generator is the configured adapter under test.
Expand All @@ -139,27 +153,32 @@ func newGeneratorFixture(t *testing.T) *generatorFixture {
writeAPTIndexes(t, root)
writeFile(t, filepath.Join(root, "apt", "dists", "stable", "Release"), []byte(buildRelease(t, root, true)))
fake := filepath.Join(directory, "docker")
key := filepath.Join(directory, "apk-index-001.rsa")
key := filepath.Join(directory, "materialized-private-key.pem")
record := filepath.Join(directory, "args")
writeFile(t, fake, []byte(fakeGeneratorScript))
require.NoError(t, os.Chmod(fake, 0o755))
writeFile(t, key, []byte("private key sentinel"))
environ := append(os.Environ(), "REPOGEN_RECORD="+record)
return &generatorFixture{
generator: New(Options{DockerPath: fake, APKSigningKey: key, Environ: environ}),
root: root,
key: key,
record: record,
generator: New(Options{
DockerPath: fake,
APKSigningKey: key,
Environ: environ,
}),
root: root,
key: key,
record: record,
}
}

// request returns the deterministic generation request used by the accepted spike.
func (f *generatorFixture) request() pkgrepo.GenerateRequest {
return pkgrepo.GenerateRequest{
Root: f.root,
Channel: pkgrepo.ChannelStable,
ReleaseTime: time.Date(2026, time.August, 21, 12, 0, 0, 0, time.UTC),
ValidUntil: time.Date(2027, time.August, 21, 12, 0, 0, 0, time.UTC),
Root: f.root,
Channel: pkgrepo.ChannelStable,
APKSigningKeyName: "apk-index-001.rsa",
ReleaseTime: time.Date(2026, time.August, 21, 12, 0, 0, 0, time.UTC),
ValidUntil: time.Date(2027, time.August, 21, 12, 0, 0, 0, time.UTC),
}
}

Expand Down
9 changes: 5 additions & 4 deletions internal/stage/pkgrepo/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,11 @@ func Build(
}

request := GenerateRequest{
Root: input.Output.Name(),
Channel: input.Config.Channel,
ReleaseTime: input.ReleaseTime,
ValidUntil: input.ValidUntil,
Root: input.Output.Name(),
Channel: input.Config.Channel,
APKSigningKeyName: strings.TrimSuffix(path.Base(input.Config.APKKey.Published), ".pub"),
ReleaseTime: input.ReleaseTime,
ValidUntil: input.ValidUntil,
}
if generateErr := generator.Generate(ctx, request); generateErr != nil {
return BuildResult{}, fmt.Errorf("generate repository metadata: %w", generateErr)
Expand Down
1 change: 1 addition & 0 deletions internal/stage/pkgrepo/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func TestBuildProducesOrderedSignedRepository(t *testing.T) {
generator.EXPECT().Generate(mock.Anything, mock.MatchedBy(func(request pkgrepo.GenerateRequest) bool {
return request.Root == fixture.outputDir &&
request.Channel == pkgrepo.ChannelStable &&
request.APKSigningKeyName == "apk-index-001.rsa" &&
request.ReleaseTime.Equal(fixture.releaseTime) &&
request.ValidUntil.Equal(fixture.validUntil)
})).Run(func(_ context.Context, request pkgrepo.GenerateRequest) {
Expand Down
2 changes: 2 additions & 0 deletions internal/stage/pkgrepo/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ type GenerateRequest struct {
Root string
// Channel is the configured repository channel.
Channel Channel
// APKSigningKeyName is the private-key basename matching the published public key.
APKSigningKeyName string
// ReleaseTime is the deterministic metadata creation time.
ReleaseTime time.Time
// ValidUntil is the APT metadata expiry time and must follow ReleaseTime.
Expand Down