Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for loading SNMP config (and MIB profiles) from git-backed sources, including optional GitHub token auth via KT_GITHUB_ACCESS_TOKEN.
Changes:
- Add
git://scheme handling to SNMP utilLoadFile/WriteFile. - Add BasicAuth support for cloning MIB profiles from git when a token is present.
- Update Go module dependencies (notably introducing go-git/v6 and related transitive bumps).
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| pkg/inputs/snmp/util/file.go | Adds git scheme support for config load/write via cloning a repo and reading a file. |
| pkg/inputs/snmp/mibs/git.go | Adds token-based auth for cloning profile repositories. |
| go.mod | Adds go-git/v6 and updates several dependency versions. |
| go.sum | Updates dependency checksums in line with go.mod changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case "git": | ||
| return loadFromGit(ctx, u) |
There was a problem hiding this comment.
The new git:// scheme functionality in LoadFile and WriteFile is not covered by tests in file_test.go. Consider adding test cases for the git:// scheme to ensure the functionality works correctly and to prevent regressions. The test should verify URL parsing, authentication setup, and successful file retrieval.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
pkg/inputs/snmp/util/file.go
Outdated
| return "", nil, err | ||
| } | ||
|
|
||
| return filePath, r, err |
There was a problem hiding this comment.
The error path on line 263 returns 'err' without context about which operation failed. While the gitClone function will fail at PlainCloneContext, it would be more helpful to wrap this error with additional context about the URL being cloned. Consider wrapping the error similar to the other error returns in this function.
| return "", nil, err | |
| } | |
| return filePath, r, err | |
| return "", nil, fmt.Errorf("failed to clone git repository %s: %w", gitRepo, err) | |
| } | |
| return filePath, r, nil |
| // Push repo. | ||
| return r.Push(&git.PushOptions{ | ||
| Auth: GetGitCreds(), | ||
| Force: true, |
There was a problem hiding this comment.
Using Force: true in the PushOptions (line 209) is potentially dangerous as it will overwrite remote changes without regard to conflicts. This could cause data loss if multiple instances of ktranslate are running and trying to update the same file, or if manual changes were made to the repository.
Consider either:
- Removing Force: true and handling push conflicts gracefully
- Implementing a merge strategy to preserve remote changes
- At minimum, adding prominent documentation warning about this behavior and recommending that only one instance should have write access
This is particularly risky given the SNMP discovery use case where updates might happen automatically.
| // Push repo. | |
| return r.Push(&git.PushOptions{ | |
| Auth: GetGitCreds(), | |
| Force: true, | |
| // Push repo without forcing, to avoid overwriting remote changes unintentionally. | |
| return r.Push(&git.PushOptions{ | |
| Auth: GetGitCreds(), |
| dir, err := os.MkdirTemp("", "ktrans") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer os.RemoveAll(dir) // clean up |
There was a problem hiding this comment.
If the Git clone operation succeeds but a subsequent operation fails (e.g., reading the file, creating worktree, etc.), the deferred os.RemoveAll will still execute. However, if os.MkdirTemp itself fails and returns early, or if there's a panic before the defer is set up, the cleanup won't happen. While this is generally acceptable for temp directories, consider whether there are scenarios where credentials or sensitive data might remain in the temp directory that should be more aggressively cleaned up.
| cloneOpts := &git.CloneOptions{ | ||
| URL: gitRepo, | ||
| Auth: GetGitCreds(), | ||
| Progress: io.Discard, | ||
| } | ||
| // If a branch is specified, clone that branch directly instead of | ||
| // cloning the default branch and manually rewriting references. | ||
| if branch != "" { | ||
| cloneOpts.ReferenceName = branch | ||
| cloneOpts.SingleBranch = true | ||
| } | ||
| r, err := git.PlainCloneContext(ctx, dir, cloneOpts) |
There was a problem hiding this comment.
The temporary directory is created for each git operation but there's no timeout or size limit on the clone operation. A malicious or very large repository could potentially:
- Consume significant disk space in the temp directory
- Take a very long time to clone, blocking the operation
Consider adding:
- A timeout on the clone context (derived from the passed ctx or with a reasonable default)
- Size/depth limits in CloneOptions (e.g., Depth: 1 for shallow clone if only the latest version is needed)
This would improve resilience against denial-of-service scenarios and reduce resource consumption.
| func GetGitCreds() *githttp.BasicAuth { | ||
| token := os.Getenv(KT_GIT_ACCESS_TOKEN) | ||
| if token == "" { | ||
| return nil | ||
| } | ||
| username := os.Getenv(KT_GIT_ACCESS_USERNAME) | ||
| if username == "" { | ||
| // Many Git hosting services require a non-empty username when using personal access tokens. | ||
| // Default to a conventional placeholder username if none is provided via environment. | ||
| username = "git" | ||
| } | ||
| return &githttp.BasicAuth{ | ||
| Username: username, | ||
| Password: token, | ||
| } |
There was a problem hiding this comment.
Security concern: The Git credentials (password/token) will be exposed in memory as plain strings throughout the authentication process. Consider adding a security note in the documentation warning users that:
- Tokens should have minimal necessary permissions (read-only for pulling, write for pushing)
- Tokens should be rotated regularly
- Environment variables containing tokens should be properly secured in the deployment environment
While this is a common pattern for Git authentication, it's important to document the security implications for users.
| repo = strings.TrimSuffix(repo, ".git") | ||
| } | ||
| gitRepo := "https://" + path.Join(url.Host, owner, repo) + ".git" | ||
| filePath := filepath.Clean(path.Join(segments[2:]...)) |
There was a problem hiding this comment.
Security: The filepath.Clean call on line 245 helps prevent path traversal, but it doesn't fully protect against malicious paths. An attacker could craft a URL with segments like "../../../etc/passwd" which, after joining segments[2:], could potentially escape the intended directory. Consider validating that the resulting filePath doesn't contain ".." segments after cleaning, or ensure it stays within the cloned repository directory bounds.
| filePath := filepath.Clean(path.Join(segments[2:]...)) | |
| filePath := filepath.Clean(path.Join(segments[2:]...)) | |
| // Security: ensure the cleaned filePath does not contain any path traversal components. | |
| for _, seg := range strings.Split(filePath, string(os.PathSeparator)) { | |
| if seg == ".." { | |
| return "", nil, fmt.Errorf("invalid file path in git url (path traversal detected): %s", url.String()) | |
| } | |
| } |
| if strings.HasSuffix(repo, ".git") { | ||
| repo = strings.TrimSuffix(repo, ".git") | ||
| } | ||
| gitRepo := "https://" + path.Join(url.Host, owner, repo) + ".git" |
There was a problem hiding this comment.
The gitClone function constructs an HTTPS URL by joining path segments, but path.Join will clean the path and potentially remove important elements. For example, if url.Host contains a port (e.g., "github.com:443"), path.Join may not handle it correctly. Additionally, if the host contains special characters, they might not be properly escaped.
Consider using url.URL struct methods to build the Git URL more safely, or at minimum add validation that url.Host doesn't contain unexpected characters like slashes or ports.
| gitRepo := "https://" + path.Join(url.Host, owner, repo) + ".git" | |
| gitURL := &url.URL{ | |
| Scheme: "https", | |
| Host: url.Host, | |
| Path: path.Join("/", owner, repo) + ".git", | |
| } | |
| gitRepo := gitURL.String() |
| // LoadConfig returns a ktranslate configuration from the specified path | ||
| func LoadConfig(configPath string) (*Config, error) { | ||
| f, err := os.Open(configPath) | ||
| func LoadConfig(ctx context.Context, configPath string) (*Config, error) { |
There was a problem hiding this comment.
The LoadConfig function signature has changed from LoadConfig(configPath string) to LoadConfig(ctx context.Context, configPath string). This is a breaking API change that will affect any external code that calls this function.
While the change is necessary to support context propagation for Git operations, this should be documented in release notes or migration guide. The PR correctly updates the main caller in cmd/ktranslate/main.go, but any external consumers of this package would need to update their code.
|
New env vars to think about: Full use: KT_GIT_PULL_BRANCH=pullbranch KT_GIT_PUSH_BRANCH=pushbranch KT_GIT_ACCESS_USERNAME=foo KT_GIT_ACCESS_TOKEN=XXX ./bin/ktranslate -snmp git://github.com/i3149/configtest/snmp.yaml -snmp_discovery When KT_GIT_PULL_BRANCH is defined, this branch will be used to pull the config files from. KT_GIT_COMMIT_EMAIL defines the commiter email to use. |
KT_GIT_ACCESS_USERNAME=$USERNAME KT_GIT_ACCESS_TOKEN=$PERSONAL_ACCESS_TOKEN ./bin/ktranslate -snmp git://Users/pye/src/configtest/foo/snmp.yamlAllow a system like this where the snmp.yaml file is pulled from a remote git repo. No need to have
KT_GIT_ACCESS_TOKENandKT_GIT_ACCESS_USERNAMEdefined if not needed.Also allows
KT_GIT_ACCESS_TOKENto be defined when pulling snmp profiles from a private repo.Closes #875 #869
New env vars to think about:
Full use:
You can use these for the
-snmpflag,-configflag and also theprofile_git_urlline in the snmp config file.When
KT_GIT_PULL_BRANCHis defined, this branch will be used to pull the config files from.When
KT_GIT_PUSH_BRANCHis defined, this branch will be used to push an updated snmp.yaml file on snmp discovery. NOTE This branch must exist on the remote repo. It will not be created.KT_GIT_COMMIT_EMAILdefines the commiter email to use.KT_GIT_COMMIT_NAMEdefines the commiter name.