Skip to content

Commit

Permalink
fix(templates): protect against potential template injection
Browse files Browse the repository at this point in the history
* fix(templates): check for unwanted templates

* refactor(config): more readable

* chore: update changelog

* refactor(config): use named fields in struct
  • Loading branch information
stefanlogue authored Aug 28, 2024
1 parent 7faa713 commit 837333e
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 23 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
### Fixed
- template validation to protect against template injection

## [v0.23.0](https://github.com/stefanlogue/meteor/releases/tag/v0.23.0) - 2024-08-28
### Added
Expand Down
48 changes: 37 additions & 11 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,28 @@ const (
defaultMessageWithTicketTemplate = "{{.TicketNumber}}{{if .Scope}}({{.Scope}}){{end}}{{if .IsBreakingChange}}!{{end}}: <{{.Type}}> {{.Message}}"
)

type LoadConfigReturn struct {
MessageTemplate string
MessageWithTicketTemplate string
Prefixes []huh.Option[string]
Coauthors []huh.Option[string]
Boards []huh.Option[string]
CommitTitleCharLimit int
ShowIntro bool
}

// loadConfig loads the config file from the current directory or any parent
func loadConfig(fs afero.Fs) ([]huh.Option[string], []huh.Option[string], []huh.Option[string], bool, int, string, string, error) {
func loadConfig(fs afero.Fs) (LoadConfigReturn, error) {
filePath, err := config.FindConfigFile(fs)
if err != nil {
log.Debug("Error finding config file", "error", err)
return config.DefaultPrefixes, nil, nil, true, defaultCommitTitleCharLimit, defaultMessageTemplate, defaultMessageWithTicketTemplate, nil
return LoadConfigReturn{
MessageTemplate: defaultMessageTemplate,
MessageWithTicketTemplate: defaultMessageWithTicketTemplate,
Prefixes: config.DefaultPrefixes,
CommitTitleCharLimit: defaultCommitTitleCharLimit,
ShowIntro: true,
}, nil
}

log.Debug("found config file", "path", filePath)
Expand All @@ -29,7 +45,12 @@ func loadConfig(fs afero.Fs) ([]huh.Option[string], []huh.Option[string], []huh.

err = c.LoadFile(filePath)
if err != nil {
return nil, nil, nil, true, defaultCommitTitleCharLimit, defaultMessageTemplate, defaultMessageWithTicketTemplate, fmt.Errorf("error parsing config file: %w", err)
return LoadConfigReturn{
MessageTemplate: defaultMessageTemplate,
MessageWithTicketTemplate: defaultMessageWithTicketTemplate,
CommitTitleCharLimit: defaultCommitTitleCharLimit,
ShowIntro: true,
}, fmt.Errorf("error parsing config file: %w", err)
}

if c.ShowIntro == nil {
Expand All @@ -42,10 +63,8 @@ func loadConfig(fs afero.Fs) ([]huh.Option[string], []huh.Option[string], []huh.
c.CommitTitleCharLimit = &commitTitleCharLimit
}

var messageTemplate, messageWithTicketTemplate string
if c.MessageTemplate == nil {
messageTemplate = defaultMessageTemplate
} else {
messageTemplate := defaultMessageTemplate
if c.MessageTemplate != nil {
messageTemplate, err = config.ConvertTemplate(*c.MessageTemplate)
if err != nil {
log.Error("Error converting message template", "error", err)
Expand All @@ -54,9 +73,8 @@ func loadConfig(fs afero.Fs) ([]huh.Option[string], []huh.Option[string], []huh.
}
c.MessageTemplate = &messageTemplate

if c.MessageWithTicketTemplate == nil {
messageWithTicketTemplate = defaultMessageWithTicketTemplate
} else {
messageWithTicketTemplate := defaultMessageWithTicketTemplate
if c.MessageWithTicketTemplate != nil {
messageWithTicketTemplate, err = config.ConvertTemplate(*c.MessageWithTicketTemplate)
if err != nil {
log.Error("Error converting message with ticket template", "error", err)
Expand All @@ -65,5 +83,13 @@ func loadConfig(fs afero.Fs) ([]huh.Option[string], []huh.Option[string], []huh.
}
c.MessageWithTicketTemplate = &messageWithTicketTemplate

return c.Prefixes.Options(), c.Coauthors.Options(), c.Boards.Options(), *c.ShowIntro, *c.CommitTitleCharLimit, messageTemplate, messageWithTicketTemplate, nil
return LoadConfigReturn{
MessageTemplate: messageTemplate,
MessageWithTicketTemplate: messageWithTicketTemplate,
Prefixes: c.Prefixes.Options(),
Coauthors: c.Coauthors.Options(),
Boards: c.Boards.Options(),
CommitTitleCharLimit: *c.CommitTitleCharLimit,
ShowIntro: *c.ShowIntro,
}, nil
}
24 changes: 12 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ func main() {
fail("Could not change directory: %s", err)
}

prefixes, coauthors, boards, showIntro, commitTitleCharLimit, messageTemplate, messageWithTicketTemplate, err := loadConfig(AFS)
config, err := loadConfig(AFS)
if err != nil {
fail("Error: %s", err)
}

var newCommit Commit
theme := huh.ThemeCatppuccin()
if showIntro && (isFlagPassed("skip-intro") && !skipIntro) {
if config.ShowIntro && (isFlagPassed("skip-intro") && !skipIntro) {
introForm := huh.NewForm(
huh.NewGroup(
splashScreen(),
Expand All @@ -103,16 +103,16 @@ func main() {
fail("Error: %s", err)
}
}
if len(boards) > 0 {
if len(config.Boards) > 0 {
boardForm := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("Board").
Description("Select the board for this commit").
Options(boards...).
Options(config.Boards...).
Value(&newCommit.Board),
).WithHideFunc(func() bool {
return len(boards) < 1
return len(config.Boards) < 1
}),
).WithTheme(theme)

Expand All @@ -139,7 +139,7 @@ func main() {
CharLimit(10).
Value(&newCommit.TicketNumber),
).WithHideFunc(func() bool {
return len(boards) < 1
return len(config.Boards) < 1
}),
).WithTheme(theme)

Expand All @@ -154,7 +154,7 @@ func main() {
huh.NewSelect[string]().
Title("Type").
Description("Select the type of change that you're committing").
Options(prefixes...).
Options(config.Prefixes...).
Value(&newCommit.Type),
huh.NewConfirm().
Title("Breaking Change").
Expand All @@ -172,10 +172,10 @@ func main() {
huh.NewMultiSelect[string]().
Title("Coauthors").
Description("Select any coauthors for this commit").
Options(coauthors...).
Options(config.Coauthors...).
Value(&newCommit.Coauthors),
).WithHideFunc(func() bool {
return len(coauthors) < 1
return len(config.Coauthors) < 1
}),
).WithTheme(theme)

Expand All @@ -186,9 +186,9 @@ func main() {

var tmpl *template.Template
if len(newCommit.Board) > 0 && newCommit.Board != "NONE" {
tmpl = template.Must(template.New("message").Parse(messageWithTicketTemplate))
tmpl = template.Must(template.New("message").Parse(config.MessageWithTicketTemplate))
} else {
tmpl = template.Must(template.New("message").Parse(messageTemplate))
tmpl = template.Must(template.New("message").Parse(config.MessageTemplate))
}
buf := new(bytes.Buffer)
err = tmpl.Execute(buf, newCommit)
Expand All @@ -203,7 +203,7 @@ func main() {
huh.NewInput().
Value(&newCommit.Message).
Title("Message").
CharLimit(commitTitleCharLimit),
CharLimit(config.CommitTitleCharLimit),
huh.NewText().
Value(&newCommit.Body).
Title("Body").
Expand Down
3 changes: 3 additions & 0 deletions pkg/config/messageTemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
)

func ConvertTemplate(t string) (string, error) {
if strings.Contains(t, "{{") {
return "", fmt.Errorf("template must not contain {{}}")
}
if !strings.Contains(t, "@type") || !strings.Contains(t, "@message") {
return t, fmt.Errorf("template must contain @type and @message")
}
Expand Down

0 comments on commit 837333e

Please sign in to comment.