v0.22.2 cli presentation improvements, hardened config
Test / test (push) Canceled after 0s
Test / e2e (push) Canceled after 0s

This commit is contained in:
lixen committed 2026-10-04 00:04:03 -04:00
1 parent 4eba6fc834
commit 15745682bc
8 files changed
+49 -44

No files matched your search

+1 -1
View File
@@ -26,7 +26,7 @@ pipelines have [their own flags](#pipelines):
``` ```
--<path>=<value> e.g. --logging.level=debug --<path>=<value> e.g. --logging.level=debug
--<path> <value> e.g. --logging.level debug --<path> <value> e.g. --logging.level debug
--<path> bare flag, means true --<path> bare flag: true, for a boolean path only
``` ```
### Common ### Common
+8 -3
View File
@@ -231,13 +231,18 @@ lw --logging.level debug # space form also works
lw --quiet # bare flag means true lw --quiet # bare flag means true
``` ```
Unrecognized flags are reported on stderr before the logger exists and are then A bare flag is accepted only for a boolean key: `--logging.file.directory
ignored: --quiet` fails with `--logging.file.directory needs a value`. Unrecognized flags,
positional words and single-dash arguments are reported on stderr before the
logger exists and are then ignored:
``` ```
Warning: unrecognized flags ignored: [pipelines.0.name] Warning: unrecognized flags ignored: [pipelines.0.name -q]
``` ```
An empty `LOGWISP_<PATH>` variable counts as unset, as the pipeline variables
do.
Array-indexed paths such as `--pipelines.0.name=x` are unrecognized; whole Array-indexed paths such as `--pipelines.0.name=x` are unrecognized; whole
pipelines have [their own flags](cli.md#pipelines). pipelines have [their own flags](cli.md#pipelines).
+10
View File
@@ -26,6 +26,16 @@ configure it, and — equally important — what it does not yet do.
[Startup Warnings](#startup-warnings). [Startup Warnings](#startup-warnings).
- Unknown configuration keys rejected: a typo in `tls`, `auth` or a table path - Unknown configuration keys rejected: a typo in `tls`, `auth` or a table path
fails startup. fails startup.
- Configuration input hardened (lixenwraith/config v0.2.2, reviewed with
adversarial tests and fuzzing like `auth`):
- a string in the file is one list entry, so a filter pattern holding a
comma is not split into patterns that match nothing;
- the file is opened without blocking, so a FIFO swapped in for it cannot
hang the watcher or a `SIGHUP` reload;
- a permission change on the file is reported once and does not stop
auto-reload;
- conversion errors name the key, never the value, so a secret placed in
the wrong key does not reach the log.
**Not implemented:** **Not implemented:**
+5 -27
View File
@@ -131,34 +131,12 @@ its input queue into the client queues, then let every client writer drain its
queue within a bound (`write_timeout_ms`) before the disconnect frame. Verify queue within a bound (`write_timeout_ms`) before the disconnect frame. Verify
with a finite stdin into each sink and a connected client counting lines. with a finite stdin into each sink and a connected client counting lines.
## 3. Hardening review of `lixenwraith/config` and `lixenwraith/toml` ## 3. Bump `lixenwraith/toml`
Both parse untrusted-shaped input (config files, environment, command line) Its hardening commits (a table budget, TOML 1.1 escapes, control characters
and were not reviewed with the auth work. Review them as `auth` was reviewed: refused outside strings, bounded errors that never print a value) wait to merge
adversarial lenses, reproduction before belief, a fix per confirmed finding in in that repository. Then bump it here and drop this item; security.md's library
the library repository, then a dependency bump here. note already covers it.
- toml parser:
- Limits on nesting depth, key count, string and array length, and number
sizes.
- Behaviour on invalid UTF-8, duplicate keys and tables, dotted-key and
inline-table redefinition.
- Fuzz targets (`go test -fuzz`) seeded with the TOML test suite.
- config:
- The weakly typed decoder: string-to-number overflow, duration and size
parsing, the comma split into slices.
- `maxValueDepth` and `MaxValueSize`, environment and CLI precedence, and
unknown-key reporting.
- Files:
- `PreventPathTraversal`, symlink handling and the file-size cap.
- The watcher: polling, debounce, a file replaced by rename or symlink swap,
TOCTOU between stat and read.
- Errors: values must never echo secrets (a misplaced password in a config
value reported back in an error).
- Deliverables:
- Findings per library.
- Fuzz targets committed in the library repositories.
- A note in this repository's security.md once done.
## 4. Packaging: AUR, FreeBSD ports, Debian ## 4. Packaging: AUR, FreeBSD ports, Debian
+2 -2
View File
@@ -4,9 +4,9 @@ go 1.27.1
require ( require (
github.com/lixenwraith/auth v0.0.0-20261002191639-3ed0e995807c github.com/lixenwraith/auth v0.0.0-20261002191639-3ed0e995807c
github.com/lixenwraith/config v0.2.1 github.com/lixenwraith/config v0.2.2
github.com/lixenwraith/log v0.1.11 github.com/lixenwraith/log v0.1.11
github.com/lixenwraith/toml v0.0.0-20261001212158-cede215365c0 github.com/lixenwraith/toml v0.1.3
golang.org/x/term v0.46.0 golang.org/x/term v0.46.0
) )
+4 -4
View File
@@ -2,12 +2,12 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/lixenwraith/auth v0.0.0-20261002191639-3ed0e995807c h1:zwpVa85g267vo13UCzCao4lfVgUPaU79CmxPW9OdF8w= github.com/lixenwraith/auth v0.0.0-20261002191639-3ed0e995807c h1:zwpVa85g267vo13UCzCao4lfVgUPaU79CmxPW9OdF8w=
github.com/lixenwraith/auth v0.0.0-20261002191639-3ed0e995807c/go.mod h1:411JUqnbQgy9jgoFV+fH91TfVUEfzYA4dyPv7f4c264= github.com/lixenwraith/auth v0.0.0-20261002191639-3ed0e995807c/go.mod h1:411JUqnbQgy9jgoFV+fH91TfVUEfzYA4dyPv7f4c264=
github.com/lixenwraith/config v0.2.1 h1:a1VLWhtAi1F7FWtRtFVpPkvemnBEf+4xrvDupvwHYtg= github.com/lixenwraith/config v0.2.2 h1:X+l3Tf8Rbk49cipYBJU7/pfdw5wDwD4hRxtFH7rZgOc=
github.com/lixenwraith/config v0.2.1/go.mod h1:yfJQwKS+/6Dg70GIoHeYc0eYztOPETpqe6nUIbakcyg= github.com/lixenwraith/config v0.2.2/go.mod h1:yfJQwKS+/6Dg70GIoHeYc0eYztOPETpqe6nUIbakcyg=
github.com/lixenwraith/log v0.1.11 h1:dKWLnaR8dhXxj4l/aU5XZiFIsr8geaJQbOf8m7SiKhU= github.com/lixenwraith/log v0.1.11 h1:dKWLnaR8dhXxj4l/aU5XZiFIsr8geaJQbOf8m7SiKhU=
github.com/lixenwraith/log v0.1.11/go.mod h1:EE8i5nSAW2yZBTytKuhdgehmgzrby6kW1k5DyH1SvBg= github.com/lixenwraith/log v0.1.11/go.mod h1:EE8i5nSAW2yZBTytKuhdgehmgzrby6kW1k5DyH1SvBg=
github.com/lixenwraith/toml v0.0.0-20261001212158-cede215365c0 h1:ue1pILlQAovrxTMSaKII4AbZ7yY4mNs3cyivoSBwMmA= github.com/lixenwraith/toml v0.1.3 h1:mhnNVLNogdaqjGd3eC4rV9ZqY3j1Sv+DK1erZ+SPFeI=
github.com/lixenwraith/toml v0.0.0-20261001212158-cede215365c0/go.mod h1:itcT/Vt5mwmX9KVGCMsWZr6vbU/JgZ24wV/I0J5cMt0= github.com/lixenwraith/toml v0.1.3/go.mod h1:itcT/Vt5mwmX9KVGCMsWZr6vbU/JgZ24wV/I0J5cMt0=
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
+18
View File
@@ -301,3 +301,21 @@ func TestNoConfigFileMeansQuietLogging(t *testing.T) {
t.Fatalf("environment over the file-less default: %+v %v", cfg, err) t.Fatalf("environment over the file-less default: %+v %v", cfg, err)
} }
} }
// A string in the file is one list entry: split at its comma, an exclude
// pattern such as password=\S{8,64} would become two that match nothing
func TestFileStringIsOneListEntry(t *testing.T) {
isolateConfig(t)
testutil.WriteFile(t, "logwisp.toml", "[[pipelines]]\nname = \"p\"\n"+
"[[pipelines.plugin_sources]]\nid = \"in\"\ntype = \"null\"\n[[pipelines.plugin_sinks]]\nid = \"out\"\ntype = \"null\"\n"+
"[[pipelines.flow.filters]]\ntype = \"exclude\"\npatterns = \"password=\\\\S{8,64}\"\n")
m, err := Load(nil)
if err != nil {
t.Fatal(err)
}
defer m.Close()
cfg, err := m.Snapshot()
if err != nil || !reflect.DeepEqual(cfg.Pipelines[0].Flow.Filters[0].Patterns, []string{`password=\S{8,64}`}) {
t.Fatalf("patterns: %v %v", cfg.Pipelines[0].Flow.Filters, err)
}
}
+1 -7
View File
@@ -111,9 +111,7 @@ func Scan(configMap map[string]any, target any) error {
} }
// checkKeys walks nested tables against the toml tags of t, refusing unknown // checkKeys walks nested tables against the toml tags of t, refusing unknown
// keys and making a lone string for a list field a one-entry list: the weakly // keys. Map-typed fields hold free-form keys and are not descended into.
// typed decoder would split it at commas, which a pattern or a DN may hold.
// Map-typed fields hold free-form keys and are not descended into.
func checkKeys(m map[string]any, t reflect.Type, prefix string) error { func checkKeys(m map[string]any, t reflect.Type, prefix string) error {
for t.Kind() == reflect.Pointer || t.Kind() == reflect.Slice { for t.Kind() == reflect.Pointer || t.Kind() == reflect.Slice {
t = t.Elem() t = t.Elem()
@@ -134,10 +132,6 @@ func checkKeys(m map[string]any, t reflect.Type, prefix string) error {
} }
var tables []map[string]any var tables []map[string]any
switch v := m[key].(type) { switch v := m[key].(type) {
case string:
if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.String {
m[key] = []string{v}
}
case map[string]any: case map[string]any:
if err := checkKeys(v, ft, prefix+key+"."); err != nil { if err := checkKeys(v, ft, prefix+key+"."); err != nil {
return err return err