Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
go-version-file: go.mod

Expand All @@ -24,6 +24,6 @@ jobs:
run: go test ./...

- name: Lint
uses: golangci/golangci-lint-action@v7
uses: golangci/golangci-lint-action@v9
with:
version: v2.12.2
35 changes: 23 additions & 12 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,21 @@ secretlens scan --fail-on=HIGH .
| フラグ | 説明 | デフォルト |
|--------|------|-----------|
| `--all` | Git履歴 + 環境変数ファイルをスキャン | false |
| `--source` | スキャンソース: `git` `envfile` `cilog` `docker` | git+envfile |
| `--source` | スキャンソース: `git` `envfile` `all` `cilog` `docker` | git+envfile |
| `--format` | 出力形式: `text` `json` `sarif` `html` `github-pr` | text |
| `--out` | 出力ファイルパス(省略時: stdout) | — |
| `--fail-on` | 指定Severity以上で exit 1: `CRITICAL` `HIGH` `MEDIUM` `LOW` | — |
| `--rules-dir` | YAMLルールディレクトリ | 実行ファイル隣の `rules/` |
| `--rules-dir` | 追加・上書きYAMLルールディレクトリ | なし(内蔵ルールのみ) |
| `--baseline` | ベースラインファイルパス | `.secretlens.baseline.json` |
| `--repo` | GitHub リポジトリ `owner/repo`(cilog用) | — |
| `--gitlab-url` | GitLabインスタンスURL(cilog用) | — |
| `--project-id` | GitLabプロジェクトID(cilog用) | — |
| `--image` | Dockerイメージ名(docker用) | — |
| `--pr` | PRコメントを投稿するPR番号 | — |
| `--sha` | Check Runを作成するコミットSHA | — |
| `--github-token` | GitHub APIトークン(`GITHUB_TOKEN`環境変数も可) | — |
| `--slack-webhook` | Slack Webhook URL(`SLACK_WEBHOOK_URL`環境変数も可) | — |
| `--verify` | Live API検証を実行(opt-in) | false |
| `--verify` | 対応ルールのLive API検証を実行(opt-in、現在はGitHubトークンのみ) | false |

### `secretlens org`

Expand All @@ -93,12 +95,13 @@ secretlens org --org=my-company --format=html --out=audit.html
| `--concurrency` | 並列スキャン数 | 4 |
| `--format` | 出力形式: `text` `json` `html` | text |
| `--out` | 出力ファイルパス | — |
| `--rules-dir` | 追加・上書きYAMLルールディレクトリ | なし(内蔵ルールのみ) |

### `secretlens baseline`

```bash
secretlens baseline update . # スキャンして現在の検出結果をベースラインに追加
secretlens baseline list # 登録済みfingerprint一覧
secretlens baseline update # ベースライン更新ガイドを表示
```

### `secretlens rules list`
Expand All @@ -119,10 +122,11 @@ secretlens rules list # 有効ルール一覧(ID / Severity /
| ベースルール: HIGH | +40 |
| ベースルール: MEDIUM | +20 |
| Live検証通過 | +50 |
| エントロピー > 4.5 | +20 |
| エントロピーがルールの`entropy_min`以上(未設定時は > 4.5 | +20 |
| センシティブファイル名 (`.env`, `credentials` 等) | +15 |
| テストコード内 | -30 |
| コメント行 | -20 |

コメント行はスキャン対象外のため、検出自体が行われません。

| スコア | Severity |
|--------|----------|
Expand All @@ -135,7 +139,7 @@ secretlens rules list # 有効ルール一覧(ID / Severity /

## カスタムルール

`rules/` 以下にYAMLファイルを追加することでルールを拡張できます
標準ルールはバイナリに内蔵されています。任意のディレクトリにYAMLファイルを置き `--rules-dir` で指定すると、ルールを追加できます(同一IDは上書き)

```yaml
# rules/my-company.yaml
Expand Down Expand Up @@ -172,7 +176,13 @@ rules:
誤検知をベースラインに登録して以降のスキャンで除外できます。

```bash
# fingerprintを確認
# スキャンして現在の検出結果をすべてベースラインに追加
secretlens baseline update .

# 登録済みfingerprintを一覧表示
secretlens baseline list

# 現在のスキャンのfingerprintを確認
secretlens scan --format=json . | jq -r '.[].fingerprint'

# .secretlens.baseline.json を手動編集して追加
Expand All @@ -197,11 +207,11 @@ jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0 # Git履歴を全取得

- uses: actions/setup-go@v5
- uses: actions/setup-go@v7
with:
go-version-file: go.mod

Expand All @@ -220,7 +230,7 @@ jobs:
.

- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: results.sarif
Expand Down Expand Up @@ -248,6 +258,7 @@ jobs:
| `rules/aws.yaml` | AWS Access Key ID / Secret Access Key |
| `rules/gcp.yaml` | GCP Service Account Key / API Key |
| `rules/azure.yaml` | Azure Storage Account Key / Connection String |
| `rules/github.yaml` | GitHub Token |
| `rules/jwt.yaml` | JWT Token |
| `rules/generic.yaml` | API Key / Password / Token / Private Key / Connection String |

Expand Down Expand Up @@ -279,7 +290,7 @@ secretlens/
│ │ ├── regex/ # YAMLルール読み込み + 正規表現マッチ
│ │ ├── entropy/ # Shannon entropy 計算
│ │ ├── context/ # テストコード / コメント除外
│ │ └── verifier/ # Live API検証 (AWS / GCP / GitHub)
│ │ └── verifier/ # Live API検証(現在はGitHubトークンのみ)
│ ├── finding/ # Finding構造体 + スコアリング
│ ├── reporter/
│ │ ├── sarif/ # SARIF v2.1.0 出力
Expand Down
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,21 @@ secretlens scan --fail-on=HIGH .
| Flag | Description | Default |
|------|-------------|---------|
| `--all` | Scan Git history and environment files | false |
| `--source` | Scan source: `git` `envfile` `cilog` `docker` | git+envfile |
| `--source` | Scan source: `git` `envfile` `all` `cilog` `docker` | git+envfile |
| `--format` | Output format: `text` `json` `sarif` `html` `github-pr` | text |
| `--out` | Output file path. Writes to stdout when omitted | - |
| `--fail-on` | Exit with code 1 at or above severity: `CRITICAL` `HIGH` `MEDIUM` `LOW` | - |
| `--rules-dir` | Directory containing YAML rules | `rules/` next to the executable |
| `--rules-dir` | Directory of additional / overriding YAML rules | none (embedded rules only) |
| `--baseline` | Baseline file path | `.secretlens.baseline.json` |
| `--repo` | GitHub repository in `owner/repo` format, used by `cilog` | - |
| `--gitlab-url` | GitLab instance URL, used by `cilog` | - |
| `--project-id` | GitLab project ID, used by `cilog` | - |
| `--image` | Docker image name, used by `docker` | - |
| `--pr` | Pull request number for posting a PR comment | - |
| `--sha` | Commit SHA for creating a Check Run | - |
| `--github-token` | GitHub API token. `GITHUB_TOKEN` is also supported | - |
| `--slack-webhook` | Slack webhook URL. `SLACK_WEBHOOK_URL` is also supported | - |
| `--verify` | Run live API verification for detected secrets (opt-in) | false |
| `--verify` | Run live API verification for supported rules (opt-in; currently GitHub tokens only) | false |

### `secretlens org`

Expand All @@ -93,12 +95,13 @@ secretlens org --org=my-company --format=html --out=audit.html
| `--concurrency` | Number of concurrent scans | 4 |
| `--format` | Output format: `text` `json` `html` | text |
| `--out` | Output file path | - |
| `--rules-dir` | Directory of additional / overriding YAML rules | none (embedded rules only) |

### `secretlens baseline`

```bash
secretlens baseline update . # scan and add all current findings to the baseline
secretlens baseline list # list registered fingerprints
secretlens baseline update # show baseline update guidance
```

### `secretlens rules list`
Expand All @@ -119,10 +122,11 @@ Each finding receives a score, which is then mapped to a severity level.
| Base rule: HIGH | +40 |
| Base rule: MEDIUM | +20 |
| Live verification passed | +50 |
| Entropy > 4.5 | +20 |
| Entropy at or above the rule's `entropy_min` (or > 4.5 when unset) | +20 |
| Sensitive file name such as `.env` or `credentials` | +15 |
| Test code | -30 |
| Comment line | -20 |

Comment lines are skipped entirely and never produce findings.

| Score | Severity |
|-------|----------|
Expand All @@ -135,7 +139,7 @@ Each finding receives a score, which is then mapped to a severity level.

## Custom Rules

Add YAML files under `rules/` to extend the rule set.
Standard rules are embedded in the binary. Put YAML files in any directory and pass it via `--rules-dir` to add rules (same IDs override embedded ones).

```yaml
# rules/my-company.yaml
Expand Down Expand Up @@ -172,7 +176,13 @@ rules:
Add known false positives to a baseline file so future scans can ignore them.

```bash
# Inspect fingerprints
# Scan and add all current findings to the baseline
secretlens baseline update .

# List registered fingerprints
secretlens baseline list

# Inspect fingerprints of the current scan
secretlens scan --format=json . | jq -r '.[].fingerprint'

# Add fingerprints manually to .secretlens.baseline.json
Expand All @@ -197,11 +207,11 @@ jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0 # fetch full Git history

- uses: actions/setup-go@v5
- uses: actions/setup-go@v7
with:
go-version-file: go.mod

Expand All @@ -220,7 +230,7 @@ jobs:
.

- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: results.sarif
Expand Down Expand Up @@ -248,6 +258,7 @@ jobs:
| `rules/aws.yaml` | AWS Access Key ID / Secret Access Key |
| `rules/gcp.yaml` | GCP Service Account Key / API Key |
| `rules/azure.yaml` | Azure Storage Account Key / Connection String |
| `rules/github.yaml` | GitHub Token |
| `rules/jwt.yaml` | JWT Token |
| `rules/generic.yaml` | API Key / Password / Token / Private Key / Connection String |

Expand Down Expand Up @@ -279,7 +290,7 @@ secretlens/
| | |-- regex/ # YAML rule loading and regex matching
| | |-- entropy/ # Shannon entropy calculation
| | |-- context/ # exclude test code and comments
| | `-- verifier/ # live API verification for AWS / GCP / GitHub
| | `-- verifier/ # live API verification (currently GitHub tokens)
| |-- finding/ # Finding type and scoring
| |-- reporter/
| | |-- sarif/ # SARIF v2.1.0 output
Expand Down
71 changes: 47 additions & 24 deletions cmd/secretlens/baseline_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package main

import (
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"

"github.com/nobuo-miura/SecretLens/internal/baseline"
"github.com/nobuo-miura/SecretLens/internal/detector/regex"
"github.com/nobuo-miura/SecretLens/internal/scanner"
)

var (
flagBLFile string
flagBLRulesDir string
)

var baselineCmd = &cobra.Command{
Expand All @@ -20,7 +23,7 @@ var baselineListCmd = &cobra.Command{
Use: "list",
Short: "登録済みfingerprintを一覧表示",
RunE: func(cmd *cobra.Command, args []string) error {
bl, err := baseline.Load(baseline.DefaultFile)
bl, err := baseline.Load(flagBLFile)
if err != nil {
return err
}
Expand All @@ -37,11 +40,44 @@ var baselineListCmd = &cobra.Command{
}

var baselineUpdateCmd = &cobra.Command{
Use: "update",
Use: "update [path]",
Short: "現在のスキャン結果をベースラインに追加",
Long: "scan コマンドの出力をベースラインに追加します。先に scan --format=json を実行してください。",
Long: "指定パス(省略時はカレントディレクトリ)をスキャンし、検出された全fingerprintをベースラインに追加して保存します。",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("使用方法: secretlens scan --format=json | jq -r '.[].fingerprint' でfingerprintを取得し、baseline updateで登録してください。")
repoPath := "."
if len(args) > 0 {
repoPath = args[0]
}

rules, err := loadRules(flagBLRulesDir)
if err != nil {
return err
}

bl, err := baseline.Load(flagBLFile)
if err != nil {
return err
}

// 既存baselineでのフィルタは有効のまま実行し、新規検出分だけを追加する
findings, err := scanner.Run(scanner.Options{
Source: "all",
RepoPath: repoPath,
Rules: rules,
BaselineFile: flagBLFile,
})
if err != nil {
return err
}

for _, f := range findings {
bl.Add(f.Fingerprint)
}
if err := bl.Save(); err != nil {
return err
}
fmt.Printf("ベースラインを更新しました: %d件追加 (合計%d件) → %s\n", len(findings), len(bl.List()), flagBLFile)
return nil
},
}
Expand All @@ -56,25 +92,10 @@ var rulesListCmd = &cobra.Command{
Short: "有効ルール一覧を表示",
RunE: func(cmd *cobra.Command, args []string) error {
rulesDir, _ := cmd.Flags().GetString("rules-dir")
if rulesDir == "" {
exe, err := os.Executable()
if err != nil {
exe = "."
}
rulesDir = filepath.Join(filepath.Dir(exe), "rules")
if _, err := os.Stat(rulesDir); os.IsNotExist(err) {
rulesDir = "rules"
}
}

rules, err := regex.LoadRulesFromDir(rulesDir)
rules, err := loadRules(rulesDir)
if err != nil {
return err
}
if len(rules) == 0 {
fmt.Printf("ルールが見つかりません: %s\n", rulesDir)
return nil
}
fmt.Printf("%-30s %-10s %s\n", "ID", "SEVERITY", "NAME")
fmt.Printf("%-30s %-10s %s\n", "---", "--------", "----")
for _, r := range rules {
Expand All @@ -86,11 +107,13 @@ var rulesListCmd = &cobra.Command{
}

func init() {
baselineCmd.PersistentFlags().StringVar(&flagBLFile, "baseline", baseline.DefaultFile, "ベースラインファイルパス")
baselineUpdateCmd.Flags().StringVar(&flagBLRulesDir, "rules-dir", "", "追加・上書きYAMLルールディレクトリ(省略時は内蔵ルールのみ)")
baselineCmd.AddCommand(baselineListCmd)
baselineCmd.AddCommand(baselineUpdateCmd)
rootCmd.AddCommand(baselineCmd)

rulesListCmd.Flags().String("rules-dir", "", "YAMLルールディレクトリ(デフォルト: 実行ファイル隣のrules/)")
rulesListCmd.Flags().String("rules-dir", "", "追加・上書きYAMLルールディレクトリ(省略時は内蔵ルールのみ)")
rulesCmd.AddCommand(rulesListCmd)
rootCmd.AddCommand(rulesCmd)
}
Loading