Skip to content

定数とエラー

ライブラリが定義する定数、エラー型、センチネルエラー、事前定義変数。

セキュリティ制限定数

デフォルト制限

go
const (
    // DefaultMaxFileSize - 単一ファイルの最大バイト数
    DefaultMaxFileSize int64 = 2 * 1024 * 1024  // 2 MB

    // DefaultMaxLineLength - 単一行の最大長
    DefaultMaxLineLength int = 1024  // 1 KB

    // DefaultMaxKeyLength - キー名の最大長
    DefaultMaxKeyLength int = 64

    // DefaultMaxValueLength - 値の最大長
    DefaultMaxValueLength int = 4096  // 4 KB

    // DefaultMaxVariables - ファイルごとの最大変数数
    DefaultMaxVariables int = 500

    // DefaultMaxExpansionDepth - 変数展開の最大深度
    DefaultMaxExpansionDepth int = 5
)

ハードリミット

注意

以下はライブラリ内部のハードリミット(未エクスポート)で、Config.Validate() の内部チェックに使用されます。ユーザーはこれらの定数を直接参照できませんが、cfg.Validate() が設定がこれらの制限を超えていないか自動的にチェックします。

定数説明
HardMaxFileSize100 MBファイルサイズのハードリミット
HardMaxLineLength64 KB行の長さのハードリミット
HardMaxKeyLength1024キーの長さのハードリミット
HardMaxValueLength1 MB値の長さのハードリミット
HardMaxVariables10000変数数のハードリミット
HardMaxExpansionDepth20展開深度のハードリミット

設定検証はハードリミットを超えていないか確認します:

go
cfg := env.DefaultConfig()
cfg.MaxFileSize = 200 * 1024 * 1024  // 100MB 上限を超過

if err := cfg.Validate(); err != nil {
    // エラーを返します:MaxFileSize exceeds hard limit
}

センチネルエラー

注意

以下のセンチネルは事前定義されたシンボルですが、現在の実装では一部のシナリオでこれらのセンチネルに errors.Is で一致しません:禁止キーは *SecurityError を返し(errors.Is(err, ErrSecurityViolation) で一致)、キー形式不正や必須キー不足は *ValidationError を返します(errors.As で抽出)。詳細は各エラー型のセクションを参照してください。

ファイルエラー

go
var ErrFileNotFound = errors.New("file not found")
var ErrFileTooLarge = errors.New("file exceeds maximum size limit")

確認方法:

go
err := loader.LoadFiles(".env")
if errors.Is(err, env.ErrFileNotFound) {
    // ファイルが存在しません
}
if errors.Is(err, env.ErrFileTooLarge) {
    // ファイルが大きすぎます
}

解析エラー

go
var ErrLineTooLong = errors.New("line exceeds maximum length limit")
var ErrInvalidKey = errors.New("invalid key format")
var ErrDuplicateKey = errors.New("duplicate key encountered")

セキュリティエラー

go
var ErrForbiddenKey = errors.New("key is forbidden for security reasons")
var ErrSecurityViolation = errors.New("security policy violation")
var ErrInvalidValue = errors.New("invalid value content")

禁止キーの確認:

go
err := loader.Set("PATH", "value")
if errors.Is(err, env.ErrSecurityViolation) {
    // 禁止キーの設定を試みると *SecurityError が返されます
}

展開エラー

go
var ErrExpansionDepth = errors.New("variable expansion depth exceeded")

制限エラー

go
var ErrMaxVariables = errors.New("maximum number of variables exceeded")

ステータスエラー

go
var ErrClosed = errors.New("loader has been closed")
var ErrInvalidConfig = errors.New("invalid configuration")
var ErrAlreadyInitialized = errors.New("default loader already initialized")
var ErrNotInitialized = errors.New("default loader not initialized; call Load() first")
var ErrMissingRequired = errors.New("required key is missing")

確認方法:

go
// ローダーがクローズ済みか確認
if errors.Is(err, env.ErrClosed) {
    // ローダーはクローズ済み
}

// デフォルトローダーが初期化済みか確認
if errors.Is(err, env.ErrAlreadyInitialized) {
    // デフォルトローダーが既に存在し、Load を繰り返し呼び出せません
}

// デフォルトローダーが未初期化か確認
if errors.Is(err, env.ErrNotInitialized) {
    // 先に env.Load() または env.LoadWithConfig() を呼び出す必要があります
}

// 必須キーが不足していないか確認(実際は *ValidationError{Rule:"required"})
var valErr *env.ValidationError
if errors.As(err, &valErr) && valErr.Rule == "required" {
    // 必須キーが不足
}

アダプターエラー

go
var ErrValidateRequiredUnsupported = errors.New(
    "custom validator does not implement ValidateRequired; " +
    "implement Validator interface for required key validation",
)

カスタムバリデーターが KeyValidator インターフェースのみを実装し、完全な Validator インターフェースを実装していない場合、ValidateRequired を呼び出すとこのエラーが返されます。

確認方法:

go
if errors.Is(err, env.ErrValidateRequiredUnsupported) {
    // カスタムバリデーターは必須キー検証をサポートしません
    // 完全な Validator インターフェースの実装が必要
}

解決方法

KeyValidator のみではなく、Validator インターフェース(ValidateKey、ValidateValue、ValidateRequired の 3 つのメソッドを含む)を実装してください。

エラー型

ParseError

解析エラー、位置情報を含みます:

go
type ParseError struct {
    File    string  // ファイル名
    Line    int     // 行番号
    Content string  // エラー内容(マスク済み)
    Err     error   // 元のエラー
}

使用例:

go
err := loader.LoadFiles(".env")
var parseErr *env.ParseError
if errors.As(err, &parseErr) {
    fmt.Printf("解析エラー %s:%d: %v\n",
        parseErr.File, parseErr.Line, parseErr.Err)
}

ValidationError

検証エラー:

go
type ValidationError struct {
    Field   string  // フィールド名
    Value   string  // 値(マスク済み)
    Rule    string  // ルール
    Message string  // メッセージ
}

SecurityError

セキュリティエラー:

go
type SecurityError struct {
    Action  string  // 操作
    Reason  string  // 原因
    Key     string  // キー名(マスク済み)
    Details string  // 追加詳細
}

使用例:

go
var secErr *env.SecurityError
if errors.As(err, &secErr) {
    fmt.Printf("セキュリティエラー: %s - %s\n", secErr.Action, secErr.Reason)
}

FileError

ファイル操作エラー:

go
type FileError struct {
    Path  string  // ファイルパス
    Op    string  // 操作(open, stat, size_check)
    Err   error   // 元のエラー
    Size  int64   // ファイルサイズ(Size チェック時)
    Limit int64   // 制限(Size チェック時)
}

使用例:

go
var fileErr *env.FileError
if errors.As(err, &fileErr) {
    fmt.Printf("ファイル %s サイズ %d が制限 %d を超過\n",
        fileErr.Path, fileErr.Size, fileErr.Limit)
}

ExpansionError

変数展開エラー:

go
type ExpansionError struct {
    Key   string             // キー名
    Depth int                // 現在の深度
    Limit int                // 制限
    Chain string             // 展開チェーン(サニタイズ済み)
    Kind  ExpansionErrorKind // エラー原因の分類(ゼロ値 = 深度/循環)
}

エラー分類(Kind フィールド):

go
type ExpansionErrorKind int

const (
    // ExpansionDepthKind は展開が再帰深度制限に達したか、変数の循環を検出したことを示します。
    // これはゼロ値のため、一般的な深度/循環エラーに明示的な分類は不要です。
    // errors.Is(err, ErrExpansionDepth) で一致します。
    ExpansionDepthKind ExpansionErrorKind = iota

    // ExpansionRequiredKind は必須変数(${VAR:?message})が未設定または空であったことを示します。
    // 深度オーバーフローではないため、ErrExpansionDepth には一致しません。
    ExpansionRequiredKind
)

errors.Is の挙動: *ExpansionErrorKind != ExpansionRequiredKind の場合のみ ErrExpansionDepth と一致します。必須変数エラーは別の失敗モードであり、ErrExpansionDepth には一致しません。

使用例:

go
var expErr *env.ExpansionError
if errors.As(err, &expErr) {
    switch expErr.Kind {
    case env.ExpansionDepthKind:
        // 深度オーバーフローまたは循環:errors.Is(err, env.ErrExpansionDepth) == true
        fmt.Printf("深度 %d/%d、チェーン: %s\n", expErr.Depth, expErr.Limit, expErr.Chain)
    case env.ExpansionRequiredKind:
        // 必須変数未設定:errors.Is(err, env.ErrExpansionDepth) == false
        fmt.Printf("必須変数 %s が未設定\n", expErr.Key)
    }
}

JSONError

JSON 解析エラー:

go
type JSONError struct {
    Path    string  // ファイルパス
    Message string  // エラーメッセージ
    Err     error   // 元のエラー
}

YAMLError

YAML 解析エラー:

go
type YAMLError struct {
    Path    string  // ファイルパス
    Line    int     // 行番号
    Column  int     // 列番号
    Message string  // エラーメッセージ
    Err     error   // 元のエラー
}

MarshalError

シリアライズエラー:

go
type MarshalError struct {
    Field   string  // フィールド名
    Message string  // エラーメッセージ
}

func IsMarshalError(err error) bool  // チェック関数

事前定義変数

DefaultForbiddenKeys

組み込み禁止キーリスト、システム重要変数の変更を防止します:

注意

defaultForbiddenKeys はライブラリ内部変数(未エクスポート)であり、env.DefaultForbiddenKeys から直接アクセスすることはできません。以下は内部で使用されている完全なリストで、参考用です。

カテゴリ禁止キー
システムパスPATH
動的リンカー (Linux)LD_PRELOAD, LD_PRELOAD_32, LD_PRELOAD_64, LD_LIBRARY_PATH, LD_LIBRARY_PATH_32, LD_LIBRARY_PATH_64, LD_AUDIT, LD_DEBUG
macOSDYLD_INSERT_LIBRARIES, DYLD_LIBRARY_PATH
WindowsCOMSPEC, PATHEXT, SYSTEMROOT, WINDIR
ShellSHELL, ENV, BASH_ENV, IFS
言語ランタイムPYTHONPATH, NODE_PATH, PERL5OPT, RUBYLIB

リスク説明:

キーリスクタイプ説明
PATHコマンド乗っ取りコマンド検索パスの変更
LD_PRELOADライブラリ注入悪意のある動的ライブラリをプリロード
LD_LIBRARY_PATHライブラリ乗っ取りライブラリ検索パスの変更
DYLD_INSERT_LIBRARIESライブラリ注入macOS ライブラリ注入
COMSPECコマンド乗っ取りWindows コマンドインタプリタのパス上書き
PATHEXTコマンド乗っ取りWindows 実行ファイル拡張子の改ざん
SYSTEMROOTシステム破壊Windows システムルートの改ざん
WINDIRシステム破壊Windows ディレクトリの改ざん
PYTHONPATHモジュール乗っ取りPython モジュール検索パス
IFSパース攻撃フィールド区切りの変更

使用例:

go
// 禁止キーを設定しようとすると *SecurityError が返されます
err := loader.Set("PATH", "/malicious/path")
if errors.Is(err, env.ErrSecurityViolation) {
    // キーが禁止されています
}

// 追加の禁止キーを設定
cfg := env.DefaultConfig()
cfg.ForbiddenKeys = []string{"MY_SENSITIVE_VAR"}

SensitiveKeyPatterns

機密キーパターンリスト、機密設定の自動検出に使用されます。キー名にこれらのパターンが含まれる場合(大文字小文字を区別しない)、機密として識別されます:

注意

sensitiveKeyPatterns はライブラリ内部変数(未エクスポート)であり、IsSensitiveKey() 関数を通じて間接的にアクセスします。以下は主要な機密パターンのカテゴリで、参考用です。

主要な機密パターンのカテゴリ:

カテゴリパターン例
認証・認可PASSWORD, SECRET, TOKEN, AUTH, CREDENTIAL, PASSPHRASE, SESSION, COOKIE
API とキーAPI_KEY, APIKEY, ACCESS_KEY, SECRET_KEY, PRIVATE_KEY, PUBLIC_KEY
暗号化とセキュリティPRIVATE, ENCRYPTION_KEY, ENCRYPT_KEY, DECRYPT_KEY, SIGNING_KEY, SIGN_KEY, VERIFY_KEY
金融・PIISSN, SOCIAL_SECURITY, CREDIT_CARD, CARD_NUMBER, CVV, CVC, CCV, PAN
暗号資産MNEMONIC, SEED, RECOVERY, WALLET, PRIVATE_ADDRESS
データベースCONNECTION_STRING, CONN_STRING, DATABASE_URL, DB_PASSWORD
クラウドサービスAWS_SECRET, AZURE_KEY, GCP_KEY, SERVICE_ACCOUNT

マッチングルール:

  • 大文字小文字を区別しない
  • キー名にいずれかのパターンが含まれれば機密として識別

使用例:

go
// キーが機密かどうかを確認
if env.IsSensitiveKey("DB_PASSWORD") {
    // 安全な方法で処理
    secret := env.GetSecure("DB_PASSWORD")
    if secret != nil {
        defer secret.Release()
    }
}

DefaultKeyPattern

デフォルトのキー名検証パターン:

go
var DefaultKeyPattern *regexp.Regexp = nil

パフォーマンス最適化

nil 値は高速なバイトレベル検証を有効にします(約 10 倍の性能向上)。 デフォルト検証ルール:英字で始まり、英字・数字・アンダースコアのみを含みます。

カスタムパターン:

go
import "regexp"

cfg := env.DefaultConfig()
// 大文字で始まるもののみ許可
cfg.KeyPattern = regexp.MustCompile(`^[A-Z][A-Z0-9_]{1,63}$`)

セキュリティツール関数

IsSensitiveKey

go
func IsSensitiveKey(key string) bool

キー名が機密パターンに一致するか確認します。

go
if env.IsSensitiveKey("DB_PASSWORD") {
    // 機密キー、安全な方法で処理
    secret := env.GetSecure("DB_PASSWORD")
    defer secret.Release()
}

MaskValue

go
func MaskValue(key, value string) string

キーの機密性に基づいてマスク値を返します。

go
// 機密キー - [MASKED:N chars] 形式で返す
masked := env.MaskValue("API_KEY", "secret123")
// 戻り値:[MASKED:9 chars]

// 非機密キー - 元の値を返す(20 文字を超える場合は切り詰め)
masked := env.MaskValue("APP_NAME", "myapp")
// 戻り値:myapp
masked := env.MaskValue("DESCRIPTION", "this is a very long description text")
// 戻り値:this is a very lo...

MaskKey

go
func MaskKey(key string) string

ログ用にキー名をマスクします。

go
masked := env.MaskKey("DB_PASSWORD")
// 戻り値:DB***

MaskSensitiveInString

go
func MaskSensitiveInString(s string) string

文字列内の潜在的な機密内容をマスクします。50 文字を超える文字列は切り詰められます。

パラメータ:

  • s - 元の文字列

戻り値:

  • string - マスクされた文字列
go
// 長い文字列は切り詰められます
log := "This is a very long log message that exceeds 50 characters and will be truncated"
clean := env.MaskSensitiveInString(log)
// 戻り値:"This is a very long log message that exceeds 50..."

// 短い文字列はそのまま保持
short := "Short message"
clean := env.MaskSensitiveInString(short)
// 戻り値:"Short message"

注意

この関数は主に長い文字列の切り詰めに使用されます。機密キーと値のペアを自動マスクする必要がある場合は、SanitizeForLog を使用してください。

SanitizeForLog

go
func SanitizeForLog(s string) string

文字列内の機密キーと値のペア情報をクリーンアップします。key=value フォーマットの機密値を自動検出してマスクします。

パラメータ:

  • s - 元の文字列

戻り値:

  • string - クリーンアップされた文字列

検出される機密キーパターン:

  • password=, secret=, token=, auth=, credential=, passphrase=, session=, cookie=
  • api_key=, apikey=, access_key=, secret_key=, private_key=, public_key=
  • encrypt_key=, decrypt_key=, signing_key=
  • ssn=, credit_card=, card_number=, cvv=, cvc=
  • mnemonic=, seed=, recovery=, wallet=
  • connection_string=, database_url=, db_password=
go
// 機密キーと値のペアを自動マスク
msg := "Connected with password=secret123 api_key=abc123"
clean := env.SanitizeForLog(msg)
// 戻り値:"Connected with password=[MASKED] api_key=[MASKED]"

// 非機密キーと値のペアはそのまま
msg := "Config loaded: app_name=myapp port=8080"
clean := env.SanitizeForLog(msg)
// 戻り値:"Config loaded: app_name=myapp port=8080"

ユースケース

ログ出力、エラーメッセージ、デバッグ情報など、機密キーと値のペアを自動フィルタリングする必要がある場面に適しています。

ClearBytes

go
func ClearBytes(b []byte)

バイトスライスを安全にゼロクリアします。

go
sensitive := []byte("secret-data")
// 使用...
env.ClearBytes(sensitive)
// sensitive 現在はすべて 0

FileFormat 定数

ファイルフォーマット型:

go
type FileFormat int

const (
    FormatAuto  FileFormat = iota  // 自動検出
    FormatEnv                      // .env フォーマット
    FormatJSON                     // JSON フォーマット
    FormatYAML                     // YAML フォーマット
)

使用例:

go
// フォーマットを検出
format := env.DetectFormat("config.json")  // FormatJSON

// フォーマットを指定してシリアライズ
data, _ := env.Marshal(cfg, env.FormatJSON)

// フォーマット文字列
fmt.Println(format.String())  // "json"

エラーチェックパターン

errors.Is パターン

センチネルエラーの確認:

go
err := loader.LoadFiles(".env")

switch {
case errors.Is(err, env.ErrFileNotFound):
    // ファイルが存在しません
case errors.Is(err, env.ErrFileTooLarge):
    // ファイルが大きすぎます
case errors.Is(err, env.ErrSecurityViolation):
    // 禁止キー
case errors.Is(err, env.ErrClosed):
    // ローダーはクローズ済み
}

errors.As パターン

詳細なエラー情報を抽出:

go
err := loader.LoadFiles(".env")

var parseErr *env.ParseError
if errors.As(err, &parseErr) {
    fmt.Printf("解析エラー %s%d 行目\n", parseErr.File, parseErr.Line)
}

var fileErr *env.FileError
if errors.As(err, &fileErr) {
    fmt.Printf("ファイル %s サイズ %d が制限 %d を超過\n",
        fileErr.Path, fileErr.Size, fileErr.Limit)
}

var secErr *env.SecurityError
if errors.As(err, &secErr) {
    fmt.Printf("セキュリティエラー: %s - %s\n", secErr.Action, secErr.Reason)
}

完全なエラー処理例

go
package main

import (
    "errors"
    "log"

    "github.com/cybergodev/env"
)

func main() {
    cfg := env.ProductionConfig()
    cfg.FailOnMissingFile = true

    loader, err := env.New(cfg)
    if err != nil {
        log.Fatal(err)
    }
    defer loader.Close()

    err = loader.LoadFiles(".env")
    if err != nil {
        switch {
        case errors.Is(err, env.ErrFileNotFound):
            log.Fatal("設定ファイルが存在しません")

        case errors.Is(err, env.ErrFileTooLarge):
            log.Fatal("設定ファイルが大きすぎます")

        case errors.Is(err, env.ErrClosed):
            log.Fatal("ローダーはクローズ済み")

        default:
            var parseErr *env.ParseError
            if errors.As(err, &parseErr) {
                log.Fatalf("解析エラー %s:%d - %v",
                    parseErr.File, parseErr.Line, parseErr.Err)
            }

            var fileErr *env.FileError
            if errors.As(err, &fileErr) {
                log.Fatalf("ファイルエラー %s - %v", fileErr.Path, fileErr.Err)
            }

            var secErr *env.SecurityError
            if errors.As(err, &secErr) {
                log.Fatalf("セキュリティエラー: %s - %s", secErr.Action, secErr.Reason)
            }

            var jsonErr *env.JSONError
            if errors.As(err, &jsonErr) {
                log.Fatalf("JSON エラー %s: %s", jsonErr.Path, jsonErr.Message)
            }

            var yamlErr *env.YAMLError
            if errors.As(err, &yamlErr) {
                log.Fatalf("YAML エラー %s:%d:%d - %s",
                    yamlErr.Path, yamlErr.Line, yamlErr.Column, yamlErr.Message)
            }

            log.Fatal(err)
        }
    }

    // 必須キーを検証
    if err := loader.Validate(); err != nil {
        var valErr *env.ValidationError
        if errors.As(err, &valErr) {
            log.Fatalf("検証失敗: %s - %s", valErr.Field, valErr.Message)
        }
        log.Fatal(err)
    }
}

関連ドキュメント