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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ make ci # CI と同じ検査 (fmt-check + lint + test、書き換
- mise のグローバル・ローカル・ツール単位の明示 age と uv の `exclude-newer` は native age として読み、depup の制約より厳しい値を維持する。ツール単位の mise age はスコープ内の最厳値を全ツールに適用する。解釈できない native 設定があれば、そのマニフェストを更新しない。
- `go.work` の `use` と `settings.gradle` / `settings.gradle.kts` の `include` を展開してサブプロジェクトのマニフェストも検出する。ルートに `go.mod` / 依存宣言が無い構成では展開しないと「更新なし」と報告して**無言の no-op** になる (Cargo workspace members と pnpm workspace packages には自動展開があるので Go と Java だけ非対称だった)。展開先のパスは `.depup` と同じ安全性検査 (絶対パス・`..`・外部へ解決される symlink の拒否) を通す。検出ソースが増えたため、重複排除は `push_unique_manifest` に集約している。`include` のプロジェクトパスの先頭の `:` は省略できる (`include('app')` は `include(':app')` と同じで、`gradle init` も省略形で書く。以前は `:` を必須にして検出しなかった: #96)。Gradle のプロジェクト名に使えない `/` `\` と `.` / `..` の要素は展開しない
- GitHub Tags の semver 受理判定は `SwiftVersionParser` へ委譲して `Package.swift` 側と一元化する。取得側だけが数値識別子の先頭ゼロを許していたため、`2024.01.15` のような CalVer タグが (semver タグと混在すると `2024 > 1` で必ず) 最新候補に選ばれ、SPM の `Version` が読めない `Package.swift` を書き込んでいた。書き込み側でも `new_version` の妥当性を検証する。タグが 1000 件 (10 ページ) を超えて打ち切られた場合は警告を出す (無言で切ると古い系列が候補から静かに落ちる)
- グローバル設定は `GlobalConfig::read_from` で読み、読み込めない・UTF-8 でない・TOML として解釈できないときは、どれも警告して組み込みの既定値を使う (#123)。以前は `read_to_string(...).ok()?` で読み、解釈できないときだけ警告していたので、読めないファイルや UTF-16 の設定は警告なしに無視され、`age` を既定より長くしていても黙って既定の age で更新先を選んでいた
- `GITHUB_TOKEN` / `GH_TOKEN` は空文字を「未設定」として読み飛ばす。`std::env::var` は「セット済みだが空文字」に対して `Ok("")` を返すため、空値をそのまま採用すると `Authorization: Bearer ` (トークン部が空) を送って GitHub に 401 で拒否され、ヘッダを付けなければ未認証で成功していた取得が全滅する。空の `GITHUB_TOKEN` が置かれた環境で `GH_TOKEN` へフォールバックできない問題も同時に防ぐ
- レジストリのパッケージ名は URL へ埋め込む前に文字種を検証する (npm / PyPI / crates.io / RubyGems / Packagist / Maven Central / GitHub Tags)。`reqwest` が使う `url` crate は WHATWG URL 仕様どおりドットセグメントを正規化するため、`"a/../lodash": "^1.0.0"` のようなキーがあると lodash の版を取得して元のキーへ書き戻す。`?` / `#` はクエリ・フラグメントとして解釈される。npm alias の実名はマニフェストの**値**から切り出されるため、名前の出所はキーだけではない
- RubyGems の `created_at`、PyPI の `upload_time_iso_8601`、Packagist の `time`、Go proxy の `.info` の `Time` が欠落・不正な場合は公開日不明として保持する。age が有効なら日時不明の版を候補から外し、実在しない 1970 年を公開日として表示しない。mise は同じツール・ベンダーの他の版に日時があれば欠落版を除外し、全版に日時がなければ選択を許すが公開日不明と表示する。GitHub Tags は一覧に日時が無いので、Swift は age の判定に要る候補だけ公開日を後から取得する (Swift の項を参照)
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ osv_risk_tradeoff = true
# github = ["example-dev"]
```

ファイルにないキーには、組み込みデフォルト(`age = "1w"`、`osv = true`、`osv_risk_tradeoff = true`、`max_change` の制限なし)が使われます。ファイルを作成・解析できない場合は、警告を表示して組み込みデフォルトを使います。値が不正なキー(`age = "abc"` など)は、警告を表示したうえで、そのキーだけを組み込みデフォルトに戻します。
ファイルにないキーには、組み込みデフォルト(`age = "1w"`、`osv = true`、`osv_risk_tradeoff = true`、`max_change` の制限なし)が使われます。ファイルを作成・読み込み・解析できない場合は、警告を表示して組み込みデフォルトを使います。ファイルは UTF-8 のテキストで書いてください。UTF-8 でないファイル(Windows PowerShell 5.1 の `>` が書く UTF-16 など)も同じく警告します。値が不正なキー(`age = "abc"` など)は、警告を表示したうえで、そのキーだけを組み込みデフォルトに戻します。

既存の設定ファイルに `osv_risk_tradeoff` がなくても、脆弱性を修正する更新は、脆弱性が残る場合も条件付きで許可します。件数の数え方と比較基準は「[脆弱性チェック](usage.ja.md#脆弱性チェックosvdev)」を参照してください。

Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ osv_risk_tradeoff = true
# github = ["example-dev"]
```

A key that is missing from the file falls back to the built-in default (`age = "1w"`, `osv = true`, `osv_risk_tradeoff = true`, no `max_change` limit). If the file cannot be created or parsed, depup prints a warning and uses the built-in defaults. If a single value is invalid (such as `age = "abc"`), depup prints a warning and uses the built-in default for that setting only.
A key that is missing from the file falls back to the built-in default (`age = "1w"`, `osv = true`, `osv_risk_tradeoff = true`, no `max_change` limit). If the file cannot be created, read, or parsed, depup prints a warning and uses the built-in defaults. Write the file as UTF-8 text; a file that is not UTF-8 (such as the UTF-16 that `>` writes in Windows PowerShell 5.1) is reported the same way. If a single value is invalid (such as `age = "abc"`), depup prints a warning and uses the built-in default for that setting only.

Updates that fix existing vulnerabilities may still be allowed under severity and count limits, even when an existing configuration file has no `osv_risk_tradeoff` key. See [Vulnerability Check](usage.md#vulnerability-check-osvdev) for the counting rules and baseline requirements.

Expand Down
57 changes: 49 additions & 8 deletions src/global_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,38 @@ impl GlobalConfig {
return None;
}

let content = std::fs::read_to_string(path).ok()?;
match toml::from_str::<GlobalConfig>(&content) {
match Self::read_from(path) {
Ok(config) => Some(config),
Err(e) => {
eprintln!(
"Warning: failed to parse global config '{}': {}",
path.display(),
e
);
Err(message) => {
eprintln!("Warning: {message}");
None
}
}
}

/// 設定ファイルを読んで解釈する。読めない・UTF-8 でない・TOML として解釈できないときは、
/// 警告の文言を返す (呼び出し側が警告して組み込みの既定値を使う)。以前は
/// `read_to_string(...).ok()?` で読み、読み込みに失敗したファイルや、Windows PowerShell 5.1 の
/// `>` が書く UTF-16 を、警告なしに「設定がない」と同じに扱っていた。`age` を既定より長く
/// していても、黙って既定の age で更新先を選んでいた (#123)。
fn read_from(path: &Path) -> Result<Self, String> {
let bytes = std::fs::read(path).map_err(|error| {
format!("failed to read global config '{}': {error}", path.display())
})?;
let content = String::from_utf8(bytes).map_err(|_| {
format!(
"failed to read global config '{}': the file is not UTF-8 text; save it as UTF-8",
path.display()
)
})?;
toml::from_str::<GlobalConfig>(&content).map_err(|error| {
format!(
"failed to parse global config '{}': {error}",
path.display()
)
})
}

/// 設定の `age` を [`Duration`] に変換する。
///
/// 未指定 or パース失敗の場合は `None`。パース失敗時は警告を出す。
Expand Down Expand Up @@ -249,6 +267,29 @@ pub fn resolve_osv(cli_osv: bool, no_osv: bool, config: Option<&GlobalConfig>) -

#[cfg(test)]
mod tests {
/// Issue #123: 読めない・UTF-8 でないグローバル設定も、解釈できない設定と同じく警告の
/// 文言を返す (読めた内容は、BOM 付きの UTF-8 でも読める)。
#[test]
fn unreadable_or_non_utf8_global_config_is_reported() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("config.toml");
let utf16: Vec<u8> = [0xFEFF_u16]
.into_iter()
.chain("age = \"2w\"\r\n".encode_utf16())
.flat_map(u16::to_le_bytes)
.collect();
std::fs::write(&path, utf16).unwrap();
let error = super::GlobalConfig::read_from(&path).err().unwrap();
assert!(error.contains("not UTF-8"), "{error}");
// 読み込みに失敗したとき (ディレクトリを指すなど)
let error = super::GlobalConfig::read_from(dir.path()).err().unwrap();
assert!(error.starts_with("failed to read global config"), "{error}");
// 同じ内容の UTF-8 (BOM 付き) は読める
std::fs::write(&path, "\u{feff}age = \"2w\"\r\n").unwrap();
let config = super::GlobalConfig::read_from(&path).unwrap();
assert_eq!(config.age.as_deref(), Some("2w"));
}

#[test]
fn age_exempt_normalizes_logins_and_preserves_other_settings_on_errors() {
let config: super::GlobalConfig = toml::from_str("age = '2w'\nosv = true\n[age_exempt]\ngithub = ['EXAMPLE-dev', 'example-dev', 'bad/name']").unwrap();
Expand Down
Loading