Repository: caddyserver/caddy (language: go)

Issue:
Reject unrecognized directives
It's an oversight to miss having `default` in some Caddyfile unmarshallers in some modules. Here's an example where `default` is missing, and allowing invalid/false directives without much of a warning:

https://github.com/caddyserver/caddy/blob/fd4de7e0ae4debd54e2a9a4aba10947cd4c3b178/modules/logging/filewriter.go#L248C2-L317C3

We need to comb through the code to add `default` where it's missing. It'll be a breaking change, but it's a correction of behavior because whatever false directive the user used gives the false impression of doing something.


Discussion / hints:
Shouldn't be a breaking change by our definition, which is according to docs / intended behavior. Sounds good to me :+1:

Fix patch:
diff --git a/caddyconfig/httpcaddyfile/serveroptions.go b/caddyconfig/httpcaddyfile/serveroptions.go
index 40a8af20962..d60ce51a94c 100644
--- a/caddyconfig/httpcaddyfile/serveroptions.go
+++ b/caddyconfig/httpcaddyfile/serveroptions.go
@@ -246,6 +246,8 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
 				switch d.Val() {
 				case "per_host":
 					serverOpts.Metrics.PerHost = true
+				default:
+					return nil, d.Errf("unrecognized metrics option '%s'", d.Val())
 				}
 			}
 
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go
index e5ca28b95b6..01bd7b8b4de 100644
--- a/modules/caddyhttp/matchers.go
+++ b/modules/caddyhttp/matchers.go
@@ -1342,6 +1342,8 @@ func (m *MatchTLS) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 			case "early_data":
 				var false bool
 				m.HandshakeComplete = &false
+			default:
+				return d.Errf("unrecognized option '%s'", d.Val())
 			}
 		}
 		if d.NextArg() {
diff --git a/modules/caddytls/internalissuer.go b/modules/caddytls/internalissuer.go
index b55757af585..be779757a87 100644
--- a/modules/caddytls/internalissuer.go
+++ b/modules/caddytls/internalissuer.go
@@ -178,6 +178,9 @@ func (iss *InternalIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 				return d.ArgErr()
 			}
 			iss.SignWithRoot = true
+
+		default:
+			return d.Errf("unrecognized subdirective '%s'", d.Val())
 		}
 	}
 	return nil
diff --git a/modules/logging/filewriter.go b/modules/logging/filewriter.go
index ef3211cbbb5..0999bbfb27f 100644
--- a/modules/logging/filewriter.go
+++ b/modules/logging/filewriter.go
@@ -313,6 +313,9 @@ func (fw *FileWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 				return d.Errf("negative roll_keep_for duration: %v", keepFor)
 			}
 			fw.RollKeepDays = int(math.Ceil(keepFor.Hours() / 24))
+
+		default:
+			return d.Errf("unrecognized subdirective '%s'", d.Val())
 		}
 	}
 	return nil
diff --git a/modules/logging/netwriter.go b/modules/logging/netwriter.go
index dc2b0922cba..7d8481e3c3c 100644
--- a/modules/logging/netwriter.go
+++ b/modules/logging/netwriter.go
@@ -145,6 +145,9 @@ func (nw *NetWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 				return d.ArgErr()
 			}
 			nw.SoftStart = true
+
+		default:
+			return d.Errf("unrecognized subdirective '%s'", d.Val())
 		}
 	}
 	return nil


Test patch: