Skip to content
Open
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
25 changes: 18 additions & 7 deletions semgrep-scan/rules/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,16 +204,27 @@ rules:
- pattern-either:
- pattern: rds.NewInstance(...)
- pattern: rds.NewCluster(...)
# Suppress only when `StorageEncrypted` is explicitly set to a
# truthy value. Two forms are accepted:
# Suppress when `StorageEncrypted` is set in a way that is NOT a
# static-disable AND is secure-by-default. Three accepted shapes:
# * `StorageEncrypted: pulumi.Bool(true)` / `sdk.Bool(true)` /
# anything that ends `.Bool(true)` (the canonical Pulumi shape)
# * bare `StorageEncrypted: true` (less idiomatic but valid)
# Important: setting the field to `false` (or `pulumi.Bool(false)`,
# or any non-true value) does NOT suppress — we want to flag those
# explicitly-disabled cases, not let them through.
# any `.Bool(true)` — the canonical Pulumi truthy shape.
# * bare `StorageEncrypted: true`.
# * `StorageEncrypted: <ptr>.Bool(lo.FromPtrOr(<*bool>, true))`
# — runtime-config with secure default. The literal `true`
# fallback is required: `lo.FromPtr(*ptr)` (nil → false → DB
# unencrypted-by-default) and `lo.FromPtrOr(*ptr, false)`
# both still fire. Pair this shape with
# `IgnoreChanges([]string{"storageEncrypted"})` on the
# resource opts so the default flip doesn't propose a
# destructive replacement on existing unencrypted instances
# (the pairing isn't verified in regex; the wrapper shape +
# literal-true fallback are the design signal).
# Setting the field to `false` (literal), `Bool(false)`, or
# `lo.FromPtr(*bool)` (= `lo.FromPtrOr(*bool, false)`) still
# fires — those default to unencrypted.
- pattern-not-regex: 'StorageEncrypted:\s*\w+\.Bool\(true\)'
- pattern-not-regex: 'StorageEncrypted:\s*true\b'
- pattern-not-regex: 'StorageEncrypted:\s*\w+\.Bool\(lo\.FromPtrOr\([^()\n]+,\s*true\s*\)\)'
paths:
include:
- "**/*.go"
Expand Down
76 changes: 76 additions & 0 deletions semgrep-scan/tests/go_cases.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,82 @@ func rdsExplicitlyDisabledBool(ctx *pulumiCtx) {
})
}

// Runtime-config secure-by-default shape:
// `<ptr>.Bool(lo.FromPtrOr(<*bool>, true))`. Used in
// simple-container-com/api: nil → true (encrypted), explicit value
// otherwise. Paired with `IgnoreChanges([]string{"storageEncrypted"})`
// on the resource opts (not shown — pairing isn't regex-verified).
// Must NOT fire.
type pulumiSdk struct{}

func (pulumiSdk) Bool(_ bool) bool { return false }

var sdk = pulumiSdk{}
var pulumi = pulumiSdk{}

type loPkg struct{}

func (loPkg) FromPtr(_ *bool) bool { return false }
func (loPkg) FromPtrOr(_ *bool, _ bool) bool { return false }

var lo = loPkg{}

type dbConfigShape struct{ StorageEncrypted *bool }

func rdsRuntimeSecureSdk(ctx *pulumiCtx) {
cfg := dbConfigShape{}
// ok: go-aws-rds-no-storage-encryption
_ = rds.NewInstance(ctx, "db4", &instanceArgs{
Engine: "postgres",
StorageEncrypted: sdk.Bool(lo.FromPtrOr(cfg.StorageEncrypted, true)),
})
}

// Same shape with `pulumi.Bool` prefix — must also NOT fire.
func rdsRuntimeSecurePulumi(ctx *pulumiCtx) {
cfg := dbConfigShape{}
// ok: go-aws-rds-no-storage-encryption
_ = rds.NewCluster(ctx, "cluster3", &clusterArgs{
Engine: "aurora-postgresql",
StorageEncrypted: pulumi.Bool(lo.FromPtrOr(cfg.StorageEncrypted, true)),
})
}

// Bare `lo.FromPtr(*ptr)` defaults to false when nil — DB ends up
// unencrypted-by-default. Must STILL FIRE.
func rdsBareFromPtrStillFires(ctx *pulumiCtx) {
cfg := dbConfigShape{}
// ruleid: go-aws-rds-no-storage-encryption
_ = rds.NewInstance(ctx, "db5", &instanceArgs{
Engine: "postgres",
StorageEncrypted: sdk.Bool(lo.FromPtr(cfg.StorageEncrypted)),
})
}

// `lo.FromPtrOr(*ptr, false)` is just bare-FromPtr in disguise — the
// fallback is unencrypted, so the DB defaults to unencrypted. Must
// STILL FIRE.
func rdsFromPtrOrFalseStillFires(ctx *pulumiCtx) {
cfg := dbConfigShape{}
// ruleid: go-aws-rds-no-storage-encryption
_ = rds.NewInstance(ctx, "db6", &instanceArgs{
Engine: "postgres",
StorageEncrypted: sdk.Bool(lo.FromPtrOr(cfg.StorageEncrypted, false)),
})
}

// Arbitrary wrapper that isn't the recognized helper — rule must
// FIRE (we don't trust unknown runtime expressions).
func rdsArbitraryWrapperStillFires(ctx *pulumiCtx) {
// ruleid: go-aws-rds-no-storage-encryption
_ = rds.NewInstance(ctx, "db7", &instanceArgs{
Engine: "postgres",
StorageEncrypted: sdk.Bool(someRuntimeBool()),
})
}

func someRuntimeBool() bool { return false }

// --------------------------------------------------------------------
// go-fmt-errorf-percent-v-for-error
// --------------------------------------------------------------------
Expand Down