Files
jmiller bab29a83fa refactor: rename Go module path MokoConsulting/MokoGitea -> MokoConsulting/MokoGIT
Sweep all .go imports + go.mod, plus module-path refs in .golangci.yml,
Dockerfile, swagger templates, locale example, and repo/wiki URLs
(MokoGitea-Fork -> MokoGIT). Part of the MokoGIT rebrand.

Claude-Session: https://claude.ai/code/session_01Bqe7fAuHQeiLueYfeHFrHw
2026-07-14 15:31:19 -05:00

121 lines
3.5 KiB
Go

// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package security
import (
"context"
git_model "code.mokoconsulting.tech/MokoConsulting/MokoGIT/models/git"
repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGIT/models/repo"
security_model "code.mokoconsulting.tech/MokoConsulting/MokoGIT/models/security"
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/modules/git"
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/modules/log"
)
// ScanPushForSecrets checks a commit for secrets and returns findings.
// Used by the pre-receive hook to block pushes containing secrets.
func ScanPushForSecrets(ctx context.Context, repoID int64, commit *git.Commit) []Finding {
cfg, err := security_model.GetScannerConfig(ctx, repoID)
if err != nil {
log.Error("ScanPushForSecrets: GetScannerConfig: %v", err)
return nil
}
if !cfg.Enabled || !cfg.BlockOnPush || !cfg.SecretScanner {
// The owning organization may mandate secret blocking regardless of the
// repository's own scanner config (org push policy).
if !orgRequiresSecretBlock(ctx, repoID) {
return nil
}
}
scanner := NewSecretScanner()
findings, err := scanner.ScanCommit(commit)
if err != nil {
log.Error("ScanPushForSecrets: ScanCommit: %v", err)
return nil
}
return findings
}
// orgRequiresSecretBlock reports whether the repo's owning organization mandates
// secret blocking on push via its org push policy.
func orgRequiresSecretBlock(ctx context.Context, repoID int64) bool {
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
if err != nil {
log.Error("orgRequiresSecretBlock: GetRepositoryByID: %v", err)
return false
}
policy, err := git_model.GetOrgPushPolicyForRepo(ctx, repo)
if err != nil {
log.Error("orgRequiresSecretBlock: GetOrgPushPolicyForRepo: %v", err)
return false
}
return policy != nil && policy.RequireSecretBlock
}
// ScanOnPush runs enabled scanners against a commit pushed to the default branch.
// Called from services/repository/push.go on default branch pushes.
func ScanOnPush(ctx context.Context, repo *repo_model.Repository, commit *git.Commit) {
if commit == nil {
return
}
cfg, err := security_model.GetScannerConfig(ctx, repo.ID)
if err != nil {
log.Error("SecurityScan: GetScannerConfig for %s: %v", repo.FullName(), err)
return
}
if !cfg.Enabled {
return
}
var scanners []Scanner
if cfg.SecretScanner {
scanners = append(scanners, NewSecretScanner())
}
if cfg.DependScanner {
scanners = append(scanners, NewDependencyScanner())
}
if cfg.CodeScanner {
scanners = append(scanners, NewCodeScanner())
}
if len(scanners) == 0 {
return
}
totalFindings := 0
for _, s := range scanners {
findings, err := s.ScanTree(commit)
if err != nil {
log.Error("SecurityScan: %s scanner for %s: %v", s.Type(), repo.FullName(), err)
continue
}
for _, f := range findings {
alert := &security_model.SecurityAlert{
RepoID: repo.ID,
Scanner: f.Scanner,
Severity: f.Severity,
RuleID: f.RuleID,
Title: f.Title,
Description: f.Description,
FilePath: f.FilePath,
LineNumber: f.LineNumber,
CommitSHA: f.CommitSHA,
Fingerprint: f.Fingerprint,
Metadata: f.Metadata,
}
if err := security_model.CreateOrUpdateAlert(ctx, alert); err != nil {
log.Error("SecurityScan: CreateOrUpdateAlert: %v", err)
}
totalFindings++
}
}
if totalFindings > 0 {
log.Warn("SecurityScan: %d findings in %s", totalFindings, repo.FullName())
}
}