

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# バージョン経過時のパッケージのゲート設定
<a name="package-version-age-gating"></a>

**重要**  
バージョン経過時間ゲートは、隔離期間をすでに過ぎている既存のバージョンで検出された脆弱性から保護しません。Amazon Inspector で CodeArtifact を使用して、確立されたパッケージの脆弱性スキャンを行います。

パッケージバージョンがパブリックレジストリに公開されると、レジストリは公開タイムスタンプを記録します。CodeArtifact は、外部接続からパッケージをキャッシュするときに、これらの元のアップストリームタイムスタンプを保持します。これらのタイムスタンプは CI/CD パイプラインで読み取ることができ、最近公開されたバージョンを拒否できます。

ほとんどの悪意のあるパッケージは、公開されてから数時間以内に検出および削除されます。3～7 日間の隔離期間により、依存関係の混乱、管理者アカウントの乗っ取り、入力ミスなど、サプライチェーン攻撃の大部分がブロックされます。

**注記**  
このトピックの例では、CodeArtifact リポジトリ URL をクエリします。これは、パッケージマネージャーがパッケージのダウンロードに使用するエンドポイントと同じです。すべてのリクエストには認証が必要です。`Authorization: Bearer` ヘッダーに有効な CodeArtifact 認可トークンを渡します。トークンを取得するには、 を実行します `aws codeartifact get-authorization-token` ( `--domain`と を使用した完全な呼び出しについては、このトピックの後半にある CI/CD の例を参照してください`--domain-owner`)。  
によって返された `publishedTime`フィールドを経過時間ゲート`aws codeartifact describe-package-version`に使用しないでください。このフィールドは取り込み時に入力されます。元のアップストリーム発行時間がキャプチャされなかったレコード (アーティファクトごとのタイムスタンプキャプチャが導入される前に取り込まれたパッケージ、および特定の取り込みパスを含む) の場合、レコードの最終更新時刻にサイレントにフォールバックします。API レスポンスは、2 つの値のうちどれを読み取っているかを示していないため、アップストリーム公開日として依存することはできません。代わりに、このトピックで説明されている形式ごとのフィールドを使用します。

形式ごとの詳細は異なりますが、ゲートパターンは、解決された各バージョンのアップストリーム発行タイムスタンプを読み、隔離カットオフ (現在の時刻から選択したウィンドウを引いた値) と比較し、いずれかのバージョンがカットオフより新しい場合はビルドを失敗させるというすべての形式で同じです。複数のパッケージ形式を使用する場合は、以下に示す形式固有のフィールドで同じパターンを適用します。

## npm
<a name="package-version-age-gating-npm"></a>

CodeArtifact は、npm Packument `time`フィールドの各バージョンの元の npmjs.org 発行タイムスタンプを返します。多くのパッケージマネージャーとセキュリティツールは、このフィールドをネイティブに読み取ります。これには、Yarn 4 `npmMinimalAgeGate`オプション、Renovate `minimumReleaseAge`設定、pnpm `minimumReleaseAge`設定が含まれます。これらはすべて CodeArtifact で変更なしで動作します。

**リクエスト**:

```
curl -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \
  https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/npm/my-repo/axios
```

**レスポンス (省略):**

```
{
  "name": "axios",
  "time": {
    "1.6.0": "2023-10-26T21:15:55.685Z",
    "1.6.5": "2024-01-05T19:52:15.051Z",
    "1.7.0": "2024-05-19T20:25:03.615Z",
    "1.7.1": "2024-05-20T13:32:52.757Z",
    "1.7.2": "2024-05-21T16:58:04.163Z"
  }
}
```

**キーフィールド:** `time["<version>"]` — 値は npmjs.org からの元の公開日です。たとえば、 は `time["1.7.2"]`を返します。これは`2024-05-21T16:58:04.163Z`、そのバージョンが公開されたときに npmjs.org によって記録された正確なタイムスタンプです。年齢に基づいてゲートを設定するには、この値を現在の時刻から隔離期間を引いた値と比較します。

## Maven
<a name="package-version-age-gating-maven"></a>

Maven アーティファクトの場合、CodeArtifact はファイル (JAR、POM、またはその他のアーティファクト) をダウンロードすると、HTTP `Last-Modified` レスポンスヘッダーに元の Maven Central 発行タイムスタンプを返します。

**リクエスト**:

```
curl -I -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \
  https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/maven/my-repo/\
org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.pom
```

**レスポンスヘッダー:**

```
HTTP/2 200
content-type: application/xml
Last-Modified: Fri, 26 Feb 2021 20:40:52 GMT
content-length: 22672
```

**キーフィールド:** `Last-Modified` header — これは Maven Central からの元の公開日です。隔離カットオフと比較して、アーティファクトが新しすぎるかどうかを判断します。

**注記**  
内の `<lastUpdated>`フィールドは、CodeArtifact がアップストリーム公開日ではなくキャッシュを最後に更新した日時`maven-metadata.xml`を反映します。経過時間ゲートには`maven-metadata.xml`タイムスタンプを使用しないでください。

## NuGet
<a name="package-version-age-gating-nuget"></a>

NuGet パッケージの場合、CodeArtifact は V3 登録レスポンスの `published`フィールドに元の nuget.org 発行タイムスタンプを返します。

**リクエスト**:

```
curl -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \
  https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/nuget/my-repo/\
v3/registration4/newtonsoft.json/index.json
```

**レスポンス (省略形、1 つのバージョンを表示):**

```
{
  "items": [{
    "items": [{
      "catalogEntry": {
        "id": "Newtonsoft.Json",
        "version": "13.0.3",
        "published": "2023-03-08T07:42:54.647+00:00",
        "listed": true
      }
    }]
  }]
}
```

**キーフィールド:** `catalogEntry.published` — これは nuget.org からの元の公開日です。たとえば、 は Newtonsoft.Json 13.0.3 が 2023 年 3 月 8 日に公開された`"2023-03-08T07:42:54.647+00:00"`ことを意味します。

## 貨物 (錆)
<a name="package-version-age-gating-cargo"></a>

crates.io からプルされた貨物クレートの場合、CodeArtifact は元の発行タイムスタンプを crates.io V1 API `versions`レスポンスに保持します。V1 API エンドポイントのクレートをクエリして、バージョンごとの`created_at`タイムスタンプを読み込みます。

**リクエスト**:

```
curl -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \
  https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/cargo/my-repo/\
api/v1/crates/serde/versions
```

**キーフィールド:** V1 `versions`レスポンスのバージョンごとの`created_at`タイムスタンプは、元の crates.io 発行日です。

**注記**  
Cargo は、通常の依存関係解決にスパースインデックスプロトコル (`<prefix>/<crate>`) を使用します。スパースインデックスは`vers`、バージョン`features`ごとに `cksum`、`yanked`、、 を返しますが、発行タイムスタンプは含まれません。Cargo の年齢への依存関係をゲートするには、パイプラインの別のステップで上記の V1 API をクエリします。標準 コマンド`cargo build`と `cargo update` コマンドは V1 API を呼び出しません。

## PyPI
<a name="package-version-age-gating-pypi"></a>

Python パッケージの場合、CodeArtifact は各ディストリビューションファイルの `upload-time`フィールドを含む PEP 691 (JSON Simple API) と PEP 700 をサポートしています。

**リクエスト**:

```
curl -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \
  -H "Accept: application/vnd.pypi.simple.v1+json" \
  https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/pypi/my-repo/simple/requests/
```

**レスポンス (省略):**

```
{
  "files": [{
    "filename": "requests-2.31.0-py3-none-any.whl",
    "upload-time": "2023-05-22T15:12:42.313790Z",
    "size": 62574,
    "url": "../../packages/requests/2.31.0/requests-2.31.0-py3-none-any.whl"
  }]
}
```

**キーフィールド:** `upload-time` — これは PyPI からの元の公開日です。などのツールは、これをネイティブに`uv`サポートします。

```
# Only install versions published before a specific date
uv pip install --exclude-newer 2026-05-01T00:00:00Z -r requirements.txt
```

**注記**  
PEP 691 JSON API には `Accept: application/vnd.pypi.simple.v1+json`ヘッダーが必要です。デフォルトの HTML Simple API にはタイムスタンプは含まれません。  
アーティファクトごとのアップロード時間キャプチャが導入される前に取り込まれたディストリビューションファイル (レガシーレコード) の場合、 のエントリに `upload-time`フィールドがない可能性があります`files[]`。ゲートロジックでは、欠落している を検証できないバージョン`upload-time`として扱い、ポリシーに従って許可またはブロックするかどうかを決定する必要があります。

## 例: CI/CD での npm パッケージのゲート設定
<a name="package-version-age-gating-example"></a>

次の Python スクリプトは、`package-lock.json`ファイルを読み取り、CodeArtifact に各依存関係の発行タイムスタンプをクエリし、隔離期間内にいずれかのバージョンが公開された場合はゼロ以外のステータスで終了します。

```
#!/usr/bin/env python3
"""
gate_package_age.py - Block npm packages published too recently.

Reads package-lock.json, queries CodeArtifact for each dependency's
publish timestamp, and rejects versions newer than QUARANTINE_HOURS.

Environment variables:
  CODEARTIFACT_AUTH_TOKEN  - from `aws codeartifact get-authorization-token`
  CODEARTIFACT_NPM_ENDPOINT - from `aws codeartifact get-repository-endpoint`
  QUARANTINE_HOURS - minimum version age in hours (default: 72)
"""
import json, os, sys, urllib.request
from datetime import datetime, timezone, timedelta

def get_publish_time(endpoint, token, package, version):
    """Fetch the publish timestamp for a specific version from CodeArtifact."""
    url = f"{endpoint.rstrip('/')}/{package}"
    req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"})
    data = json.loads(urllib.request.urlopen(req, timeout=30).read())
    time_str = data.get("time", {}).get(version)
    if not time_str:
        return None
    return datetime.fromisoformat(time_str.replace("Z", "+00:00"))

token = os.environ["CODEARTIFACT_AUTH_TOKEN"]
endpoint = os.environ["CODEARTIFACT_NPM_ENDPOINT"]
quarantine_hours = int(os.environ.get("QUARANTINE_HOURS", "72"))
cutoff = datetime.now(timezone.utc) - timedelta(hours=quarantine_hours)

with open("package-lock.json") as f:
    lockfile = json.load(f)

blocked = []
checked = 0
for path, info in lockfile.get("packages", {}).items():
    if not path.startswith("node_modules/"):
        continue
    name = path.replace("node_modules/", "", 1)
    version = info.get("version")
    if not name or not version:
        continue
    checked += 1
    pub_time = get_publish_time(endpoint, token, name, version)
    if pub_time and pub_time > cutoff:
        age_h = (datetime.now(timezone.utc) - pub_time).total_seconds() / 3600
        blocked.append(f"  {name}@{version} (age: {age_h:.0f}h, published {pub_time.isoformat()})")

if blocked:
    print(f"BLOCKED: {len(blocked)} package(s) newer than {quarantine_hours} hours:")
    print("\n".join(blocked))
    sys.exit(1)
print(f"PASSED: all {checked} packages older than {quarantine_hours} hours.")
```

**CI/CD 統合:**

```
# Step 1: Authenticate
export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
  --domain my-domain --domain-owner 111122223333 \
  --query authorizationToken --output text)

export CODEARTIFACT_NPM_ENDPOINT=$(aws codeartifact get-repository-endpoint \
  --domain my-domain --domain-owner 111122223333 \
  --repository my-repo --format npm \
  --query repositoryEndpoint --output text)

# Step 2: Set quarantine window (72 hours = 3 days)
export QUARANTINE_HOURS=72

# Step 3: Generate lockfile without installing
npm install --package-lock-only

# Step 4: Gate on age — fails if any dependency is too new
python3 gate_package_age.py

# Step 5: If gate passes, install
npm ci
```

**パッケージがブロックされている場合の出力例:**

```
BLOCKED: 1 package(s) newer than 72 hours:
  evil-package@1.0.0 (age: 2h, published 2026-06-03T08:30:00+00:00)
```

**すべてのパッケージが合格した場合の出力例:**

```
PASSED: all 847 packages older than 72 hours.
```

## 攻撃の軽減
<a name="package-version-age-gating-attacks"></a>


| 攻撃のタイプ | 仕組み | 経過時間ゲートがどのように役立つか | 
| --- | --- | --- | 
| 依存関係の混乱 | 攻撃者は、内部パッケージと同じ名前のハイバージョンパッケージをパブリックレジストリに公開します。 | 悪意のあるバージョンはまったく新しいものです。隔離ウィンドウによってブロックされます。 | 
| アカウント乗っ取り | 攻撃者は、保守者の認証情報を侵害し、悪意のある更新 (イベントストリームなど) を発行します。 | 新しいバージョンは保持されます。コミュニティは、隔離期間内に検出して元に戻します。 | 
| タイプクォーティング | 攻撃者は、一般的なパッケージに似た名前のパッケージを公開します (たとえば、 lodashsではなく)lodash。 | タイプクォートパッケージのすべてのバージョンが新規です。すべてのバージョンがブロックされます。 | 
| スタージャック | 攻撃者は、自らが管理する正当なパッケージのパッチリリースにマルウェアを挿入します。 | 悪意のあるパッチは新しく公開されたバージョンです。隔離期間を過ぎるまでブロックされます。 | 

## サポートされている形式の概要
<a name="package-version-age-gating-summary"></a>


| 形式 | 公開タイムスタンプの読み取り場所 | キーフィールド | ステータス | 
| --- | --- | --- | --- | 
| npm | パッケージレスポンス (GET /<package>) | time["<version>"] | 使用可能 | 
| Maven | アーティファクトダウンロード時の HTTP レスポンスヘッダー | Last-Modified | 使用可能 | 
| NuGet | V3 登録インデックス | catalogEntry.published | 使用可能 | 
| 貨物 | V1 API バージョンのレスポンス | created\_at | 使用可能 | 
| PyPI | PEP 691 JSON Simple API | upload-time | 使用可能 | 