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{