forked from kenn-io/agentsview
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcwd_filter.go
More file actions
114 lines (106 loc) · 3.48 KB
/
Copy pathcwd_filter.go
File metadata and controls
114 lines (106 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package sync
import (
"os"
"path/filepath"
"strings"
"go.kenn.io/agentsview/internal/parser"
)
// cwdPrefixFilter gates session ingestion on the session working
// directory. An empty filter allows everything. A non-empty filter
// allows only sessions whose cwd equals a configured prefix or lives
// underneath one; sessions without a recorded cwd are rejected
// because they cannot be attributed to any workspace.
//
// Prefixes and cwds are lexically cleaned before matching and the
// path boundary is the local OS separator. The filter only ever sees
// local paths (remote sync does not apply it), so local filesystem
// semantics are the correct ones: on POSIX a backslash is an ordinary
// filename character, not a boundary, and a cwd like "/a/b/../c" is
// resolved to "/a/c" rather than prefix-matching "/a/b".
type cwdPrefixFilter struct {
prefixes []string
}
// newCwdPrefixFilter normalizes the configured prefixes: entries are
// trimmed, blank entries are dropped, and each remaining entry is
// cleaned with filepath.Clean so "/a/b/" and "/a/b" behave
// identically and ".." components cannot linger in a prefix.
func newCwdPrefixFilter(prefixes []string) cwdPrefixFilter {
normalized := make([]string, 0, len(prefixes))
for _, p := range prefixes {
p = strings.TrimSpace(p)
if p == "" {
continue
}
normalized = append(normalized, filepath.Clean(p))
}
return cwdPrefixFilter{prefixes: normalized}
}
func (f cwdPrefixFilter) empty() bool {
return len(f.prefixes) == 0
}
// allows reports whether a session with the given cwd may be
// ingested. Matching is path-boundary aware: prefix "/a/b" matches
// "/a/b" and "/a/b/c" but not "/a/bc".
func (f cwdPrefixFilter) allows(cwd string) bool {
if f.empty() {
return true
}
if cwd == "" {
return false
}
cwd = filepath.Clean(cwd)
sep := string(os.PathSeparator)
for _, p := range f.prefixes {
if cwd == p {
return true
}
prefix := p
if !strings.HasSuffix(prefix, sep) {
prefix += sep
}
if strings.HasPrefix(cwd, prefix) {
return true
}
}
return false
}
// sourceAllowsParserExclusions reports whether a source's parser
// exclusions (including engine stale-row cleanup) may delete archived
// rows. When the cwd allow-list is active, a source proves it is
// inside the list by producing at least one allowed session or
// incremental update; a source with no allowed output is frozen — its
// exclusions would erase archived sessions whose replacement writes
// the filter vetoes, which the ingestion-only contract forbids.
// Zero-result exclusion carriers (e.g. a file that parses to no live
// session) have no cwd to judge, so they are frozen too.
func (e *Engine) sourceAllowsParserExclusions(res processResult) bool {
if e.cwdFilter.empty() {
return true
}
if res.incremental != nil && e.cwdFilter.allows(res.incremental.cwd) {
return true
}
for _, pr := range res.results {
if e.cwdFilter.allows(pr.Session.Cwd) {
return true
}
}
return false
}
// splitResultsByCwdFilter returns the parsed sessions the cwd
// allow-list admits and the number it vetoes. With no filter
// configured it returns the input untouched.
func (e *Engine) splitResultsByCwdFilter(
results []parser.ParseResult,
) ([]parser.ParseResult, int) {
if e.cwdFilter.empty() || len(results) == 0 {
return results, 0
}
allowed := make([]parser.ParseResult, 0, len(results))
for _, pr := range results {
if e.cwdFilter.allows(pr.Session.Cwd) {
allowed = append(allowed, pr)
}
}
return allowed, len(results) - len(allowed)
}