From 7424e1d32fd639d804ed461996e6c1752736e96b Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Sun, 8 Nov 2026 17:54:36 +0000 Subject: [PATCH 1/2] Detect sensitive ignore policy --- README.md | 6 +- detector.go | 1 + go.mod | 1 + go.sum | 2 + local.go | 31 +++++- sensitive_ignores.go | 195 ++++++++++++++++++++++++++++++++++++++ sensitive_ignores_test.go | 147 ++++++++++++++++++++++++++++ 7 files changed, 380 insertions(+), 3 deletions(-) create mode 100644 sensitive_ignores.go create mode 100644 sensitive_ignores_test.go diff --git a/README.md b/README.md index bfeb0fc..4fd932d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # signals -A command for tracing security-relevant evidence stored in Git across repository history. It shows when detected evidence appears, changes, or disappears, with the commit and path behind each change. `signals` walks commit trees with [`github.com/git-pkgs/history`](https://github.com/git-pkgs/history). Local parsers detect workflow risks and permissions, artifact-provenance configuration, CI and security tools, threat surface, secrets, certificates and trust stores, Agent Skills, REUSE metadata, package managers, manifests, lockfiles, dependency pinning, update configuration, licenses, SBOMs, fuzzers, and binary artifacts. Commit data adds dependency-bot and signature events plus contributor, maintenance, review, and signing metrics. The output contains the evidence and its provenance without calculating a score. The scan reads local Git objects, with forge APIs, release APIs, credential validation, and external services disabled. +A command for tracing security-relevant evidence stored in Git across repository history. It shows when detected evidence appears, changes, or disappears, with the commit and path behind each change. `signals` walks commit trees with [`github.com/git-pkgs/history`](https://github.com/git-pkgs/history). Local parsers detect workflow risks and permissions, artifact-provenance configuration, CI and security tools, threat surface, secrets, certificates and trust stores, Agent Skills, sensitive ignore policy, REUSE metadata, package managers, manifests, lockfiles, dependency pinning, update configuration, licenses, SBOMs, fuzzers, and binary artifacts. Commit data adds dependency-bot and signature events plus contributor, maintenance, review, and signing metrics. The output contains the evidence and its provenance without calculating a score. The scan reads local Git objects, with forge APIs, release APIs, credential validation, and external services disabled. The local-only timeline answers questions about when repository evidence changed. Examples include: @@ -156,11 +156,12 @@ The additional detectors use the same persistent signal model. A threat-surface | Runner-Selection | Explicit self-hosted runner labels, runner groups, and dynamic runner expressions in GitHub Actions jobs | | Package-Parse | Package files that were identified but could not be parsed | | Secret | Findings from the embedded `git-pkgs/secrets` rule corpus; output contains `[redacted]` in place of the credential | +| Sensitive-Ignore | Effective `.gitignore` rules covering a bounded list of environment, credential, private-key, secret, state, and database paths, with the source pattern and line | | REUSE | Per-file SPDX license declarations, REUSE.toml or DEP5 metadata, and the number of non-empty covered files with both license and copyright information | ## History scanning -The root commit and missing history boundaries receive a full scan. Later commits reuse their first parent's signal state and update the paths changed by that commit. Cross-file detectors, including Agent Skills, dependency-update configuration, and REUSE metadata, receive a full commit tree when one of their inputs changes. Agent Skills rerun when a `SKILL.md` or a file inside a known skill package changes. +The root commit and missing history boundaries receive a full scan. Later commits reuse their first parent's signal state and update the paths changed by that commit. Cross-file detectors, including Agent Skills, dependency-update configuration, REUSE metadata, and sensitive ignore policy, receive a full commit tree when one of their inputs changes. Agent Skills rerun when a `SKILL.md` or a file inside a known skill package changes. Sensitive ignore policy reruns only when a project `.gitignore` changes. Parser results for changed files are cached by Git blob OID and path for the whole run. Repository files that cannot contain a certificate, license, SBOM, fuzzer, policy, or SAST configuration need only a 1 KB prefix for binary classification. Commit reading can run concurrently, while signal-state updates remain ordered. `github.com/git-pkgs/languages` keeps a language inventory so Brief can rerun language-gated detection only when needed. `github.com/git-pkgs/roles` removes bundled and derived paths from fuzzing and license detection, and removes test, fixture, example, cache, and build-output paths from certificate detection. Test paths remain eligible for fuzz detection because fuzz targets commonly use them. @@ -172,6 +173,7 @@ The local detectors share parsers and changed-path caches across related signals - [`github.com/git-pkgs/magic`](https://github.com/git-pkgs/magic) detects binary content. - [`github.com/git-pkgs/certs`](https://github.com/git-pkgs/certs) scans bounded certificate files, trust stores, and small archives. +- [`github.com/git-pkgs/gitignore`](https://github.com/git-pkgs/gitignore) resolves effective ignore rules and returns their source pattern and line. - [`github.com/git-pkgs/licenses`](https://github.com/git-pkgs/licenses) identifies license text, notices, and SPDX declarations with its embedded ScanCode corpus. - [`github.com/git-pkgs/manifests`](https://github.com/git-pkgs/manifests) parses package metadata, declared licenses, Actions, Docker, NuGet, and other dependency files. - [`github.com/git-pkgs/managers`](https://github.com/git-pkgs/managers) identifies package managers and their manifest or lockfile evidence from commit trees. diff --git a/detector.go b/detector.go index 847b2c1..a51f011 100644 --- a/detector.go +++ b/detector.go @@ -17,6 +17,7 @@ const ( checkSAST = "SAST" checkSBOM = "SBOM" checkSecurityPolicy = "Security-Policy" + checkSensitiveIgnore = "Sensitive-Ignore" checkTokenPermissions = "Token-Permissions" signalKindDeclaration = "declaration" signalKindFileReference = "file-reference" diff --git a/go.mod b/go.mod index 5cc0fb6..81a89a7 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( actionlint.kjanat.dev v1.17.0 github.com/git-pkgs/brief v0.15.1-0.20261107204040-d1ed5a631f83 github.com/git-pkgs/certs v0.1.1-0.20261104093717-2e1d0e2e0f63 + github.com/git-pkgs/gitignore v1.3.1-0.20261108175116-5fcc6a8ea053 github.com/git-pkgs/history v0.2.0 github.com/git-pkgs/languages v0.1.0 github.com/git-pkgs/licenses v0.10.1-0.20261107154534-00c5bf03ade8 diff --git a/go.sum b/go.sum index 92a4bb0..ccee438 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ github.com/git-pkgs/citation v0.1.0 h1:0toQBjxVOtaUsxRtcDJL9uZ6SYFb6IgbHgKpTpP0c github.com/git-pkgs/citation v0.1.0/go.mod h1:4vrS78/jjHeusS+eozjP/d3pX2NogEi1R/7gLVqkgqs= github.com/git-pkgs/codemeta v0.3.0 h1:zkWndmquCv2tPIHCHb+JXx3eKjmxRoD48f4/3sNAlGw= github.com/git-pkgs/codemeta v0.3.0/go.mod h1:R1vGXVohTa22vVKaV3/8xVs3+tcgvqc4ziaMikdXc/I= +github.com/git-pkgs/gitignore v1.3.1-0.20261108175116-5fcc6a8ea053 h1:dSX8+fHbGgxNP3HRVRv9Oxf63m7jEDEhDDaGm2Shaqw= +github.com/git-pkgs/gitignore v1.3.1-0.20261108175116-5fcc6a8ea053/go.mod h1:zleLGZ4PhQrUXrJSsVQWKoeEllEjJH49+DjyKpzIFPM= github.com/git-pkgs/history v0.2.0 h1:UCbrp/PX6rfLukUCRnYme8PtyCqKO8Q5wrpDUY0SOTE= github.com/git-pkgs/history v0.2.0/go.mod h1:DaoJasfycHYLtwr8ZTDp7PDnU8WMjAqmadHgJDqAO4A= github.com/git-pkgs/languages v0.1.0 h1:Y7bwb86k9DgagCJn3bXW6+8lOtwayDD17Np2usEPooY= diff --git a/local.go b/local.go index 334c6b7..f56a027 100644 --- a/local.go +++ b/local.go @@ -38,6 +38,7 @@ const ( detectorBrief = "Brief" detectorAgentSkills = "Agent-Skills" detectorRepositoryFiles = "Repository-Files" + detectorSensitiveIgnore = "Sensitive-Ignores" detectorWorkflows = "Workflows" maxSecretBlobSize = 1 << 20 @@ -51,6 +52,7 @@ var localDetectorNames = []string{ detectorPackages, detectorPackageManagers, detectorRepositoryFiles, + detectorSensitiveIgnore, detectorWorkflows, checkSecret, checkREUSE, @@ -165,6 +167,8 @@ func (d *localDetectors) detect( packageRun := packageFull || packagesAffected(changes) repositoryFileFull := forceFull || parent.errors[detectorRepositoryFiles] != "" repositoryFileRun := repositoryFileFull || len(changes) > 0 + sensitiveIgnoreFull := forceFull || parent.errors[detectorSensitiveIgnore] != "" + sensitiveIgnoreRun := sensitiveIgnoreFull || sensitiveIgnoresAffected(changes) workflowAffected := workflowsAffected(changes) workflowError := parent.errors[detectorWorkflows] workflowFull := forceFull || workflowError != "" && @@ -185,7 +189,7 @@ func (d *localDetectors) detect( reuseRun := reuseFull || parent.reuseActive && len(changes) > 0 var tree fs.FS - if anyDetectorRuns(agentSkillRun, briefRun, dependencyUpdateRun, packageRun, repositoryFileRun, workflowRun, managerRun, reuseRun) { + if anyDetectorRuns(agentSkillRun, briefRun, dependencyUpdateRun, packageRun, repositoryFileRun, sensitiveIgnoreRun, workflowRun, managerRun, reuseRun) { var err error tree, err = d.repo.TreeFS(commit.Hash) if err != nil { @@ -198,6 +202,7 @@ func (d *localDetectors) detect( d.runDependencyUpdateConfigs(tree, dependencyUpdateRun, dependencyUpdateFull, ¤t, stats) d.runPackages(tree, changes, packageRun, packageFull, ¤t, stats) d.runRepositoryFiles(ctx, tree, changes, repositoryFileRun, repositoryFileFull, ¤t, stats) + d.runSensitiveIgnores(tree, sensitiveIgnoreRun, ¤t, stats) d.runWorkflows(tree, changes, workflowRun, workflowFull, ¤t, stats) d.runPackageManagers( tree, @@ -237,6 +242,30 @@ func (d *localDetectors) runAgentSkills( addSignals(current.signals, signals) } +func (d *localDetectors) runSensitiveIgnores( + tree fs.FS, + run bool, + current *commitState, + stats *scanStats, +) { + detectorStats := stats.detectors[detectorSensitiveIgnore] + if !run { + detectorStats.skipped++ + return + } + recordDetectorRun(detectorStats, true) + started := time.Now() + signals, err := sensitiveIgnoreSignals(tree) + detectorStats.duration += time.Since(started) + if err != nil { + current.errors[detectorSensitiveIgnore] = err.Error() + return + } + delete(current.errors, detectorSensitiveIgnore) + removeCheckSignals(current.signals, checkSensitiveIgnore) + addSignals(current.signals, signals) +} + func anyDetectorRuns(runs ...bool) bool { for _, run := range runs { if run { diff --git a/sensitive_ignores.go b/sensitive_ignores.go new file mode 100644 index 0000000..e53abc4 --- /dev/null +++ b/sensitive_ignores.go @@ -0,0 +1,195 @@ +package main + +import ( + "io/fs" + "path" + "sort" + "strconv" + + "github.com/git-pkgs/gitignore" + githistory "github.com/git-pkgs/history" + "github.com/git-pkgs/roles" +) + +const ( + maxSensitiveIgnoreScopes = 10_000 + maxSensitiveIgnoreFileSize = 1 << 20 + sensitiveIgnoreCredential = "credential" + sensitiveIgnoreDatabase = "database" + sensitiveIgnoreEnvironment = "environment" + sensitiveIgnorePrivateKey = "private-key" + sensitiveIgnoreSecret = "secret" + sensitiveIgnoreState = "state" + sensitiveIgnoreCoverage = "coverage" +) + +type sensitiveIgnoreCandidate struct { + kind string + path string +} + +var sensitiveIgnoreCandidates = []sensitiveIgnoreCandidate{ + {kind: sensitiveIgnoreCredential, path: ".aws/credentials"}, + {kind: sensitiveIgnoreCredential, path: ".docker/config.json"}, + {kind: sensitiveIgnoreCredential, path: ".gem/credentials"}, + {kind: sensitiveIgnoreCredential, path: ".netrc"}, + {kind: sensitiveIgnoreCredential, path: ".npmrc"}, + {kind: sensitiveIgnoreCredential, path: ".pypirc"}, + {kind: sensitiveIgnoreCredential, path: "application_default_credentials.json"}, + {kind: sensitiveIgnoreCredential, path: "credentials.json"}, + {kind: sensitiveIgnoreCredential, path: "credentials.yaml"}, + {kind: sensitiveIgnoreCredential, path: "credentials.yml"}, + {kind: sensitiveIgnoreDatabase, path: "backup.sql"}, + {kind: sensitiveIgnoreDatabase, path: "database.dump"}, + {kind: sensitiveIgnoreDatabase, path: "database.sql"}, + {kind: sensitiveIgnoreEnvironment, path: ".env"}, + {kind: sensitiveIgnoreEnvironment, path: ".env.local"}, + {kind: sensitiveIgnoreEnvironment, path: ".env.production"}, + {kind: sensitiveIgnoreEnvironment, path: ".envrc"}, + {kind: sensitiveIgnorePrivateKey, path: "private.key"}, + {kind: sensitiveIgnorePrivateKey, path: "private.pem"}, + {kind: sensitiveIgnorePrivateKey, path: "id_rsa"}, + {kind: sensitiveIgnorePrivateKey, path: "id_ed25519"}, + {kind: sensitiveIgnorePrivateKey, path: "config/master.key"}, + {kind: sensitiveIgnorePrivateKey, path: "certificate.p12"}, + {kind: sensitiveIgnorePrivateKey, path: "certificate.pfx"}, + {kind: sensitiveIgnoreSecret, path: "config/secrets.yml"}, + {kind: sensitiveIgnoreSecret, path: "secrets.json"}, + {kind: sensitiveIgnoreSecret, path: "secrets.yaml"}, + {kind: sensitiveIgnoreSecret, path: "secrets.yml"}, + {kind: sensitiveIgnoreState, path: "terraform.tfstate"}, + {kind: sensitiveIgnoreState, path: "terraform.tfstate.backup"}, + {kind: sensitiveIgnoreState, path: ".terraform/terraform.tfstate"}, +} + +func sensitiveIgnoreSignals(tree fs.FS) ([]Signal, error) { + matcher, scopeNames, signals, truncatedScopes, err := loadSensitiveIgnorePatterns(tree) + if err != nil { + return nil, err + } + signals = append(signals, matchSensitiveIgnoreCandidates(matcher, scopeNames)...) + for _, patternErr := range matcher.Errors() { + signals = append(signals, Signal{ + Check: checkSensitiveIgnore, + Kind: "invalid-pattern", + Name: patternErr.Pattern, + Value: patternErr.Message, + Path: patternErr.Source, + Line: uint(patternErr.Line), + }) + } + if truncatedScopes { + signals = append(signals, Signal{ + Check: checkSensitiveIgnore, + Kind: sensitiveIgnoreCoverage, + Name: "scopes-truncated", + Value: "limit=" + strconv.Itoa(maxSensitiveIgnoreScopes), + }) + } + sortSignals(signals) + return deduplicateSignals(signals), nil +} + +func loadSensitiveIgnorePatterns(tree fs.FS) (*gitignore.Matcher, []string, []Signal, bool, error) { + matcher := gitignore.New("") + scopes := map[string]bool{"": true} + var signals []Signal + truncatedScopes := false + err := fs.WalkDir(tree, ".", func(filePath string, entry fs.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + if entry.IsDir() { + if filePath != "." && sensitiveIgnorePathExcluded(filePath) { + return fs.SkipDir + } + return nil + } + if entry.Name() != ".gitignore" || sensitiveIgnorePathExcluded(filePath) { + return nil + } + info, err := entry.Info() + if err != nil { + return err + } + if info.Size() > maxSensitiveIgnoreFileSize { + signals = append(signals, Signal{ + Check: checkSensitiveIgnore, + Kind: sensitiveIgnoreCoverage, + Name: "oversized-ignore-file", + Value: strconv.FormatInt(info.Size(), 10), + Path: filePath, + }) + return nil + } + data, err := fs.ReadFile(tree, filePath) + if err != nil { + return err + } + dir := path.Dir(filePath) + if dir == "." { + dir = "" + } + matcher.AddPatternsWithSource(data, dir, filePath) + if len(scopes) < maxSensitiveIgnoreScopes { + scopes[dir] = true + } else if !scopes[dir] { + truncatedScopes = true + } + return nil + }) + if err != nil { + return nil, nil, nil, false, err + } + + scopeNames := make([]string, 0, len(scopes)) + for dir := range scopes { + scopeNames = append(scopeNames, dir) + } + sort.Strings(scopeNames) + return matcher, scopeNames, signals, truncatedScopes, nil +} + +func matchSensitiveIgnoreCandidates(matcher *gitignore.Matcher, scopeNames []string) []Signal { + seen := make(map[string]bool) + var signals []Signal + for _, dir := range scopeNames { + for _, candidate := range sensitiveIgnoreCandidates { + target := path.Join(dir, candidate.path) + result := matcher.MatchDetail(target) + if !result.Ignored { + continue + } + key := result.Source + "\x00" + strconv.Itoa(result.Line) + "\x00" + candidate.kind + if seen[key] { + continue + } + seen[key] = true + signals = append(signals, Signal{ + Check: checkSensitiveIgnore, + Kind: candidate.kind, + Name: target, + Value: result.Pattern, + Path: result.Source, + Line: uint(result.Line), + }) + } + } + return signals +} + +func sensitiveIgnorePathExcluded(filePath string) bool { + labels, err := roles.Match(filePath) + return err == nil && (labels.BundledOrDerived() || labels.Has(roles.Test) || + labels.Has(roles.Fixture) || labels.Has(roles.Example) || labels.Has(roles.Cache) || + labels.Has(roles.BuildOutput)) +} + +func sensitiveIgnoresAffected(changes []githistory.Change) bool { + for _, change := range changes { + if path.Base(change.Path) == ".gitignore" && !sensitiveIgnorePathExcluded(change.Path) { + return true + } + } + return false +} diff --git a/sensitive_ignores_test.go b/sensitive_ignores_test.go new file mode 100644 index 0000000..57f4c9c --- /dev/null +++ b/sensitive_ignores_test.go @@ -0,0 +1,147 @@ +package main + +import ( + "bytes" + "os" + "path/filepath" + "strings" + "testing" + "testing/fstest" + + githistory "github.com/git-pkgs/history" +) + +const ( + testRootGitignore = ".gitignore" + testNestedGitignore = "services/api/.gitignore" +) + +func TestSensitiveIgnoreSignalsDescribeEffectivePatterns(t *testing.T) { + fsys := fstest.MapFS{ + testRootGitignore: { + Data: []byte("*.log\n.env*\n*.tfstate\n[[:bogus:]]\n"), + }, + testNestedGitignore: { + Data: []byte("credentials.*\n!credentials.example\n"), + }, + "testdata/project/.gitignore": { + Data: []byte("*.key\n"), + }, + } + + signals, err := sensitiveIgnoreSignals(fsys) + if err != nil { + t.Fatal(err) + } + for _, wanted := range []Signal{ + {Check: checkSensitiveIgnore, Kind: "environment", Name: ".env", Value: ".env*", Path: testRootGitignore, Line: 2}, + {Check: checkSensitiveIgnore, Kind: "state", Name: "terraform.tfstate", Value: "*.tfstate", Path: testRootGitignore, Line: 3}, + {Check: checkSensitiveIgnore, Kind: "credential", Name: "services/api/credentials.json", Value: "credentials.*", Path: testNestedGitignore, Line: 1}, + } { + if !containsExactSignal(signals, wanted) { + t.Errorf("signals missing %#v:\n%#v", wanted, signals) + } + } + for _, signal := range signals { + if strings.HasPrefix(signal.Path, "testdata/") || signal.Name == "*.log" { + t.Fatalf("unwanted ignore signal: %#v", signal) + } + } + var invalid int + for _, signal := range signals { + if signal.Check == checkSensitiveIgnore && signal.Kind == "invalid-pattern" && + signal.Path == testRootGitignore && signal.Line == 4 { + invalid++ + } + } + if invalid != 1 { + t.Errorf("invalid pattern signals = %d, want 1: %#v", invalid, signals) + } +} + +func TestSensitiveIgnoresAffectedOnlyByProjectIgnoreFiles(t *testing.T) { + tests := []struct { + name string + change githistory.Change + want bool + }{ + {name: "root", change: githistory.Change{Path: testRootGitignore}, want: true}, + {name: "nested", change: githistory.Change{Path: testNestedGitignore}, want: true}, + {name: "fixture", change: githistory.Change{Path: "testdata/project/.gitignore"}}, + {name: "other", change: githistory.Change{Path: ".dockerignore"}}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := sensitiveIgnoresAffected([]githistory.Change{test.change}); got != test.want { + t.Errorf("sensitiveIgnoresAffected = %t, want %t", got, test.want) + } + }) + } +} + +func TestSensitiveIgnoreSignalsBoundFileSize(t *testing.T) { + fsys := fstest.MapFS{ + testRootGitignore: {Data: make([]byte, maxSensitiveIgnoreFileSize+1)}, + } + signals, err := sensitiveIgnoreSignals(fsys) + if err != nil { + t.Fatal(err) + } + wanted := Signal{ + Check: checkSensitiveIgnore, + Kind: "coverage", + Name: "oversized-ignore-file", + Value: "1048577", + Path: testRootGitignore, + } + if !containsExactSignal(signals, wanted) { + t.Fatalf("signals missing %#v: %#v", wanted, signals) + } +} + +func TestLogTracksSensitiveIgnorePolicy(t *testing.T) { + repo := t.TempDir() + gitRun(t, repo, "init", "-b", "main") + gitRun(t, repo, "config", "user.name", "Signals Test") + gitRun(t, repo, "config", "user.email", "signals@example.com") + gitRun(t, repo, "config", "commit.gpgsign", "false") + + writeFile(t, repo, "README.md", "# example\n") + gitRun(t, repo, "add", ".") + gitRun(t, repo, "commit", "-m", "Initial commit") + + writeFile(t, repo, testRootGitignore, ".env\n*.tfstate\n") + gitRun(t, repo, "add", testRootGitignore) + gitRun(t, repo, "commit", "-m", "Ignore local environment and state") + + writeFile(t, repo, testRootGitignore, ".env\n*.key\n") + gitRun(t, repo, "add", ".gitignore") + gitRun(t, repo, "commit", "-m", "Ignore private keys instead of state") + + if err := os.Remove(filepath.Join(repo, testRootGitignore)); err != nil { + t.Fatal(err) + } + gitRun(t, repo, "add", "-A") + gitRun(t, repo, "commit", "-m", "Remove ignore policy") + + var stdout, stderr bytes.Buffer + if err := run([]string{testLogCommand, testWorkersFlag, "1", repo}, &stdout, &stderr); err != nil { + t.Fatalf("run log: %v\nstderr:\n%s", err, stderr.String()) + } + output := stdout.String() + for signal, counts := range map[string][2]int{ + "Sensitive-Ignore/environment .env": {1, 1}, + "Sensitive-Ignore/state terraform.tfstate": {1, 1}, + "Sensitive-Ignore/private-key private.key": {1, 1}, + } { + if got := strings.Count(output, "+ "+signal); got != counts[0] { + t.Errorf("%s additions = %d, want %d:\n%s", signal, got, counts[0], output) + } + if got := strings.Count(output, "- "+signal); got != counts[1] { + t.Errorf("%s removals = %d, want %d:\n%s", signal, got, counts[1], output) + } + } + if stderr.Len() != 0 { + t.Fatalf("unexpected detection errors:\n%s", stderr.String()) + } +} From 3b54fc9840b76881c7fe990ab8fd70a7b6435dab Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Sun, 8 Nov 2026 19:41:40 +0000 Subject: [PATCH 2/2] Use merged gitignore source labels --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 81a89a7..a000e16 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( actionlint.kjanat.dev v1.17.0 github.com/git-pkgs/brief v0.15.1-0.20261107204040-d1ed5a631f83 github.com/git-pkgs/certs v0.1.1-0.20261104093717-2e1d0e2e0f63 - github.com/git-pkgs/gitignore v1.3.1-0.20261108175116-5fcc6a8ea053 + github.com/git-pkgs/gitignore v1.3.1-0.20261108194038-d09d5ccc19b9 github.com/git-pkgs/history v0.2.0 github.com/git-pkgs/languages v0.1.0 github.com/git-pkgs/licenses v0.10.1-0.20261107154534-00c5bf03ade8 diff --git a/go.sum b/go.sum index ccee438..5adfd2d 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,8 @@ github.com/git-pkgs/citation v0.1.0 h1:0toQBjxVOtaUsxRtcDJL9uZ6SYFb6IgbHgKpTpP0c github.com/git-pkgs/citation v0.1.0/go.mod h1:4vrS78/jjHeusS+eozjP/d3pX2NogEi1R/7gLVqkgqs= github.com/git-pkgs/codemeta v0.3.0 h1:zkWndmquCv2tPIHCHb+JXx3eKjmxRoD48f4/3sNAlGw= github.com/git-pkgs/codemeta v0.3.0/go.mod h1:R1vGXVohTa22vVKaV3/8xVs3+tcgvqc4ziaMikdXc/I= -github.com/git-pkgs/gitignore v1.3.1-0.20261108175116-5fcc6a8ea053 h1:dSX8+fHbGgxNP3HRVRv9Oxf63m7jEDEhDDaGm2Shaqw= -github.com/git-pkgs/gitignore v1.3.1-0.20261108175116-5fcc6a8ea053/go.mod h1:zleLGZ4PhQrUXrJSsVQWKoeEllEjJH49+DjyKpzIFPM= +github.com/git-pkgs/gitignore v1.3.1-0.20261108194038-d09d5ccc19b9 h1:+1HyWVLjMCi4Xoq9Gh1oq2tmbNn1Aa52tShtuPGddOo= +github.com/git-pkgs/gitignore v1.3.1-0.20261108194038-d09d5ccc19b9/go.mod h1:zleLGZ4PhQrUXrJSsVQWKoeEllEjJH49+DjyKpzIFPM= github.com/git-pkgs/history v0.2.0 h1:UCbrp/PX6rfLukUCRnYme8PtyCqKO8Q5wrpDUY0SOTE= github.com/git-pkgs/history v0.2.0/go.mod h1:DaoJasfycHYLtwr8ZTDp7PDnU8WMjAqmadHgJDqAO4A= github.com/git-pkgs/languages v0.1.0 h1:Y7bwb86k9DgagCJn3bXW6+8lOtwayDD17Np2usEPooY=