VYPR
High severity7.8NVD Advisory· Published Jun 9, 2026

CVE-2026-8795

CVE-2026-8795

Description

YAML injection in Velociraptor allows attackers to execute arbitrary VQL on analyst machines via crafted collection ZIPs.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

YAML injection in Velociraptor allows attackers to execute arbitrary VQL on analyst machines via crafted collection ZIPs.

Vulnerability

A YAML injection vulnerability exists in the Windows.Collectors.Remapping artifact of Rapid7 Velociraptor before version 0.76.6. The hostname field in client_info.json within a collection ZIP is inserted into a YAML template using Go's text/template without proper YAML escaping. This allows for the injection of new mount remapping entries [1].

Exploitation

An attacker must provide a crafted collection ZIP file. By embedding literal double quotes and newlines within the hostname field, an attacker can break out of the YAML quoted string. This allows them to inject a new mount remapping entry containing arbitrary VQL code [1].

Impact

When an analyst applies the crafted remapping file using the --remap option, the injected VQL executes on their machine with NullACLManager privileges. This grants the attacker all permissions and bypasses sandboxing, effectively allowing arbitrary code execution on the analyst's system [1].

Mitigation

Rapid7 Velociraptor version 0.76.6 and later contain a fix for this vulnerability. Users are advised to upgrade to this version or a later one. This vulnerability only affects users who use remapping to operate on offline collections [1].

AI Insight generated on Jun 9, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

1
20c779e55e69

Sync master to 0.76 release branch (#4859)

https://github.com/Velocidex/velociraptorMike CohenJun 3, 2026Fixed in 0.76.6via release-tag
36 files changed · +542 314
  • accessors/mount.go+10 8 modified
    @@ -22,7 +22,7 @@ import (
     	"www.velocidex.com/golang/vfilter"
     )
     
    -// Mount tree is very sparse so we dont really need a map here -
    +// Mount tree is very sparse so we don't really need a map here -
     // linear search is fast enough.
     
     // The mount accessor is essentially a redirector - it needs to find a
    @@ -71,9 +71,10 @@ func (self *node) Debug() string {
     }
     
     // Lookup a child by name. If not found returns nil
    -func (self *node) GetChild(name string) *node {
    +func (self *node) GetChild(
    +	name string, manipulator PathManipulator) *node {
     	for _, c := range self.children {
    -		if c.name == name {
    +		if manipulator.ComponentEqual(c.name, name) {
     			return c
     		}
     	}
    @@ -83,9 +84,10 @@ func (self *node) GetChild(name string) *node {
     
     // Get the child node for the given name. If the node is not found, we
     // create a new node based on our last mount point.
    -func (self *node) MakeChild(name string) *node {
    +func (self *node) MakeChild(
    +	name string, manipulator PathManipulator) *node {
     	for _, c := range self.children {
    -		if c.name == name {
    +		if manipulator.ComponentEqual(c.name, name) {
     			return c
     		}
     	}
    @@ -193,7 +195,7 @@ func (self *MountFileSystemAccessor) getDelegateNode(os_path *OSPath) (
     
     	for idx, c := range os_path.Components {
     		if c != "" {
    -			next_node := node.GetChild(c)
    +			next_node := node.GetChild(c, os_path.Manipulator)
     
     			// There is no internal mount point, use the last known
     			// mounted filesystem.
    @@ -353,13 +355,13 @@ func (self *MountFileSystemAccessor) AddMapping(
     	target *OSPath,
     	source_accessor FileSystemAccessor) {
     
    -	// Walk the tree and create the sentinal node. NOTE: split the
    +	// Walk the tree and create the sentinel node. NOTE: split the
     	// path according to the target accessor we are emulating.
     	node := self.root
     
     	for _, c := range target.Components {
     		if c != "" {
    -			node = node.MakeChild(c)
    +			node = node.MakeChild(c, target.Manipulator)
     		}
     	}
     
    
  • accessors/s3/s3_minio.go+2 1 modified
    @@ -82,6 +82,7 @@ func (self RawS3SystemAccessor) ReadDirWithOSPath(
     	}
     
     	if len(path.Components) == 0 {
    +		metricS3OpsListObjects.Inc()
     		resp, err := s3Client.ListBuckets(self.ctx)
     		if err != nil {
     			return nil, err
    @@ -256,7 +257,7 @@ func init() {
     	accessors.Register(&RawS3SystemAccessor{})
     }
     
    -// Set the page size for tests. Normally we dont need to adjust this
    +// Set the page size for tests. Normally we don't need to adjust this
     // at all. Used in tests.
     func SetPageSize(size int) {
     	mu.Lock()
    
  • accessors/zip/zip.go+3 3 modified
    @@ -117,10 +117,10 @@ func (self *Tracker) Dec(filename string) {
     		} else {
     			self.refs[filename] = prev
     		}
    -
    -	} else {
    -		panic(filename)
    +		return
     	}
    +
    +	fmt.Printf("ZipTracker: Close of untracked Open: %v\n", filename)
     }
     
     func (self *Tracker) ProfileWriter(ctx context.Context,
    
  • artifacts/definitions/Server/Internal/ToolDependencies.yaml+10 10 modified
    @@ -7,19 +7,19 @@ description: |
     
     tools:
       - name: VelociraptorWindows
    -    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.4-windows-amd64.exe
    +    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.6-windows-amd64.exe
         serve_locally: true
    -    version: 0.76.4
    +    version: 0.76.6
     
       - name: VelociraptorWindows_x86
    -    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.4-windows-386.exe
    +    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.6-windows-386.exe
         serve_locally: true
    -    version: 0.76.4
    +    version: 0.76.6
     
       - name: VelociraptorLinux
    -    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.4-linux-amd64-musl
    +    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.6-linux-amd64-musl
         serve_locally: true
    -    version: 0.76.4
    +    version: 0.76.6
     
       # On MacOS we cannot embed the config in the binary so we use a
       # shell script stub instead. See
    @@ -31,14 +31,14 @@ tools:
         serve_locally: true
     
       - name: VelociraptorWindowsMSI
    -    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.4-windows-amd64.msi
    +    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.6-windows-amd64.msi
         serve_locally: true
    -    version: 0.76.4
    +    version: 0.76.6
     
       - name: VelociraptorWindows_x86MSI
    -    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.4-windows-386.msi
    +    url: https://github.com/Velocidex/velociraptor/releases/download/v0.76/velociraptor-v0.76.6-windows-386.msi
         serve_locally: true
    -    version: 0.76.4
    +    version: 0.76.6
     
       - name: DocsIndex
         url: https://github.com/Velocidex/velociraptor-docs/raw/refs/heads/gh-pages/docs_index/docs_index_v1.zip
    
  • artifacts/definitions/Windows/Collectors/Remapping.yaml+34 18 modified
    @@ -1,6 +1,7 @@
     name: Windows.Collectors.Remapping
     description: |
    -  Calculates a remapping config for a collection container (zip).
    +  Creates filesystem and registry remappings for offline analysis of
    +  Velociraptor collection containers.
     
       The remapping allows Velociraptor to treat the collection container as a dead
       disk image in a similar way to `Generic.Utils.DeadDiskRemapping`. This means
    @@ -143,7 +144,7 @@ parameters:
             - EXECVE
           - type: impersonation
             os: windows
    -        hostname: "{{ .Hostname }}"
    +        hostname: {{ .Hostname | quote }}
             env:
             - key: SystemRoot
               value: C:\Windows
    @@ -174,6 +175,16 @@ parameters:
               accessor: zip
             "on":
               accessor: zip
    +      - type: shadow
    +        from:
    +          accessor: zip_nocase
    +        "on":
    +          accessor: zip_nocase
    +      - type: shadow
    +        from:
    +          accessor: collector
    +        "on":
    +          accessor: collector
           - type: shadow
             from:
               accessor: raw_reg
    @@ -193,14 +204,17 @@ export: |
                                            path_type="zip",
                                            Path="client_info.json")))
     
    -  -- Set the hostname either from the parameters or read it from the file itself
    -  LET DerivedHostname = Hostname ||
    -                        CollectionHostinfo.Hostname ||
    -                        CollectionHostinfo.hostname
    +  -- Set the hostname either from the parameters or read it from the
    +  -- file itself. Validate the hostname to have only allowed hostname chars.
    +  LET DerivedHostname = parse_string_with_regex(
    +    string=Hostname || CollectionHostinfo.Hostname || CollectionHostinfo.hostname,
    +    regex="([a-z0-9-.]+)"
    +  ).g1
     
       LET Remappings = parse_yaml(
                         filename=template(template=CommonRemapping,
    -                                      expansion=dict(Hostname=DerivedHostname)),
    +                                      expansion=dict(
    +                                        Hostname=DerivedHostname || "Unknown")),
                         accessor="data")
     
       -- Normalize the drive name from device to drive. NTFS accessor uses
    @@ -222,16 +236,17 @@ export: |
       LET ScopeTemplate <= if(condition=ZIP_PASSWORDS,
                               then=template(template='''
       LET OVERLAY_ACCESSOR_DELEGATES <= dict(
    -       accessor="{{ .Accessor }}",
    -       paths={{ .Paths }})
    +       accessor={{ .Accessor | quote }},
    +       paths={{"{{ .Paths }}"}})
    +
    +  LET ZIP_PASSWORDS <= {{ .ZipPassword | quote }}
    +  ''', expansion=dict(ZipPassword=ZIP_PASSWORDS, Accessor=Accessor)),
     
    -  LET ZIP_PASSWORDS <= "{{ .ZipPassword }}"
    -  ''', expansion=dict(ZipPassword=ZIP_PASSWORDS, Accessor=Accessor, Paths="{{ .Paths }}")),
    -                          else=template(template='''
    +       else=template(template='''
       LET OVERLAY_ACCESSOR_DELEGATES <= dict(
    -       accessor="{{ .Accessor }}",
    -       paths={{ .Paths }})
    -  ''', expansion=dict(Accessor=Accessor, Paths="{{ .Paths }}"))
    +       accessor={{ .Accessor | quote }},
    +       paths={{"{{ .Paths }}"}})
    +  ''', expansion=dict(Accessor=Accessor))
       )
     
       -- Overlay the files that were fetched with all accessors into the
    @@ -341,12 +356,13 @@ sources:
     
     - name: TestRegistryAccess
       query: |
    -    LET _ <= remap(config=YamlText)
    +    LET _ <= remap(config=YamlText, clear=TRUE)
         SELECT OSPath
         FROM glob(globs="HKEY_LOCAL_MACHINE/Software/*", accessor='registry')
     
     - name: TestFileAccess
       query: |
    -    LET _ <= remap(config=YamlText)
    +    LET _ <= remap(config=YamlText, clear=TRUE)
         SELECT OSPath
    -    FROM glob(globs="*/*")
    +    FROM glob(globs="*/**")
    +    LIMIT 5
    
  • artifacts/testdata/server/testcases/magic.out.yaml+2 2 modified
    @@ -2,7 +2,7 @@
     Query: SELECT magic(path=srcDir + '/artifacts/testdata/files/notnbt.exe'), magic(path=srcDir + '/artifacts/testdata/files/test.docx'), magic(path=srcDir + '/artifacts/testdata/files/access.log') FROM scope()
     Output: [
      {
    -  "magic(path=srcDir + '/artifacts/testdata/files/notnbt.exe')": "PE32 executable (console) Intel 80386, for MS Windows",
    +  "magic(path=srcDir + '/artifacts/testdata/files/notnbt.exe')": "PE32 executable for MS Windows 10.00 (console), Intel i386, 5 sections",
       "magic(path=srcDir + '/artifacts/testdata/files/test.docx')": "Microsoft Word 2007+",
       "magic(path=srcDir + '/artifacts/testdata/files/access.log')": "ASCII text"
      }
    @@ -20,7 +20,7 @@ Output: [
     Query: SELECT magic(path=srcDir + '/artifacts/testdata/files/notnbt.exe', accessor="file"), magic(path=sampleLog, accessor="data", magic=ApacheMagic) FROM scope()
     Output: [
      {
    -  "magic(path=srcDir + '/artifacts/testdata/files/notnbt.exe', accessor=\"file\")": "PE32 executable (console) Intel 80386, for MS Windows",
    +  "magic(path=srcDir + '/artifacts/testdata/files/notnbt.exe', accessor=\"file\")": "PE32 executable for MS Windows 10.00 (console), Intel i386, 5 sections",
       "magic(path=sampleLog, accessor=\"data\", magic=ApacheMagic)": "Apache Logs, ASCII text"
      }
     ]
    
  • bin/debian.go+45 14 modified
    @@ -30,9 +30,11 @@ import (
     	"path/filepath"
     
     	"github.com/Velocidex/ordereddict"
    +	"www.velocidex.com/golang/velociraptor/config"
     	logging "www.velocidex.com/golang/velociraptor/logging"
     	"www.velocidex.com/golang/velociraptor/services"
     	"www.velocidex.com/golang/velociraptor/startup"
    +	"www.velocidex.com/golang/velociraptor/utils/tempfile"
     	"www.velocidex.com/golang/velociraptor/vql/acl_managers"
     )
     
    @@ -69,21 +71,33 @@ func doServerDeb() error {
     	// deb on the same system where the logs should go.
     	logging.DisableLogging()
     
    -	config_obj, err := makeDefaultConfigLoader().
    -		WithRequiredFrontend().LoadAndValidate()
    +	if *config_path == "" {
    +		return fmt.Errorf("A server config must be specified using the --config flag")
    +	}
    +
    +	abs_config_path, err := filepath.Abs(*config_path)
    +	if err != nil {
    +		return err
    +	}
    +
    +	temp_dir, err := tempfile.TempDir("debian")
     	if err != nil {
    -		return fmt.Errorf("Unable to load config file: %w", err)
    +		return err
     	}
    +	defer os.RemoveAll(temp_dir)
    +
    +	blank_config := config.GetDefaultConfig()
    +	blank_config.Datastore.Location = temp_dir
    +	blank_config.Datastore.FilestoreDirectory = temp_dir
     
     	ctx, cancel := install_sig_handler()
     	defer cancel()
     
    -	sm, err := startup.StartToolServices(ctx, config_obj)
    -	defer sm.Close()
    -
    +	sm, err := startup.StartToolServices(ctx, blank_config)
     	if err != nil {
     		return err
     	}
    +	defer sm.Close()
     
     	if *server_debian_command_binary == "" {
     		*server_debian_command_binary, err = os.Executable()
    @@ -110,7 +124,8 @@ func doServerDeb() error {
     		Env: ordereddict.NewDict().
     			Set("Release", *debian_command_release).
     			Set("Output", *server_debian_command_output).
    -			Set("BinaryToPackage", *server_debian_command_binary),
    +			Set("BinaryToPackage", *server_debian_command_binary).
    +			Set("ConfigPath", abs_config_path),
     	}
     
     	query := `
    @@ -119,9 +134,10 @@ func doServerDeb() error {
            SELECT OSPath
            FROM deb_create(exe=BinaryToPackage, server=TRUE,
                            directory_name=Output,
    +                       config=read_file(filename=ConfigPath, length=1000000),
                            release=Release)`
     
    -	err = runQueryWithEnv(query, builder, "json")
    +	err = runQueryWithEnv(ctx, query, builder, "json")
     	if err != nil {
     		return err
     	}
    @@ -134,16 +150,29 @@ func doClientDeb() error {
     	// deb on the same system where the logs should go.
     	logging.DisableLogging()
     
    -	config_obj, err := makeDefaultConfigLoader().
    -		WithRequiredClient().LoadAndValidate()
    +	if *config_path == "" {
    +		return fmt.Errorf("A server config must be specified using the --config flag")
    +	}
    +
    +	abs_config_path, err := filepath.Abs(*config_path)
     	if err != nil {
    -		return fmt.Errorf("Unable to load config file: %w", err)
    +		return err
     	}
     
    +	temp_dir, err := tempfile.TempDir("debian")
    +	if err != nil {
    +		return err
    +	}
    +	defer os.RemoveAll(temp_dir)
    +
    +	blank_config := config.GetDefaultConfig()
    +	blank_config.Datastore.Location = temp_dir
    +	blank_config.Datastore.FilestoreDirectory = temp_dir
    +
     	ctx, cancel := install_sig_handler()
     	defer cancel()
     
    -	sm, err := startup.StartToolServices(ctx, config_obj)
    +	sm, err := startup.StartToolServices(ctx, blank_config)
     	defer sm.Close()
     
     	if err != nil {
    @@ -175,7 +204,8 @@ func doClientDeb() error {
     		Env: ordereddict.NewDict().
     			Set("Release", *debian_command_release).
     			Set("Output", *client_debian_command_output).
    -			Set("BinaryToPackage", *client_debian_command_binary),
    +			Set("BinaryToPackage", *client_debian_command_binary).
    +			Set("ConfigPath", abs_config_path),
     	}
     
     	query := `
    @@ -184,9 +214,10 @@ func doClientDeb() error {
            SELECT OSPath
            FROM deb_create(exe=BinaryToPackage,
                            directory_name=Output,
    +                       config=read_file(filename=ConfigPath, length=1000000),
                            release=Release)`
     
    -	err = runQueryWithEnv(query, builder, "json")
    +	err = runQueryWithEnv(ctx, query, builder, "json")
     	if err != nil {
     		return err
     	}
    
  • bin/offline_decrypt.go+1 1 modified
    @@ -118,7 +118,7 @@ FROM stat(filename=copy(
        filename=PATHSPEC + "data.zip",
        accessor="zip", dest=OUTPUT))
     `
    -		return runQueryWithEnv(query, builder, *collector_decrypt_format)
    +		return runQueryWithEnv(ctx, query, builder, *collector_decrypt_format)
     	}
     
     	if *collector_decrypt_show_password {
    
  • bin/offline.go+1 1 modified
    @@ -289,7 +289,7 @@ SELECT * FROM if(condition=Spec.OS, then={
        )
     })
     `
    -	err = runQueryWithEnv(query, builder, *collector_format)
    +	err = runQueryWithEnv(ctx, query, builder, *collector_format)
     	if err != nil {
     		return err
     	}
    
  • bin/reformat.go+1 1 modified
    @@ -75,7 +75,7 @@ func doReformat() error {
                    AND copy(accessor="data", dest=_value, filename=Result.Artifact))
            AND FALSE
         `
    -	err = runQueryWithEnv(query, builder, "json")
    +	err = runQueryWithEnv(ctx, query, builder, "json")
     	if err != nil {
     		logger.Error("reformat: error running query: %v", query)
     	}
    
  • bin/rpm.go+47 16 modified
    @@ -7,9 +7,11 @@ import (
     	"path/filepath"
     
     	"github.com/Velocidex/ordereddict"
    +	"www.velocidex.com/golang/velociraptor/config"
     	logging "www.velocidex.com/golang/velociraptor/logging"
     	"www.velocidex.com/golang/velociraptor/services"
     	"www.velocidex.com/golang/velociraptor/startup"
    +	"www.velocidex.com/golang/velociraptor/utils/tempfile"
     	"www.velocidex.com/golang/velociraptor/vql/acl_managers"
     )
     
    @@ -48,21 +50,33 @@ func doClientRPM() error {
     	// package on the same system where the logs should go.
     	logging.DisableLogging()
     
    -	config_obj, err := makeDefaultConfigLoader().
    -		WithRequiredClient().LoadAndValidate()
    +	if *config_path == "" {
    +		return fmt.Errorf("A server config must be specified using the --config flag")
    +	}
    +
    +	abs_config_path, err := filepath.Abs(*config_path)
    +	if err != nil {
    +		return err
    +	}
    +
    +	temp_dir, err := tempfile.TempDir("debian")
     	if err != nil {
    -		return fmt.Errorf("Unable to load config file: %w", err)
    +		return err
     	}
    +	defer os.RemoveAll(temp_dir)
    +
    +	blank_config := config.GetDefaultConfig()
    +	blank_config.Datastore.Location = temp_dir
    +	blank_config.Datastore.FilestoreDirectory = temp_dir
     
     	ctx, cancel := install_sig_handler()
     	defer cancel()
     
    -	sm, err := startup.StartToolServices(ctx, config_obj)
    -	defer sm.Close()
    -
    +	sm, err := startup.StartToolServices(ctx, blank_config)
     	if err != nil {
     		return err
     	}
    +	defer sm.Close()
     
     	if *client_rpm_command_binary == "" {
     		*client_rpm_command_binary, err = os.Executable()
    @@ -94,7 +108,8 @@ func doClientRPM() error {
     		Env: ordereddict.NewDict().
     			Set("Release", *rpm_command_release).
     			Set("Output", *client_rpm_command_output).
    -			Set("BinaryToPackage", *client_rpm_command_binary),
    +			Set("BinaryToPackage", *client_rpm_command_binary).
    +			Set("ConfigPath", abs_config_path),
     	}
     
     	query := `
    @@ -103,10 +118,11 @@ func doClientRPM() error {
            SELECT OSPath
            FROM rpm_create(exe=BinaryToPackage,
                            directory_name=Output,
    +                       config=read_file(filename=ConfigPath, length=1000000),
                            release=Release)
     `
     
    -	err = runQueryWithEnv(query, builder, "json")
    +	err = runQueryWithEnv(ctx, query, builder, "json")
     	if err != nil {
     		return err
     	}
    @@ -120,20 +136,33 @@ func doServerRPM() error {
     	// package on the same system where the logs should go.
     	logging.DisableLogging()
     
    -	config_obj, err := makeDefaultConfigLoader().
    -		WithRequiredFrontend().LoadAndValidate()
    +	if *config_path == "" {
    +		return fmt.Errorf("A server config must be specified using the --config flag")
    +	}
    +
    +	abs_config_path, err := filepath.Abs(*config_path)
    +	if err != nil {
    +		return err
    +	}
    +
    +	temp_dir, err := tempfile.TempDir("debian")
     	if err != nil {
    -		return fmt.Errorf("Unable to load config file: %w", err)
    +		return err
     	}
    +	defer os.RemoveAll(temp_dir)
    +
    +	blank_config := config.GetDefaultConfig()
    +	blank_config.Datastore.Location = temp_dir
    +	blank_config.Datastore.FilestoreDirectory = temp_dir
    +
     	ctx, cancel := install_sig_handler()
     	defer cancel()
     
    -	sm, err := startup.StartToolServices(ctx, config_obj)
    -	defer sm.Close()
    -
    +	sm, err := startup.StartToolServices(ctx, blank_config)
     	if err != nil {
     		return err
     	}
    +	defer sm.Close()
     
     	if *server_rpm_command_binary == "" {
     		*server_rpm_command_binary, err = os.Executable()
    @@ -165,7 +194,8 @@ func doServerRPM() error {
     		Env: ordereddict.NewDict().
     			Set("Release", *rpm_command_release).
     			Set("Output", *server_rpm_command_output).
    -			Set("BinaryToPackage", *server_rpm_command_binary),
    +			Set("BinaryToPackage", *server_rpm_command_binary).
    +			Set("ConfigPath", abs_config_path),
     	}
     
     	query := `
    @@ -174,10 +204,11 @@ func doServerRPM() error {
            SELECT OSPath
            FROM rpm_create(exe=BinaryToPackage, server=TRUE,
                            directory_name=Output,
    +                       config=read_file(filename=ConfigPath, length=1000000),
                            release=Release)
     `
     
    -	err = runQueryWithEnv(query, builder, "json")
    +	err = runQueryWithEnv(ctx, query, builder, "json")
     	if err != nil {
     		return err
     	}
    
  • bin/unzip.go+18 13 modified
    @@ -1,6 +1,7 @@
     package main
     
     import (
    +	"context"
     	"fmt"
     	"log"
     	"os"
    @@ -86,13 +87,13 @@ func doUnzip() error {
     	}
     
     	if *unzip_cmd_list {
    -		err = runUnzipList(builder)
    +		err = runUnzipList(ctx, builder)
     
     	} else if *unzip_cmd_print {
    -		err = runUnzipPrint(builder)
    +		err = runUnzipPrint(ctx, builder)
     
     	} else {
    -		err = runUnzipFiles(builder)
    +		err = runUnzipFiles(ctx, builder)
     	}
     	if err != nil {
     		return err
    @@ -101,7 +102,9 @@ func doUnzip() error {
     	return logger.Error
     }
     
    -func runUnzipList(builder services.ScopeBuilder) error {
    +func runUnzipList(
    +	ctx context.Context,
    +	builder services.ScopeBuilder) error {
     	query := `
            SELECT OSPath.Path AS Filename,
                   Size
    @@ -114,10 +117,12 @@ func runUnzipList(builder services.ScopeBuilder) error {
     		query += " AND " + *unzip_cmd_filter
     	}
     
    -	return runQueryWithEnv(query, builder, *unzip_format)
    +	return runQueryWithEnv(ctx, query, builder, *unzip_format)
     }
     
    -func runUnzipFiles(builder services.ScopeBuilder) error {
    +func runUnzipFiles(
    +	ctx context.Context,
    +	builder services.ScopeBuilder) error {
     	builder.Uploader = &uploads.FileBasedUploader{
     		UploadDir: *unzip_path,
     	}
    @@ -135,10 +140,12 @@ func runUnzipFiles(builder services.ScopeBuilder) error {
     		query += " AND " + *unzip_cmd_filter
     	}
     
    -	return runQueryWithEnv(query, builder, *unzip_format)
    +	return runQueryWithEnv(ctx, query, builder, *unzip_format)
     }
     
    -func runUnzipPrint(builder services.ScopeBuilder) error {
    +func runUnzipPrint(
    +	ctx context.Context,
    +	builder services.ScopeBuilder) error {
     	query := `
            SELECT * FROM foreach(
            row={
    @@ -157,11 +164,12 @@ func runUnzipPrint(builder services.ScopeBuilder) error {
     		query += " WHERE " + *unzip_cmd_filter
     	}
     
    -	return runQueryWithEnv(query, builder, *unzip_format)
    +	return runQueryWithEnv(ctx, query, builder, *unzip_format)
     }
     
     func runQueryWithEnv(
    -	query string, builder services.ScopeBuilder, format string) error {
    +	ctx context.Context, query string,
    +	builder services.ScopeBuilder, format string) error {
     	manager, err := services.GetRepositoryManager(builder.Config)
     	if err != nil {
     		return err
    @@ -175,9 +183,6 @@ func runQueryWithEnv(
     		return fmt.Errorf("Unable to parse VQL Query: %w", err)
     	}
     
    -	ctx, cancel := InstallSignalHandler(nil, scope)
    -	defer cancel()
    -
     	for _, vql := range vqls {
     		scope.Log("Running query %v", vfilter.FormatToString(scope, vql))
     
    
  • constants/constants.go+1 1 modified
    @@ -23,7 +23,7 @@ import (
     )
     
     const (
    -	VERSION = "0.76.5"
    +	VERSION = "0.76.6"
     
     	// This is the version of dependent client binaries that will be
     	// included in the offline collector or MSI. Usually this will be
    
  • datastore/remote.go+85 50 modified
    @@ -23,7 +23,7 @@ import (
     
     var (
     	remote_mu             sync.Mutex
    -	remote_datastopre_imp = NewRemoteDataStore(context.Background())
    +	remote_datastopre_imp *RemoteDataStore
     	RPC_BACKOFF           = 10.0
     	RPC_RETRY             = 10
     	timeoutError          = errors.New("gRPC Timeout in Remote datastore")
    @@ -38,46 +38,54 @@ func RPCTimeout(config_obj *config_proto.Config) time.Duration {
     }
     
     func Retry(ctx context.Context,
    -	config_obj *config_proto.Config, cb func() error) error {
    +	config_obj *config_proto.Config,
    +	failure_cb func(err error),
    +	cb func() error) error {
     	var err error
     
    +	logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
    +
     	for i := 0; i < RPC_RETRY; i++ {
     		err = cb()
     		if err == nil {
     			return nil
     		}
     
    -		// Figure out if the error is retryable - only some errors
    -		// mean a retry is appropriate (see
    -		// https://pkg.go.dev/google.golang.org/grpc/codes)
    -		st, ok := status.FromError(err)
    -		if !ok {
    +		if !isErrorRetriable(err) {
    +			failure_cb(err)
     			return err
     		}
     
    -		switch st.Code() {
    +		logger.Error("While connecting to remote datastore (retry %v): %v", i, err)
    +		select {
    +		case <-ctx.Done():
    +			return timeoutError
     
    -		// These ones are retryable errors - sleep a bit and retry
    -		// again.
    -		case codes.DeadlineExceeded, codes.ResourceExhausted,
    -			codes.Aborted, codes.Unavailable:
    -			logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
    -			logger.Error("While connecting to remote datastore: %v", err)
    -			select {
    -			case <-ctx.Done():
    -				return timeoutError
    +		case <-time.After(time.Duration(RPC_BACKOFF) * time.Second):
    +		}
    +	}
    +	return err
    +}
     
    -			case <-time.After(time.Duration(RPC_BACKOFF) * time.Second):
    -			}
    +func isErrorRetriable(err error) bool {
     
    -		case codes.Internal, codes.Unknown:
    -			return err
    +	// Figure out if the error is retryable - only some errors
    +	// mean a retry is appropriate (see
    +	// https://pkg.go.dev/google.golang.org/grpc/codes)
    +	st, ok := status.FromError(err)
    +	if !ok {
    +		return false
    +	}
     
    -		default:
    -			return err
    -		}
    +	switch st.Code() {
    +	case codes.ResourceExhausted:
    +		return false
    +
    +	case codes.DeadlineExceeded, codes.Aborted, codes.Unavailable:
    +		return true
    +	default:
    +		return false
     	}
    -	return err
     }
     
     type RemoteDataStore struct {
    @@ -88,9 +96,14 @@ func (self *RemoteDataStore) GetSubject(
     	config_obj *config_proto.Config,
     	urn api.DSPathSpec,
     	message proto.Message) error {
    -	return Retry(self.ctx, config_obj, func() error {
    -		return self._GetSubject(config_obj, urn, message)
    -	})
    +	return Retry(self.ctx, config_obj,
    +		func(err error) {
    +			logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
    +			logger.Error("RemoteDataStore: GetSubject %s: %v", urn.String(), err)
    +		},
    +		func() error {
    +			return self._GetSubject(config_obj, urn, message)
    +		})
     }
     
     func (self *RemoteDataStore) Healthy() error {
    @@ -105,7 +118,7 @@ func (self *RemoteDataStore) _GetSubject(
     	defer Instrument("read", "RemoteDataStore", urn)()
     
     	ctx, cancel := utils.WithTimeoutCause(
    -		context.Background(), RPCTimeout(config_obj), timeoutError)
    +		self.ctx, RPCTimeout(config_obj), timeoutError)
     	defer cancel()
     
     	// Make the call as the superuser
    @@ -176,10 +189,15 @@ func (self *RemoteDataStore) SetSubjectWithCompletion(
     		}
     	}()
     
    -	return Retry(self.ctx, config_obj, func() error {
    -		return self._SetSubjectWithCompletion(
    -			config_obj, urn, message, completion)
    -	})
    +	return Retry(self.ctx, config_obj,
    +		func(err error) {
    +			logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
    +			logger.Error("RemoteDataStore: GetSubject %s: %v", urn.String(), err)
    +		},
    +		func() error {
    +			return self._SetSubjectWithCompletion(
    +				config_obj, urn, message, completion)
    +		})
     }
     
     func (self *RemoteDataStore) _SetSubjectWithCompletion(
    @@ -206,7 +224,7 @@ func (self *RemoteDataStore) _SetSubjectWithCompletion(
     	}
     
     	ctx, cancel := utils.WithTimeoutCause(
    -		context.Background(), RPCTimeout(config_obj), timeoutError)
    +		self.ctx, RPCTimeout(config_obj), timeoutError)
     	defer cancel()
     
     	// Make the call as the superuser
    @@ -238,9 +256,14 @@ func (self *RemoteDataStore) _SetSubjectWithCompletion(
     func (self *RemoteDataStore) DeleteSubjectWithCompletion(
     	config_obj *config_proto.Config,
     	urn api.DSPathSpec, completion func()) error {
    -	return Retry(self.ctx, config_obj, func() error {
    -		return self._DeleteSubjectWithCompletion(config_obj, urn, completion)
    -	})
    +	return Retry(self.ctx, config_obj,
    +		func(err error) {
    +			logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
    +			logger.Error("RemoteDataStore: DeleteSubjectWithCompletion %s: %v", urn.String(), err)
    +		},
    +		func() error {
    +			return self._DeleteSubjectWithCompletion(config_obj, urn, completion)
    +		})
     }
     
     func (self *RemoteDataStore) _DeleteSubjectWithCompletion(
    @@ -250,7 +273,7 @@ func (self *RemoteDataStore) _DeleteSubjectWithCompletion(
     	defer Instrument("delete", "RemoteDataStore", urn)()
     
     	ctx, cancel := utils.WithTimeoutCause(
    -		context.Background(), RPCTimeout(config_obj), timeoutError)
    +		self.ctx, RPCTimeout(config_obj), timeoutError)
     	defer cancel()
     
     	conn, closer, err := grpc_client.Factory.GetAPIClient(
    @@ -285,9 +308,14 @@ func (self *RemoteDataStore) _DeleteSubjectWithCompletion(
     func (self *RemoteDataStore) DeleteSubject(
     	config_obj *config_proto.Config,
     	urn api.DSPathSpec) error {
    -	return Retry(self.ctx, config_obj, func() error {
    -		return self._DeleteSubject(config_obj, urn)
    -	})
    +	return Retry(self.ctx, config_obj,
    +		func(err error) {
    +			logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
    +			logger.Error("RemoteDataStore: DeleteSubject %s: %v", urn.String(), err)
    +		},
    +		func() error {
    +			return self._DeleteSubject(config_obj, urn)
    +		})
     }
     
     func (self *RemoteDataStore) _DeleteSubject(
    @@ -297,7 +325,7 @@ func (self *RemoteDataStore) _DeleteSubject(
     	defer Instrument("delete", "RemoteDataStore", urn)()
     
     	ctx, cancel := utils.WithTimeoutCause(
    -		context.Background(), RPCTimeout(config_obj), timeoutError)
    +		self.ctx, RPCTimeout(config_obj), timeoutError)
     	defer cancel()
     
     	conn, closer, err := grpc_client.Factory.GetAPIClient(
    @@ -330,10 +358,15 @@ func (self *RemoteDataStore) ListChildren(
     	var result []api.DSPathSpec
     	var err error
     
    -	err = Retry(self.ctx, config_obj, func() error {
    -		result, err = self._ListChildren(config_obj, urn)
    -		return err
    -	})
    +	err = Retry(self.ctx, config_obj,
    +		func(err error) {
    +			logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
    +			logger.Error("RemoteDataStore: ListChildren %s: %v", urn.String(), err)
    +		},
    +		func() error {
    +			result, err = self._ListChildren(config_obj, urn)
    +			return err
    +		})
     
     	return result, err
     }
    @@ -346,7 +379,7 @@ func (self *RemoteDataStore) _ListChildren(
     	defer Instrument("list", "RemoteDataStore", urn)()
     
     	ctx, cancel := utils.WithTimeoutCause(
    -		context.Background(), RPCTimeout(config_obj), timeoutError)
    +		self.ctx, RPCTimeout(config_obj), timeoutError)
     	defer cancel()
     
     	conn, closer, err := grpc_client.Factory.GetAPIClient(
    @@ -404,19 +437,21 @@ func StartDatastore(
     	implementation, err := GetImplementationName(config_obj)
     	if err != nil {
     		// Invalid datastore configuration is not an issue here - it
    -		// just means we dont want to use the remote datastore.
    +		// just means we don't want to use the remote datastore.
     		return nil
     	}
     
    -	if implementation == "RemoteFileDataStore" {
    +	switch implementation {
    +	// These datastores require starting the remote connection.
    +	case "RemoteFileDataStore", "MemcacheFileDataStore":
     		logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
     		logger.Info("<green>Starting</> remote datastore service")
     		remote_mu.Lock()
     		remote_datastopre_imp = NewRemoteDataStore(ctx)
     		g_impl = nil
     		remote_mu.Unlock()
     
    -	} else if implementation == "FileBaseDataStore" {
    +	case "FileBaseDataStore":
     		return startFullDiskChecker(ctx, wg, config_obj)
     	}
     	return nil
    
  • executor/executor_test.go+4 4 modified
    @@ -156,7 +156,7 @@ func (self *ExecutorTestSuite) TestCancellation() {
     	assert.Equal(self.T(), crypto_proto.VeloStatus_GENERIC_ERROR,
     		stats.FlowStats.QueryStatus[0].Status)
     	assert.Contains(self.T(), getLogMessages(received_messages),
    -		"Cancelled all inflight queries")
    +		"Cancelled all in-flight queries")
     }
     
     // Exceeding flow upload limit will cancel the flow
    @@ -226,7 +226,7 @@ func (self *ExecutorTestSuite) TestUploadCancellation() {
     		"Upload bytes 37 exceeded limit 10 for flow")
     
     	assert.Contains(self.T(), getLogMessages(received_messages),
    -		"Cancelled all inflight queries")
    +		"Cancelled all in-flight queries")
     }
     
     // Exceeding row limit will cancel flow
    @@ -295,7 +295,7 @@ func (self *ExecutorTestSuite) TestRowLimitCancellation() {
     	assert.Contains(self.T(), stats.FlowStats.QueryStatus[0].ErrorMessage,
     		"Rows 11 exceeded limit 10 for flow")
     	assert.Contains(self.T(), getLogMessages(received_messages),
    -		"Cancelled all inflight queries")
    +		"Cancelled all in-flight queries")
     }
     
     // Test the total result row count is accurate
    @@ -635,7 +635,7 @@ func (self *ExecutorTestSuite) TestFlowStatsRequest() {
     	vtesting.WaitUntil(2*time.Second, self.T(), func() bool {
     		// Should be two messages - first is status of the real flow,
     		// second is a "Flow not known - maybe the client crashed?"
    -		// message for the not existant flow we dont know about. This
    +		// message for the non-existent flow we don't know about. This
     		// should cause the server to error out that outstanding flow.
     		return len(collector.Messages()) == 2
     	})
    
  • file_store/directory/reader.go+7 1 modified
    @@ -6,6 +6,7 @@ import (
     	"io"
     	"os"
     
    +	"www.velocidex.com/golang/velociraptor/constants"
     	"www.velocidex.com/golang/velociraptor/file_store/api"
     	"www.velocidex.com/golang/velociraptor/third_party/cache"
     	"www.velocidex.com/golang/velociraptor/utils"
    @@ -39,8 +40,13 @@ func (self *CompressedDirectoryReader) uncompressChunk(
     		return nil, io.EOF
     	}
     
    +	uncompressed_length := chunk.UncompressedLength
    +	if uncompressed_length > constants.MAX_MEMORY {
    +		uncompressed_length = constants.MAX_MEMORY
    +	}
    +
     	uncompressed, err := utils.UncompressWithLimit(
    -		context.Background(), compressed, chunk.UncompressedLength)
    +		context.Background(), compressed, uncompressed_length)
     	if err != nil {
     		return nil, io.EOF
     	}
    
  • file_store/memory/compressed.go+7 1 modified
    @@ -5,6 +5,7 @@ import (
     	"io"
     	"os"
     
    +	"www.velocidex.com/golang/velociraptor/constants"
     	"www.velocidex.com/golang/velociraptor/file_store/api"
     	"www.velocidex.com/golang/velociraptor/utils"
     )
    @@ -32,8 +33,13 @@ func (self *CompressedMemoryReader) readPartial(buf []byte) (int, error) {
     		return 0, io.EOF
     	}
     
    +	uncompressed_length := chunk.UncompressedLength
    +	if uncompressed_length > constants.MAX_MEMORY {
    +		uncompressed_length = constants.MAX_MEMORY
    +	}
    +
     	uncompressed, err := utils.UncompressWithLimit(
    -		context.Background(), compressed, chunk.UncompressedLength)
    +		context.Background(), compressed, uncompressed_length)
     	if err != nil {
     		return 0, io.EOF
     	}
    
  • file_store/memory/writer.go+2 1 modified
    @@ -3,6 +3,7 @@ package memory
     import (
     	"bytes"
     	"encoding/binary"
    +	"fmt"
     
     	"www.velocidex.com/golang/velociraptor/constants"
     	"www.velocidex.com/golang/velociraptor/file_store/api"
    @@ -136,7 +137,7 @@ func (self *MemoryWriter) _Flush() error {
     
     func (self *MemoryWriter) Close() error {
     	if self.closed {
    -		// panic("MemoryWriter already closed")
    +		return fmt.Errorf("MemoryWriter already closed")
     	}
     	self.closed = true
     
    
  • file_store/path_specs/utils.go+0 5 modified
    @@ -102,11 +102,6 @@ func getTypeFromComponents(components []string) ([]string, api.PathType) {
     		return components, api.PATH_TYPE_FILESTORE_ANY
     	}
     
    -	// Client uploads are all untyped
    -	if len(components) > 4 && components[0] == "clients" {
    -		return components, api.PATH_TYPE_FILESTORE_ANY
    -	}
    -
     	last_component := components[len(components)-1]
     
     	// Fallback, use the extension to deduce the type.
    
  • file_store/path_specs/utils_test.go+25 0 modified
    @@ -44,6 +44,31 @@ func TestFromGenericComponentList(t *testing.T) {
     			client_path: "/notebooks/N.D55OJV2COB544/NC.D56I6S6LK00FI-D56I71V0BJULE/uploads/data/file.zip",
     			path_type:   api.PATH_TYPE_FILESTORE_ANY,
     		},
    +		{
    +			// Client collection uploads
    +			components: []string{
    +				"clients", "C.d7f8859f5e0e01f7", "collections", "F.D55T34A0NIDTC",
    +				"uploads", "data", "file.json"},
    +			client_path: "/clients/C.d7f8859f5e0e01f7/collections/F.D55T34A0NIDTC/uploads/data/file.json",
    +			path_type:   api.PATH_TYPE_FILESTORE_ANY,
    +		},
    +		{
    +			// Client collection jsonl logs
    +			components: []string{
    +				"clients", "C.d7f8859f5e0e01f7",
    +				"collections", "F.D55T34A0NIDTC", "logs.json"},
    +			client_path: "/clients/C.d7f8859f5e0e01f7/collections/F.D55T34A0NIDTC/logs.json",
    +			path_type:   api.PATH_TYPE_FILESTORE_JSON,
    +		},
    +		{
    +			// Client collection jsonl logs
    +			components: []string{
    +				"clients", "C.d7f8859f5e0e01f7",
    +				"artifacts", "System.VFS.ListDirectory",
    +				"F.D55T34A0NIDTC", "Stats.json"},
    +			client_path: "/clients/C.d7f8859f5e0e01f7/artifacts/System.VFS.ListDirectory/F.D55T34A0NIDTC/Stats.json",
    +			path_type:   api.PATH_TYPE_FILESTORE_JSON,
    +		},
     		{
     			// Client notebook attachments are always PATH_TYPE_FILESTORE_ANY
     			components: []string{
    
  • flows/artifacts.go+4 5 modified
    @@ -93,6 +93,8 @@ type CollectionContext struct {
     	// only happens once the collection is complete and results are
     	// written. It only happens at most once per collection.
     	send_update bool
    +
    +	closer func()
     }
     
     func NewCollectionContext(
    @@ -105,7 +107,7 @@ func NewCollectionContext(
     	// If we need to send a notification we should wait until all parts of
     	// the collection are fully stored first to avoid a race with any
     	// listeners on System.Flow.Completion.
    -	self.completer = utils.NewCompleter(func() {
    +	self.completer, self.closer = utils.NewCompleter(func() {
     		self.mu.Lock()
     		defer self.mu.Unlock()
     
    @@ -181,10 +183,7 @@ func closeContext(
     	config_obj *config_proto.Config,
     	collection_context *CollectionContext) error {
     
    -	// Ensure the completion is not fired until we are done here
    -	// completely.
    -	completion_func := collection_context.completer.GetCompletionFunc()
    -	defer completion_func()
    +	defer collection_context.closer()
     
     	// Context is not dirty - nothing to do.
     	if !collection_context.Dirty || collection_context.ClientId == "" {
    
  • flows/client_flow_runner.go+1 2 modified
    @@ -67,8 +67,7 @@ func NewFlowRunner(
     	}
     
     	// Wait for completion until Close() is called.
    -	result.completer = utils.NewCompleter(result.Complete)
    -	result.closer = result.completer.GetCompletionFunc()
    +	result.completer, result.closer = utils.NewCompleter(result.Complete)
     	return result
     }
     
    
  • .github/workflows/go.yml+4 5 modified
    @@ -18,9 +18,10 @@ jobs:
         steps:
     
         - name: Check out code into the Go module directory
    -      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
    +      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
     
    -    - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
    +    - name: Set up Go 1.25
    +      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
           with:
             go-version: '^1.25'
     
    @@ -38,9 +39,7 @@ jobs:
             sudo apt-get install mingw-w64-x86-64-dev gcc-mingw-w64-x86-64 gcc-mingw-w64
     
         - name: Use Node.js
    -      uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e
    -      with:
    -        node-version: 22.19
    +      uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
     
         - name: npm install gui
           run: |
    
  • .github/workflows/musl.yaml+4 5 modified
    @@ -18,9 +18,10 @@ jobs:
         steps:
     
         - name: Check out code into the Go module directory
    -      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
    +      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
     
    -    - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
    +    - name: Set up Go 1.25
    +      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
           with:
             go-version: '^1.25'
     
    @@ -42,9 +43,7 @@ jobs:
             cd ..
     
         - name: Use Node.js
    -      uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e
    -      with:
    -        node-version: 20
    +      uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
     
         - name: npm install gui
           run: |
    
  • .github/workflows/windows.yml+3 3 modified
    @@ -9,12 +9,12 @@ jobs:
         runs-on: windows-2022
         steps:
         - name: Check out code into the Go module directory
    -      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
    +      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
           with:
             fetch-depth: 50
     
         - name: Set up Go 1.25
    -      uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
    +      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
           with:
             go-version: 1.25
     
    @@ -127,7 +127,7 @@ jobs:
             cp artifacts/testdata/server/testcases/*.out* artifact_output/server/
     
         - name: StoreBinaries
    -      uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1
    +      uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
           if: always()
           with:
             name: artifact
    
  • go.mod+30 27 modified
    @@ -8,9 +8,9 @@ require (
     	github.com/Showmax/go-fqdn v1.0.0
     	github.com/Velocidex/amsi v0.0.0-20250418124629-ea341d1aa3f2
     	github.com/Velocidex/etw v0.0.0-20251027041548-6d97883fd588
    -	github.com/Velocidex/go-magic v0.0.0-20250203094020-32f94b14f00f
    +	github.com/Velocidex/go-magic v0.0.0-20260507053215-ee4782958ce2
     	github.com/Velocidex/go-yara v1.1.10-0.20250823152352-e5fc0843e50e
    -	github.com/Velocidex/grpc-go-pool v1.2.2-0.20241016164850-ff0cb80037a8
    +	github.com/Velocidex/grpc-go-pool v1.2.2-0.20260526092838-b4e236b2105f
     	github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a
     	github.com/Velocidex/pkcs7 v0.0.0-20230220112103-d4ed02e1862a
     	github.com/Velocidex/sflags v0.3.1-0.20241126160332-cc1a5b66b8f1
    @@ -44,17 +44,17 @@ require (
     	github.com/jonboulle/clockwork v0.5.0 // indirect
     	github.com/juju/ratelimit v1.0.1
     	github.com/lib/pq v1.10.9
    -	github.com/magefile/mage v1.17.0
    +	github.com/magefile/mage v1.17.2
     	github.com/mattn/go-isatty v0.0.20
     	github.com/mattn/go-pointer v0.0.0-20180825124634-49522c3f3791
    -	github.com/mattn/go-sqlite3 v1.14.37
    +	github.com/mattn/go-sqlite3 v1.14.44
     	github.com/microcosm-cc/bluemonday v1.0.23
     	github.com/mitchellh/copystructure v1.2.0 // indirect
     	github.com/mitchellh/panicwrap v1.0.0
     	github.com/olekukonko/tablewriter v0.0.5
     	github.com/oschwald/maxminddb-golang v1.8.0
     	github.com/pkg/sftp v1.13.6
    -	github.com/prometheus/client_golang v1.15.1
    +	github.com/prometheus/client_golang v1.23.2
     	github.com/prometheus/client_model v0.6.2
     	github.com/qri-io/starlib v0.5.0
     	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
    @@ -67,29 +67,29 @@ require (
     	github.com/xor-gate/ar v0.0.0-20170530204233-5c72ae81e2b7 // indirect
     	github.com/xor-gate/debpkg v1.0.0
     	go.starlark.net v0.0.0-20230925163745-10651d5192ab
    -	golang.org/x/crypto v0.48.0
    -	golang.org/x/mod v0.34.0
    -	golang.org/x/net v0.51.0
    -	golang.org/x/sys v0.43.0
    -	golang.org/x/text v0.36.0
    +	golang.org/x/crypto v0.51.0
    +	golang.org/x/mod v0.35.0
    +	golang.org/x/net v0.55.0
    +	golang.org/x/sys v0.45.0
    +	golang.org/x/text v0.37.0
     	golang.org/x/time v0.14.0
     	google.golang.org/api v0.258.0
     	google.golang.org/genproto v0.0.0-20251222181119-0a764e51fe1b // indirect
    -	google.golang.org/grpc v1.79.3
    +	google.golang.org/grpc v1.81.1
     	google.golang.org/protobuf v1.36.11
     	gopkg.in/alecthomas/kingpin.v2 v2.2.6
     	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
     	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
     	gopkg.in/sourcemap.v1 v1.0.5 // indirect
     	howett.net/plist v1.0.0
     	www.velocidex.com/golang/evtx v0.2.1-0.20260427154223-06ab70045154
    -	www.velocidex.com/golang/go-ese v0.2.1-0.20260321072931-b8793d15e573
    +	www.velocidex.com/golang/go-ese v0.2.1-0.20260525143345-56639b015799
     	www.velocidex.com/golang/go-ntfs v0.2.1-0.20260331025433-9cce38e8a396
     	www.velocidex.com/golang/go-pe v0.1.1-0.20251107001057-f93001158cd9
     	www.velocidex.com/golang/go-prefetch v0.0.0-20251027080408-85407689d0cb
     	www.velocidex.com/golang/oleparse v0.0.0-20250312121321-f7c2b4ec0959
     	www.velocidex.com/golang/regparser v0.0.0-20250203141505-31e704a67ef7
    -	www.velocidex.com/golang/vfilter v0.0.0-20260325003227-469fc60d0e45
    +	www.velocidex.com/golang/vfilter v0.0.0-20260513155414-08c929c15ddb
     )
     
     require (
    @@ -156,8 +156,8 @@ require (
     	github.com/valyala/fastjson v1.6.4
     	github.com/vincent-petithory/dataurl v1.0.0
     	github.com/virtuald/go-paniclog v0.0.0-20190812204905-43a7fa316459
    -	golang.org/x/oauth2 v0.34.0
    -	google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b
    +	golang.org/x/oauth2 v0.36.0
    +	google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171
     	google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
     	gopkg.in/yaml.v2 v2.4.0
     	gopkg.in/yaml.v3 v3.0.1
    @@ -175,10 +175,11 @@ require (
     	cloud.google.com/go/monitoring v1.24.3 // indirect
     	cloud.google.com/go/pubsub/v2 v2.3.0 // indirect
     	github.com/360EntSecGroup-Skylar/excelize v1.4.1 // indirect
    -	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 // indirect
    -	github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
    +	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 // indirect
    +	github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect
    +	github.com/AzureAD/microsoft-authentication-library-for-go v1.7.1 // indirect
     	github.com/BurntSushi/toml v1.5.0 // indirect
    -	github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
    +	github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
     	github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 // indirect
     	github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 // indirect
     	github.com/PuerkitoBio/goquery v1.8.1 // indirect
    @@ -237,7 +238,7 @@ require (
     	github.com/cilium/ebpf v0.20.1-0.20251215101449-df5c3096bd8c // indirect
     	github.com/clipperhouse/stringish v0.1.1 // indirect
     	github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
    -	github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
    +	github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
     	github.com/crewjam/httperr v0.2.0 // indirect
     	github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
     	github.com/dlclark/regexp2 v1.7.0 // indirect
    @@ -246,8 +247,8 @@ require (
     	github.com/elastic/elastic-transport-go/v8 v8.9.1-0.20260224212447-513c84c73cc8 // indirect
     	github.com/emersion/go-message v0.16.0 // indirect
     	github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect
    -	github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
    -	github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
    +	github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect
    +	github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
     	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
     	github.com/felixge/httpsnoop v1.0.4 // indirect
     	github.com/geoffgarside/ber v1.1.0 // indirect
    @@ -281,6 +282,7 @@ require (
     	github.com/klauspost/crc32 v1.3.0 // indirect
     	github.com/klauspost/pgzip v1.2.6 // indirect
     	github.com/kr/fs v0.1.0 // indirect
    +	github.com/kylelemons/godebug v1.1.0 // indirect
     	github.com/labstack/echo v3.3.10+incompatible // indirect
     	github.com/labstack/gommon v0.4.2 // indirect
     	github.com/lestrrat-go/strftime v1.0.5 // indirect
    @@ -290,7 +292,6 @@ require (
     	github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
     	github.com/mattn/go-localereader v0.0.1 // indirect
     	github.com/mattn/go-runewidth v0.0.19 // indirect
    -	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
     	github.com/minio/crc64nvme v1.1.1 // indirect
     	github.com/minio/md5-simd v1.1.2 // indirect
     	github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
    @@ -302,14 +303,15 @@ require (
     	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
     	github.com/muesli/cancelreader v0.2.2 // indirect
     	github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect
    +	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
     	github.com/nsf/termbox-go v1.1.1 // indirect
     	github.com/paulmach/orb v0.10.0 // indirect
     	github.com/philhofer/fwd v1.2.0 // indirect
     	github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
     	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
     	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
    -	github.com/prometheus/common v0.44.0 // indirect
    -	github.com/prometheus/procfs v0.9.0 // indirect
    +	github.com/prometheus/common v0.67.5 // indirect
    +	github.com/prometheus/procfs v0.20.1 // indirect
     	github.com/rivo/uniseg v0.4.7 // indirect
     	github.com/rotisserie/eris v0.5.4 // indirect
     	github.com/rs/xid v1.6.0 // indirect
    @@ -329,7 +331,7 @@ require (
     	go.mongodb.org/mongo-driver v1.12.1 // indirect
     	go.opencensus.io v0.24.0 // indirect
     	go.opentelemetry.io/auto/sdk v1.2.1 // indirect
    -	go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
    +	go.opentelemetry.io/contrib/detectors/gcp v1.42.0 // indirect
     	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect
     	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect
     	go.opentelemetry.io/otel v1.43.0 // indirect
    @@ -339,12 +341,13 @@ require (
     	go.opentelemetry.io/otel/trace v1.43.0 // indirect
     	go.uber.org/multierr v1.11.0 // indirect
     	go.uber.org/zap v1.27.0 // indirect
    +	go.yaml.in/yaml/v2 v2.4.4 // indirect
     	go.yaml.in/yaml/v3 v3.0.4 // indirect
     	golang.org/x/arch v0.23.0 // indirect
     	golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect
     	golang.org/x/sync v0.20.0 // indirect
    -	golang.org/x/term v0.40.0 // indirect
    -	google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
    +	golang.org/x/term v0.43.0 // indirect
    +	google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 // indirect
     	kernel.org/pub/linux/libs/security/libcap/cap v1.2.71 // indirect
     	kernel.org/pub/linux/libs/security/libcap/psx v1.2.71 // indirect
     	www.velocidex.com/golang/binparsergen v0.1.1-0.20240404114946-8f66c7cf586e // indirect
    
  • go.sum+64 63 modified
    @@ -30,26 +30,26 @@ cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U
     cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=
     github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
     github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
    -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo=
    -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
    -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
    -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
    -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
    -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
    +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 h1:jHb/wfvRikGdxMXYV3QG/SzUOPYN9KEUUuC0Yd0/vC0=
    +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1/go.mod h1:pzBXCYn05zvYIrwLgtK8Ap8QcjRg+0i76tMQdWN6wOk=
    +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
    +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
    +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4=
    +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY=
     github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
     github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
     github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 h1:UXT0o77lXQrikd1kgwIPQOUect7EoR/+sbP4wQKdzxM=
     github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0/go.mod h1:cTvi54pg19DoT07ekoeMgE/taAwNtCShVeZqA+Iv2xI=
    -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
    -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
    +github.com/AzureAD/microsoft-authentication-library-for-go v1.7.1 h1:edShSHV3DV90+kt+CMaEXEzR9QF7wFrPJxVGz2blMIU=
    +github.com/AzureAD/microsoft-authentication-library-for-go v1.7.1/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
     github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
     github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
     github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
     github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
     github.com/Depado/bfchroma v1.3.0 h1:zz14vpvySU6S0CL6yGPr1vkFevQecIt8dJdCsMS2JpM=
     github.com/Depado/bfchroma v1.3.0/go.mod h1:c0bFk0tFmT+clD3TIGurjWCfD/QV8/EebfM3JGr+98M=
    -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
    -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
    +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
    +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
     github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 h1:lhhYARPUu3LmHysQ/igznQphfzynnqI3D75oUyw1HXk=
     github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0/go.mod h1:l9rva3ApbBpEJxSNYnwT9N4CDLrWgtq3u8736C5hyJw=
     github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0 h1:xfK3bbi6F2RDtaZFtUdKO3osOBIhNb+xTs8lFW6yx9o=
    @@ -103,8 +103,8 @@ github.com/Velocidex/go-fat v0.0.0-20260301005230-08fb790838a9 h1:Lc/fU68mtM09XP
     github.com/Velocidex/go-fat v0.0.0-20260301005230-08fb790838a9/go.mod h1:eIgJ3KnRtnoO6lXOfZBina/KGAoyUBFUYjy+iZGVvkc=
     github.com/Velocidex/go-journalctl v0.0.0-20250902002606-881a5f66df10 h1:AGL0nv7O7LoRqdNwx1IZNmcacgC9W+ItMPJL4Vhy2gM=
     github.com/Velocidex/go-journalctl v0.0.0-20250902002606-881a5f66df10/go.mod h1:5WxXsCtLdEvnc4FsFa+QfMwOWYkfey3nlQbPssZWqjc=
    -github.com/Velocidex/go-magic v0.0.0-20250203094020-32f94b14f00f h1:KCDi0hKrkDrn0DI2L8cSMkrF0yWj57c6VIhAKmmQFV8=
    -github.com/Velocidex/go-magic v0.0.0-20250203094020-32f94b14f00f/go.mod h1:2oVfOYRdtA0yuSZiN9ai8PRgxvkw6SLUlUXy1Sm76qk=
    +github.com/Velocidex/go-magic v0.0.0-20260507053215-ee4782958ce2 h1:QTEYtH3TrfsmfZATXuS4xAWAv9Mm6G4FQ0957fNdEIw=
    +github.com/Velocidex/go-magic v0.0.0-20260507053215-ee4782958ce2/go.mod h1:2oVfOYRdtA0yuSZiN9ai8PRgxvkw6SLUlUXy1Sm76qk=
     github.com/Velocidex/go-mscfb v0.0.0-20240618091452-31f4ccc54002 h1:FWeeVb/x+XvaIKZyMdxwB+JYmj4dSATSuu+DBfCXFVU=
     github.com/Velocidex/go-mscfb v0.0.0-20240618091452-31f4ccc54002/go.mod h1:YvYAfyK6Jg2WIaqvK42KPmVDfU8FSVxoSiZSVJfihDo=
     github.com/Velocidex/go-vhdx v0.0.0-20250511013458-5cba970cdeda h1:Q0pdD/aAUCmTv3ndK+m0yxQU1WdBLKEGPHEs68+w+8I=
    @@ -115,8 +115,8 @@ github.com/Velocidex/go-yara v1.1.10-0.20250823152352-e5fc0843e50e h1:mF+iDZwtXL
     github.com/Velocidex/go-yara v1.1.10-0.20250823152352-e5fc0843e50e/go.mod h1:5KKwF74avzF4GPZg37NoWfJiDgrBF5ASHH27g1p83zk=
     github.com/Velocidex/grok v0.0.1 h1:bOaqowsW9ecfJISQOM9K47Jk/ID9V945BLfKngUZEjo=
     github.com/Velocidex/grok v0.0.1/go.mod h1:tO25ta1WAuru0QA9ajcK0bAsk0gpWiT8so+fR4u3GZE=
    -github.com/Velocidex/grpc-go-pool v1.2.2-0.20241016164850-ff0cb80037a8 h1:ezfYixd2bTfPGaLsSKl77aIje68WDgITm65bK0vF5Ak=
    -github.com/Velocidex/grpc-go-pool v1.2.2-0.20241016164850-ff0cb80037a8/go.mod h1:lyHshoS5LiGow9v99IvfFM8xmyOGoYKtjbcNQx20z4M=
    +github.com/Velocidex/grpc-go-pool v1.2.2-0.20260526092838-b4e236b2105f h1:nVc9Y69VrGkBVCDKYbrbf4gQYykRzArO6JUD3MOqKcE=
    +github.com/Velocidex/grpc-go-pool v1.2.2-0.20260526092838-b4e236b2105f/go.mod h1:Rcpbr2QTlawwUkhHKHX0kOKw1nA++gwuyxAwUDgXdZg=
     github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a h1:AeXPUzhU0yhID/v5JJEIkjaE85ASe+Vh4Kuv1RSLL+4=
     github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a/go.mod h1:ukJBuruT9b24pdgZwWDvOaCYHeS03B7oQPCUWh25bwM=
     github.com/Velocidex/ordereddict v0.0.0-20230909174157-2aa49cc5d11d/go.mod h1:+MqO5UMBemyFSm+yRXslbpFTwPUDhFHUf7HPV92twg4=
    @@ -309,8 +309,8 @@ github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEX
     github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
     github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
     github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
    -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
    -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
    +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
    +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
     github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
     github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
     github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
    @@ -351,13 +351,13 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
     github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
     github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
     github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
    -github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
    -github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
    +github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
    +github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
     github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
     github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
     github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
    -github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
    -github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
    +github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
    +github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
     github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
     github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
     github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
    @@ -403,8 +403,8 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
     github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
     github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
     github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
    -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
    -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
    +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
    +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
     github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg=
     github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4=
     github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
    @@ -598,8 +598,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
     github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
     github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
     github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
    -github.com/magefile/mage v1.17.0 h1:dS4tkq997Ism03akafC8509iqDjeE7TNTexI25Y7sXM=
    -github.com/magefile/mage v1.17.0/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA=
    +github.com/magefile/mage v1.17.2 h1:fyXVu1eadI8Ap1HCCNgEhJ5McIWiYhLR8uol64ZZc40=
    +github.com/magefile/mage v1.17.2/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA=
     github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
     github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
     github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
    @@ -625,10 +625,8 @@ github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC
     github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
     github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
     github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
    -github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg=
    -github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
    -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
    -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
    +github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8=
    +github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
     github.com/mccutchen/go-httpbin/v2 v2.18.3 h1:DyckIScjHLJtmlSju+rgjqqI1nL8AdMZHsLSljlbnMU=
     github.com/mccutchen/go-httpbin/v2 v2.18.3/go.mod h1:GBy5I7XwZ4ZLhT3hcq39I4ikwN9x4QUt6EAxNiR8Jus=
     github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
    @@ -677,6 +675,8 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
     github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
     github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg=
     github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ=
    +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
    +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
     github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
     github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
     github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY=
    @@ -715,15 +715,15 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
     github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
     github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
     github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
    -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
    -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
    +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
    +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
     github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
     github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
     github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
    -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
    -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
    -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
    -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
    +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
    +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
    +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
    +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
     github.com/qri-io/starlib v0.5.0 h1:NlveoBAhO6mNgM7+JpM9QlHh3/3pOtOiH6iXaqSdVK0=
     github.com/qri-io/starlib v0.5.0/go.mod h1:FpVumyB2CMrKIrjf39fAi4uydYWVvnWEvXEOwfzZRHY=
     github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
    @@ -845,8 +845,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
     go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
     go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
     go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
    -go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
    -go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
    +go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ=
    +go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8=
     go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk=
     go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70=
     go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=
    @@ -875,6 +875,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
     go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
     go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
     go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
    +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
    +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
     go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
     go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
     golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
    @@ -890,8 +892,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
     golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
     golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
     golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
    -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
    -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
    +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
    +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
     golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
     golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
     golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
    @@ -909,8 +911,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
     golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
     golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
     golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
    -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
    -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
    +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
    +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
     golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
     golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
     golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
    @@ -936,16 +938,15 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
     golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
     golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
     golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
    -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
    -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
    +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
    +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
     golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
     golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
    -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
    -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
    +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
    +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
     golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
     golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
     golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
    -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
     golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
     golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
     golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
    @@ -987,15 +988,15 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
     golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
     golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
     golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
    -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
    -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
    +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
    +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
     golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
     golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
     golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
     golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
     golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
    -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
    -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
    +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
    +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
     golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
     golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
     golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
    @@ -1006,8 +1007,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
     golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
     golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
     golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
    -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
    -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
    +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
    +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
     golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
     golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
     golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
    @@ -1033,8 +1034,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
     golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
     golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
     golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
    -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
    -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
    +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
    +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
     google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
     google.golang.org/api v0.258.0 h1:IKo1j5FBlN74fe5isA2PVozN3Y5pwNKriEgAXPOkDAc=
     google.golang.org/api v0.258.0/go.mod h1:qhOMTQEZ6lUps63ZNq9jhODswwjkjYYguA7fA3TBFww=
    @@ -1047,18 +1048,18 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
     google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
     google.golang.org/genproto v0.0.0-20251222181119-0a764e51fe1b h1:kqShdsddZrS6q+DGBCA73CzHsKDu5vW4qw78tFnbVvY=
     google.golang.org/genproto v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:gw1DtiPCt5uh/HV9STVEeaO00S5ATsJiJ2LsZV8lcDI=
    -google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E=
    -google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk=
    -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
    -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
    +google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4=
    +google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng=
    +google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 h1:PvEgGJf9C/1u5CHkInMg7UFYYUoiaQmW2LbtH0pjB78=
    +google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
     google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
     google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
     google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
     google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
     google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
     google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
    -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
    -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
    +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
    +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
     google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0=
     google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
     google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
    @@ -1121,8 +1122,8 @@ www.velocidex.com/golang/binparsergen v0.1.1-0.20240404114946-8f66c7cf586e h1:uf
     www.velocidex.com/golang/binparsergen v0.1.1-0.20240404114946-8f66c7cf586e/go.mod h1:jk+uZGukrJZWgnNH6q9tJLUnbugHEDPCQdIOmBBMXY4=
     www.velocidex.com/golang/evtx v0.2.1-0.20260427154223-06ab70045154 h1:DtXlY/onyKgj0YIUFpt7n93Huv2EwssRsT7HV1PSE/k=
     www.velocidex.com/golang/evtx v0.2.1-0.20260427154223-06ab70045154/go.mod h1:eqjy0jz9Q/dy7LOMBuXNPUilcm/pe/6GMOop6CvczkY=
    -www.velocidex.com/golang/go-ese v0.2.1-0.20260321072931-b8793d15e573 h1:xlvektWQLJGXwHO6BwE6CeZgr0F2XelQ3je2Y8LfbAY=
    -www.velocidex.com/golang/go-ese v0.2.1-0.20260321072931-b8793d15e573/go.mod h1:H1Gw62XkspXEtZPT2ep+L57GgdKIDHwueazxKnn5EXc=
    +www.velocidex.com/golang/go-ese v0.2.1-0.20260525143345-56639b015799 h1:HvU20MrEnMfY2ogFdYmM/6XM9MDi0ihsx57lm4W0uyw=
    +www.velocidex.com/golang/go-ese v0.2.1-0.20260525143345-56639b015799/go.mod h1:H1Gw62XkspXEtZPT2ep+L57GgdKIDHwueazxKnn5EXc=
     www.velocidex.com/golang/go-ntfs v0.2.1-0.20260331025433-9cce38e8a396 h1:Y3be+CIWxJvKpNH1YyeExPG8wA0e8ungTsXrxUtEBlc=
     www.velocidex.com/golang/go-ntfs v0.2.1-0.20260331025433-9cce38e8a396/go.mod h1:4MSO8W9iNMXyBpjSpxApWfMjJUb9IWFD2Yis5JPZaSY=
     www.velocidex.com/golang/go-pe v0.1.1-0.20251107001057-f93001158cd9 h1:y5nOjOAyjH36mEGZsX2Wx3XpS78e9Jk2wK1M99rn4TY=
    @@ -1133,7 +1134,7 @@ www.velocidex.com/golang/oleparse v0.0.0-20250312121321-f7c2b4ec0959 h1:qJlm0T61
     www.velocidex.com/golang/oleparse v0.0.0-20250312121321-f7c2b4ec0959/go.mod h1:wjOPwI3Vy6TP0AhF6NjjXV/to93E5A1tjA04liHvf5E=
     www.velocidex.com/golang/regparser v0.0.0-20250203141505-31e704a67ef7 h1:BMX/37sYwX+8JhHt+YNbPfbx7dXG1w1L1mXonNBtjt0=
     www.velocidex.com/golang/regparser v0.0.0-20250203141505-31e704a67ef7/go.mod h1:pxSECT5mWM3goJ4sxB4HCJNKnKqiAlpyT8XnvBwkLGU=
    -www.velocidex.com/golang/vfilter v0.0.0-20260325003227-469fc60d0e45 h1:zxoivsTCZlFvON3YQ7L/2DqxjHzEZbzGkOUuxnm8Df4=
    -www.velocidex.com/golang/vfilter v0.0.0-20260325003227-469fc60d0e45/go.mod h1:5g1ExLSKP3TpT1cAF6bLzVWL8opGzTifzVjmMAdDnFE=
    +www.velocidex.com/golang/vfilter v0.0.0-20260513155414-08c929c15ddb h1:uHexHO1uVqiL+JZQTBegPEd8TTZpJn897Vc+b6fot9Q=
    +www.velocidex.com/golang/vfilter v0.0.0-20260513155414-08c929c15ddb/go.mod h1:5g1ExLSKP3TpT1cAF6bLzVWL8opGzTifzVjmMAdDnFE=
     www.velocidex.com/golang/vtypes v0.0.0-20250802153006-821cec8fd392 h1:+FrLvUI9WPVcVsIvsZMtmPVwOcutLbbO1WWXxRFm2es=
     www.velocidex.com/golang/vtypes v0.0.0-20250802153006-821cec8fd392/go.mod h1:tjaJNlBWbvH4cEMrEu678CFR2hrtcdyPINIpRxrOh4U=
    
  • grpc_client/grpc.go+26 8 modified
    @@ -52,7 +52,7 @@ const (
     	API_User
     
     	// Used by the Velociraptor Server to make minion to master API
    -	// calls. Implicitely trusted.
    +	// calls. Implicitly trusted.
     	SuperUser
     )
     
    @@ -64,6 +64,11 @@ var (
     		Help: "Total number of internal gRPC calls.",
     	})
     
    +	grpcStubs = promauto.NewGauge(prometheus.GaugeOpts{
    +		Name: "grpc_stubs",
    +		Help: "Total current number of stubs.",
    +	})
    +
     	grpcTimeoutCounter = promauto.NewCounter(prometheus.CounterOpts{
     		Name: "grpc_client_timeouts",
     		Help: "Total number of timeouts in getting a connection from the pool.",
    @@ -104,7 +109,7 @@ func NewGRPCPool(config_obj *config_proto.Config,
     	case SuperUser:
     		if config_obj.Frontend != nil && config_obj.Client != nil {
     			// Present the frontend certificate as our identity. This
    -			// will be implicitely trusted for every ACL.
    +			// will be implicitly trusted for every ACL.
     			certificate = config_obj.Frontend.Certificate
     			private_key = config_obj.Frontend.PrivateKey
     			ca_certificate = config_obj.Client.CaCertificate
    @@ -232,12 +237,23 @@ func (self GRPCAPIClient) GetAPIClient(
     		return nil, nil, err
     	}
     
    -	grpcCallCounter.Inc()
    +	// Channels should be reused so we can return the channel to the
    +	// pool immediately.
    +	defer channel.Close()
     
    -	return api_proto.NewAPIClient(channel.ClientConn), channel.Close, err
    +	grpcCallCounter.Inc()
    +	grpcStubs.Inc()
    +	return api_proto.NewAPIClient(channel.ClientConn),
    +		// This is called when the stub is done with.
    +		func() error {
    +			grpcStubs.Dec()
    +			return nil
    +		},
    +		err
     }
     
    -func (self *gRPCPool) getChannel(ctx context.Context) (*grpcpool.ClientConn, error) {
    +func (self *gRPCPool) getChannel(ctx context.Context) (
    +	*grpcpool.ClientConn, error) {
     
     	// Collect number of callers waiting for a channel - this
     	// indicates backpressure from the grpc pool.
    @@ -346,8 +362,8 @@ func (self *gRPCPool) EnsureInit(
     		return grpc.DialContext(ctx, self.address, opts...)
     	}
     
    -	max_size := 100
    -	max_wait := 60
    +	max_size := 10
    +	max_wait := 6000
     	if self.config_obj.Frontend != nil {
     		if self.config_obj.Frontend.GRPCPoolMaxSize > 0 {
     			max_size = int(self.config_obj.Frontend.GRPCPoolMaxSize)
    @@ -359,7 +375,9 @@ func (self *gRPCPool) EnsureInit(
     	}
     
     	self.pool, err = grpcpool.NewWithContext(ctx,
    -		factory, 1, max_size, time.Duration(max_wait)*time.Second)
    +		factory, 1, max_size,
    +		time.Duration(max_wait)*time.Second,
    +		time.Duration(max_wait)*time.Second)
     	if err != nil {
     		return fmt.Errorf(
     			"Unable to connect to gRPC server: %v: %v", self.address, err)
    
  • responder/flow_context.go+36 16 modified
    @@ -69,7 +69,7 @@ type FlowContext struct {
     	error_message     string // If an error occurs trap the error message
     
     	last_stats_timestamp uint64
    -	frequency_msec       uint64
    +	frequency_ns         uint64
     
     	// We ensure to only send the final flow complete message
     	// once. This will trigger a System.Flow.Completion event on the
    @@ -93,26 +93,32 @@ func newFlowContext(ctx context.Context,
     
     	// How often do we send a FlowStat message to sync the server's
     	// flow progress stat.
    -	frequency_msec := uint64(5000)
    +	frequency := 5 * time.Second
     	if config_obj != nil &&
     		config_obj.Client != nil &&
     		config_obj.Client.DefaultServerFlowStatsUpdate > 0 {
    -		frequency_msec = config_obj.Client.DefaultServerFlowStatsUpdate
    +		frequency = time.Second * time.Duration(
    +			config_obj.Client.DefaultServerFlowStatsUpdate)
     	}
    +
     	if req.FlowRequest.FlowUpdateTime > 0 {
    -		frequency_msec = req.FlowRequest.FlowUpdateTime
    +		frequency = time.Second * time.Duration(
    +			req.FlowRequest.FlowUpdateTime)
     	}
     
     	// Default is set by config file
    -	batch_delay := uint64(5000)
    +	batch_delay := 5 * time.Second
     	if config_obj != nil &&
     		config_obj.Frontend != nil &&
     		config_obj.Frontend.Resources != nil &&
     		config_obj.Frontend.Resources.DefaultLogBatchTime > 0 {
    -		batch_delay = config_obj.Frontend.Resources.DefaultLogBatchTime
    +		batch_delay = time.Millisecond *
    +			time.Duration(config_obj.Frontend.Resources.DefaultLogBatchTime)
     	}
    +
     	if req.FlowRequest.LogBatchTime > 0 {
    -		batch_delay = req.FlowRequest.LogBatchTime
    +		batch_delay = time.Millisecond *
    +			time.Duration(req.FlowRequest.LogBatchTime)
     	}
     
     	// Allow the flow to be cancelled.
    @@ -123,7 +129,7 @@ func newFlowContext(ctx context.Context,
     		wg:                &sync.WaitGroup{},
     		output:            output,
     		req:               req.FlowRequest,
    -		frequency_msec:    frequency_msec,
    +		frequency_ns:      uint64(frequency.Nanoseconds()),
     		config_obj:        config_obj,
     		flow_id:           flow_id,
     		owner:             owner,
    @@ -140,7 +146,7 @@ func newFlowContext(ctx context.Context,
     			case <-sub_ctx.Done():
     				return
     
    -			case <-time.After(time.Duration(batch_delay) * time.Millisecond):
    +			case <-time.After(batch_delay):
     				stats := self.MaybeSendStats()
     				if stats != nil {
     					select {
    @@ -200,15 +206,15 @@ func (self *FlowContext) GetJSONLBytes(name string) uint64 {
     	self.mu.Lock()
     	defer self.mu.Unlock()
     
    -	count, _ := self.total_jsonl_bytes[name]
    +	count := self.total_jsonl_bytes[name]
     	return count
     }
     
     func (self *FlowContext) ChargeJSONLBytes(name string, bytes uint64) {
     	self.mu.Lock()
     	defer self.mu.Unlock()
     
    -	count, _ := self.total_jsonl_bytes[name]
    +	count := self.total_jsonl_bytes[name]
     	count += bytes
     	self.total_jsonl_bytes[name] = count
     }
    @@ -243,7 +249,7 @@ func (self *FlowContext) _Cancel() {
     	}
     
     	self.addLogMessage("ERROR",
    -		fmt.Sprintf("Cancelled all inflight queries for flow %v", self.flow_id))
    +		fmt.Sprintf("Cancelled all in-flight queries for flow %v", self.flow_id))
     
     	self._Close()
     }
    @@ -326,18 +332,32 @@ func (self *FlowContext) FlushLogMessages(ctx context.Context) {
     }
     
     func (self *FlowContext) flushLogMessages(ctx context.Context) {
    +	var messages []*crypto_proto.VeloMessage
     	buf, id, count, error_message := self.getLogMessages()
     	if len(buf) > 0 {
    -		self.output <- &crypto_proto.VeloMessage{
    +		messages = append(messages, &crypto_proto.VeloMessage{
     			SessionId: self.flow_id,
     			RequestId: constants.LOG_SINK,
     			LogMessage: &crypto_proto.LogMessage{
     				Id:           int64(id),
     				NumberOfRows: count,
     				Jsonl:        string(buf),
     				ErrorMessage: error_message,
    -			}}
    +			}})
     	}
    +
    +	// Dump the messages to the output in the background so we do not
    +	// block the locks for too long.
    +	go func() {
    +		for _, msg := range messages {
    +			select {
    +			case <-ctx.Done():
    +				return
    +
    +			case self.output <- msg:
    +			}
    +		}
    +	}()
     }
     
     // Alert messages are sent in their own packet because the server will
    @@ -436,10 +456,10 @@ func (self *FlowContext) MaybeSendStats() *crypto_proto.VeloMessage {
     		return nil
     	}
     
    -	now := uint64(utils.GetTime().Now().UnixNano() / 1000)
    +	now := uint64(utils.GetTime().Now().UnixNano())
     	last_timestamp := self.last_stats_timestamp
     	if self.isFlowComplete() ||
    -		now-last_timestamp > self.frequency_msec {
    +		now-last_timestamp > self.frequency_ns {
     		self.last_stats_timestamp = now
     		return self.getStats()
     	}
    
  • result_sets/simple/simple.go+11 6 modified
    @@ -102,10 +102,10 @@ func (self *ResultSetWriterImpl) SetSync() {
     }
     
     // WriteJSONL writes an entire JSONL blob to the end of the result
    -// set. This is supposed to be very fast so we dont have to parse the
    +// set. This is supposed to be very fast so we don't have to parse the
     // JSON (Typically the client sends us the complete JSON blob).  Since
    -// we do not not know exactly where in the JSON blob each row starts
    -// we update the index to refer to the begining of the row and the
    +// we do not know exactly where in the JSON blob each row starts
    +// we update the index to refer to the beginning of the row and the
     // number of rows from there.
     
     // The reader will find the correct row by loading the JSONL file at
    @@ -116,6 +116,10 @@ func (self *ResultSetWriterImpl) WriteJSONL(serialized []byte, total_rows uint64
     		total_rows = countLines(serialized)
     	}
     
    +	if total_rows > constants.MAX_ROW_LIMIT {
    +		return utils.MemoryError
    +	}
    +
     	if len(serialized) == 0 {
     		return nil
     	}
    @@ -276,7 +280,8 @@ func (self ResultSetFactory) NewResultSetWriter(
     	}
     
     	// Call the completion when both files are done.
    -	completer := utils.NewCompleter(completion)
    +	completer, closer := utils.NewCompleter(completion)
    +	defer closer()
     
     	fd, err := file_store_factory.WriteFileWithCompletion(
     		log_path, completer.GetCompletionFunc())
    @@ -566,7 +571,7 @@ func (self ResultSetFactory) DeleteResultSet(
     	// 3. optionally a chunk file for compressed result sets
     	// 4. A directory hierarchy of transformed cache files.
     
    -	// Try to delete these but dont worry if they are missing
    +	// Try to delete these but don't worry if they are missing
     	_ = file_store_factory.Delete(path.
     		SetType(api.PATH_TYPE_FILESTORE_JSON_INDEX))
     
    @@ -609,7 +614,7 @@ func (self ResultSetFactory) NewResultSetReader(
     	}
     	// Keep the open file until the reader is closed.
     
    -	// -1 indicates we dont know how many rows there are
    +	// -1 indicates we don't know how many rows there are
     	total_rows := int64(-1)
     	var mtime time.Time
     	idx_fd, err := file_store_factory.ReadFile(log_path.
    
  • result_sets/timed/writer.go+9 3 modified
    @@ -49,6 +49,8 @@ type TimedResultSetWriterImpl struct {
     
     	// Set for internal artifact writers to avoid writing to disk at all.
     	internal bool
    +
    +	closer func()
     }
     
     func (self *TimedResultSetWriterImpl) Write(row *ordereddict.Dict) {
    @@ -111,7 +113,7 @@ func (self *TimedResultSetWriterImpl) Flush() {
     	}
     
     	for _, row := range self.rows {
    -		writer, err := self.getWriter(row.ts)
    +		writer, err := self.getWriter()
     		if err == nil {
     			_ = writer.WriteBuffer(row.ts, row.serialized)
     		}
    @@ -122,7 +124,7 @@ func (self *TimedResultSetWriterImpl) Flush() {
     	self.total_rows_cached = 0
     }
     
    -func (self *TimedResultSetWriterImpl) getWriter(ts time.Time) (
    +func (self *TimedResultSetWriterImpl) getWriter() (
     	*timelines.TimelineWriter, error) {
     	log_path, err := self.path_manager.GetPathForWriting()
     	if err != nil {
    @@ -169,6 +171,7 @@ func (self *TimedResultSetWriterImpl) Close() {
     		self.log_path = nil
     		self.writer = nil
     	}
    +	self.closer()
     }
     
     func NewTimedResultSetWriter(
    @@ -182,14 +185,17 @@ func NewTimedResultSetWriter(
     		return nil, err
     	}
     
    +	completer, closer := utils.NewCompleter(completion)
    +
     	return &TimedResultSetWriterImpl{
     		config_obj:   config_obj,
     		path_manager: path_manager,
     		opts:         opts,
     
     		// Only call the completion function once all writes
     		// completed.
    -		completer: utils.NewCompleter(completion),
    +		completer: completer,
    +		closer:    closer,
     		internal:  log_path == nil,
     	}, nil
     }
    
  • services/client_info/tasks.go+10 10 modified
    @@ -99,7 +99,7 @@ func (self *ClientInfoManager) QueueMessagesForClient(
     	// to update the client record's has_tasks field. On the master
     	// node this information will be flushed on the next snapshot
     	// write.
    -	completer := utils.NewCompleter(func() {
    +	completer, closer := utils.NewCompleter(func() {
     		err := self.storage.Modify(ctx, self.config_obj, client_id,
     			func(client_info *services.ClientInfo) (*services.ClientInfo, error) {
     				if client_info == nil {
    @@ -127,7 +127,7 @@ func (self *ClientInfoManager) QueueMessagesForClient(
     			notifier.NotifyDirectListener(client_id)
     		}
     	})
    -	defer completer.GetCompletionFunc()()
    +	defer closer()
     
     	client_path_manager := paths.NewClientPathManager(client_id)
     
    @@ -170,7 +170,7 @@ func (self *ClientInfoManager) QueueMessageForClient(
     		return err
     	}
     
    -	completer := utils.NewCompleter(func() {
    +	completer, closer := utils.NewCompleter(func() {
     		if completion != nil &&
     			!utils.CompareFuncs(completion, utils.SyncCompleter) {
     			completion()
    @@ -205,7 +205,7 @@ func (self *ClientInfoManager) QueueMessageForClient(
     			notifier.NotifyDirectListener(client_id)
     		}
     	})
    -	defer completer.GetCompletionFunc()()
    +	defer closer()
     
     	client_path_manager := paths.NewClientPathManager(client_id)
     	return db.SetSubjectWithCompletion(self.config_obj,
    @@ -316,7 +316,7 @@ func (self *ClientInfoManager) getClientTasks(
     
     					// Tack the first VQLClientAction on top of the
     					// FlowRequest for backwards compatibility. Newer clients
    -					// procees FlowRequest first and ignore VQLClientAction
    +					// process FlowRequest first and ignore VQLClientAction
     					// while older clients will process the VQLClientAction
     					// and ignore the FlowRequest message. In both cases the
     					// message will be valid.
    @@ -389,7 +389,7 @@ func (self *ClientInfoManager) GetClientTasks(
     	ctx context.Context, client_id string) (
     	[]*crypto_proto.VeloMessage, error) {
     
    -	// This list holds the flows that are inflight and we have not
    +	// This list holds the flows that are in-flight and we have not
     	// heard from them for some time. We can actively request the
     	// client to report on them again to see how they are going.
     	var inflight_notifications []string
    @@ -423,7 +423,7 @@ func (self *ClientInfoManager) GetClientTasks(
     			// Gather up any stats notifications we might have
     			inflight_requests = len(client_info.InFlightFlows)
     
    -			// No tasks to send and we dont have anything in flight -
    +			// No tasks to send and we don't have anything in flight -
     			// just exit quickly.
     			if !client_info.HasTasks && inflight_requests == 0 {
     				return nil, noTasksError
    @@ -439,7 +439,7 @@ func (self *ClientInfoManager) GetClientTasks(
     					}
     				}
     
    -				// Update the time to ensure we dont send these too often.
    +				// Update the time to ensure we don't send these too often.
     				for _, k := range inflight_notifications {
     					client_info.InFlightFlows[k] = utils.GetTime().Now().Unix()
     				}
    @@ -471,7 +471,7 @@ func (self *ClientInfoManager) GetClientTasks(
     		max_inflight_requests = 2 + int(self.config_obj.Client.Concurrency)
     	}
     
    -	// If the feature is disabled we dont have a limit on the number
    +	// If the feature is disabled we don't have a limit on the number
     	// of tasks we send.
     	if !inflight_checks_enabled {
     		max_inflight_requests = 100000
    @@ -559,7 +559,7 @@ func (self *ClientInfoManager) GetClientTasks(
     
     	if inflight_checks_enabled && len(inflight_flows) > 0 {
     
    -		// Add the inflight tags to the client record immediately.
    +		// Add the in-flight tags to the client record immediately.
     		err := self.storage.Modify(ctx, self.config_obj, client_id,
     			func(client_info *services.ClientInfo) (*services.ClientInfo, error) {
     				if client_info == nil {
    
  • timelines/writer.go+2 1 modified
    @@ -146,7 +146,8 @@ func NewTimelineWriter(
     	result := &TimelineWriter{}
     
     	// Call the completer when both index and file are done.
    -	completer := utils.NewCompleter(completion)
    +	completer, closer := utils.NewCompleter(completion)
    +	defer closer()
     
     	file_store_factory := file_store.GetFileStore(config_obj)
     
    
  • utils/completer.go+25 6 modified
    @@ -34,13 +34,13 @@ const (
       need to perform. This is the common usage pattern.
     
       func doStuff() {
    -    completer := NewCompleter(func() {
    +    completer, closer := NewCompleter(func() {
           fmt.Printf("I am called once")
         })
     
    -    // This ensures the completer is not called until we leave this
    -    // function.
    -    defer completer.GetCompletionFunc()()
    +    // The closer must be called after all sub-completer functions are
    +    // created with GetCompletionFunc()
    +    defer closer()
     
         err := db.SetSubjectWithCompletion(...., completer.GetCompletionFunc())
         ...
    @@ -57,10 +57,29 @@ type Completer struct {
     	completion func()
     }
     
    -func NewCompleter(completion func()) *Completer {
    -	return &Completer{
    +// To avoid a race the API has changed to force the completer to close
    +// at an opportune time. The closer should be called **after** all the
    +// completion functions have been called:
    +//
    +// completer, closer := NewCompleter(completion)
    +// defer closer()
    +//
    +// ... Code using the completer getting new sub-completions with
    +// completer.GetCompletionFunc()
    +//
    +// When the closer() is called all GetCompletionFunc() have been
    +// called on the completer!
    +func NewCompleter(completion func()) (completer *Completer, closer func()) {
    +	res := &Completer{
     		completion: completion,
     	}
    +
    +	// Do not actually call the sync completer.
    +	if CompareFuncs(completion, SyncCompleter) {
    +		return res, func() {}
    +	}
    +
    +	return res, res.GetCompletionFunc()
     }
     
     func (self *Completer) GetCompletionFunc() func() {
    
  • vql/darwin/xattr.go+5 1 modified
    @@ -4,6 +4,7 @@ package darwin
     
     import (
     	"context"
    +	"sort"
     
     	"golang.org/x/sys/unix"
     
    @@ -126,7 +127,10 @@ func List(path string) ([]string, error) {
     	if err != nil {
     		return nil, err
     	}
    -	return stripPrefix(nullTermToStrings(buf[:size])), nil
    +	res := stripPrefix(nullTermToStrings(buf[:size]))
    +	sort.Strings(res)
    +
    +	return res, nil
     }
     
     // Associates data as an extended attribute of path.
    
  • vql/server/flows/monitoring.go+3 2 modified
    @@ -187,7 +187,7 @@ func (self WatchMonitoringPlugin) Call(
     			return
     		}
     
    -		journal, _ := services.GetJournal(config_obj)
    +		journal, err := services.GetJournal(config_obj)
     		if err != nil {
     			return
     		}
    @@ -235,7 +235,8 @@ func (self WatchMonitoringPlugin) Call(
     			case <-ctx.Done():
     				return
     
    -			case output_chan <- row:
    +			case output_chan <- row.
    +				Update("_Source", arg.Artifact):
     			}
     		}
     	}()
    

Vulnerability mechanics

No source-code context for this CVE — mechanics is only generated when we can read the actual fix diff. Without that, the four sections (root cause, attack vector, affected code, fix) would be speculation rather than analysis.

References

1

News mentions

0

No linked articles in our index yet.