@marcel91200 / caddy Mirror · read-only / commits / 64b64c61
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 08:30 UTC

log: don't allow overwriting singly-assigned vals (#7927)

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

Mohammed Al Sahaf committed Aug 8, 2026 at 00:59 UTC 64b64c61ebc40ea37280b2baa1a7a492cc1156c5
2 files changed +76
caddyconfig/httpcaddyfile/builtins.go
+34
@@ -995,6 +995,17 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
995 // with a wildcard domain
996 customHostnames := []string{}
997 noHostname := false
998 + // track single-valued subdirectives so that repeating one is an error
999 + // rather than silently overriding the previous value; see
1000 + // https://caddy.community/t/logs-not-getting-written-to-journal/33852
1001 + seen := make(map[string]bool)
1002 + assertUnique := func(subdir string) error {
1003 + if seen[subdir] {
1004 + return h.Errf("'%s' subdirective specified more than once in the same log block", subdir)
1005 + }
1006 + seen[subdir] = true
1007 + return nil
1008 + }
1009 for h.NextBlock(0) {
1010 switch h.Val() {
1011 case "hostnames":
@@ -1008,6 +1019,9 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
1019 customHostnames = append(customHostnames, args...)
1020
1021 case "output":
1022 + if err := assertUnique(h.Val()); err != nil {
1023 + return nil, err
1024 + }
1025 if !h.NextArg() {
1026 return nil, h.ArgErr()
1027 }
@@ -1040,14 +1054,22 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
1054 cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings)
1055
1056 case "sampling":
1057 + if err := assertUnique(h.Val()); err != nil {
1058 + return nil, err
1059 + }
1060 d := h.Dispenser.NewFromNextSegment()
1061 for d.NextArg() {
1062 // consume any tokens on the same line, if any.
1063 }
1064
1065 sampling := &caddy.LogSampling{}
1066 + samplingSeen := make(map[string]bool)
1067 for nesting := d.Nesting(); d.NextBlock(nesting); {
1068 subdir := d.Val()
1069 + if samplingSeen[subdir] {
1070 + return nil, d.Errf("'%s' specified more than once in the same sampling block", subdir)
1071 + }
1072 + samplingSeen[subdir] = true
1073 switch subdir {
1074 case "interval":
1075 if !d.NextArg() {
@@ -1084,6 +1106,9 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
1106 cl.Sampling = sampling
1107
1108 case "core":
1109 + if err := assertUnique(h.Val()); err != nil {
1110 + return nil, err
1111 + }
1112 if !h.NextArg() {
1113 return nil, h.ArgErr()
1114 }
@@ -1100,6 +1125,9 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
1125 cl.CoreRaw = caddyconfig.JSONModuleObject(core, "module", moduleName, h.warnings)
1126
1127 case "format":
1128 + if err := assertUnique(h.Val()); err != nil {
1129 + return nil, err
1130 + }
1131 if !h.NextArg() {
1132 return nil, h.ArgErr()
1133 }
@@ -1116,6 +1144,9 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
1144 cl.EncoderRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, h.warnings)
1145
1146 case "level":
1147 + if err := assertUnique(h.Val()); err != nil {
1148 + return nil, err
1149 + }
1150 if !h.NextArg() {
1151 return nil, h.ArgErr()
1152 }
@@ -1141,6 +1172,9 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
1172 }
1173
1174 case "no_hostname":
1175 + if err := assertUnique(h.Val()); err != nil {
1176 + return nil, err
1177 + }
1178 if h.NextArg() {
1179 return nil, h.ArgErr()
1180 }
caddyconfig/httpcaddyfile/builtins_test.go
+42
@@ -76,6 +76,48 @@ func TestLogDirectiveSyntax(t *testing.T) {
76 output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"sampling":{"interval":2000000000,"first":3,"thereafter":4},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
77 expectError: false,
78 },
79 + {
80 + input: `:8080 {
81 + log {
82 + output stdout
83 + output file foo.log
84 + }
85 + }
86 + `,
87 + expectError: true,
88 + },
89 + {
90 + input: `:8080 {
91 + log {
92 + format json
93 + format console
94 + }
95 + }
96 + `,
97 + expectError: true,
98 + },
99 + {
100 + input: `:8080 {
101 + log {
102 + level INFO
103 + level DEBUG
104 + }
105 + }
106 + `,
107 + expectError: true,
108 + },
109 + {
110 + input: `:8080 {
111 + log {
112 + sampling {
113 + interval 1s
114 + interval 2s
115 + }
116 + }
117 + }
118 + `,
119 + expectError: true,
120 + },
121 } {
122
123 adapter := caddyfile.Adapter{