

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 外掛程式開發人員指南
<a name="sbomgen-plugin-developer-guide"></a>

 本指南說明如何使用自訂 Lua 外掛程式擴展 Amazon Inspector SBOM 產生器 (inspector-sbomgen)。外掛程式可讓您新增對新套件生態系統的支援，而無需修改 sbomgen 的原始程式碼或重新編譯。

 如需完整的函數目錄，請參閱 [外掛程式 API 參考](sbomgen-plugin-api-reference.md)。如需撰寫測試的指引，請參閱 [外掛程式測試指南](sbomgen-plugin-testing-guide.md)。

## 概觀
<a name="sbomgen-plugin-developer-guide-overview"></a>

 Sbomgen 外掛程式是以 Lua 撰寫，並遵循兩個步驟管道：
+ **探索** — 掃描成品的檔案清單，並報告哪些檔案與您的生態系統相關。
+ **集合** — 剖析每個探索的檔案，並將套件調查結果推送至 SBOM。

### 外掛程式事件模型
<a name="sbomgen-plugin-developer-guide-plugin-event-model"></a>

 探索外掛程式需要一種方式來告知集合外掛程式，在庫存下的成品中發現包含套件中繼資料的檔案。為了促進此資料共用，探索外掛程式會定義**事件名稱**並傳回檔案路徑清單。集合外掛程式會**訂閱**該事件，並接收每個相符的檔案路徑。這會將檔案偵測與剖析分離 — 您可以有一個探索外掛程式饋送多個收集器 （扇出模式）。不過，每個探索外掛程式都必須有唯一的事件名稱，而且每個集合外掛程式都必須有唯一的收集器名稱。如需詳細資訊，請參閱 [外掛程式碰撞規則](#sbomgen-plugin-developer-guide-plugin-collision-rules)。

 開發人員可能會將此視為**觀察者**設計模式。

 此設計可讓單一探索外掛程式以高效能的方式觸發多個獨立分析。例如，一個探索外掛程式可以在成品`requirements.txt`中找到每個 ，然後饋送：
+ **套件收集器**，會將每一行剖析為 SBOM 問題清單 (`name==version`)。
+ **秘密收集器**，會標記包含 API 金鑰或字符的行，意外鎖定為版本。
+ 報告未鎖定或萬用字元版本指標**的政策收集**器。

 每個收集器會根據相同的檔案清單獨立執行，而不會重新探索成品的檔案系統。這也可讓外掛程式作者新增訂閱現有事件的新集合外掛程式，而無需變更對應的探索外掛程式。

## 快速入門：建立新的外掛程式
<a name="sbomgen-plugin-developer-guide-quick-start-creating-new-plugins"></a>

 建立新外掛程式的最快方法是使用內建 scaffolding 命令：

```
inspector-sbomgen plugin new
```

 命令會提示外掛程式名稱和專案目錄。按下 Enter 接受括號中顯示的預設值：

```
Plugin name (identifies the software ecosystem your plugin will inventory, e.g. debian-dpkg, rhel-rpm, python-pip, cmake) [my-custom-ecosystem]: cmake
Project directory [my-sbomgen-plugins]:
```

 您也可以直接傳遞引數：

```
inspector-sbomgen plugin new --name cmake --path /tmp/custom-plugins
```

### 從工作範例開始
<a name="sbomgen-plugin-developer-guide-starting-with-a-working-example"></a>

 如果您想要在編寫自己的邏輯之前先實驗外掛程式，請使用 `--with-example`旗標建立新的外掛程式：

```
inspector-sbomgen plugin new --with-example
```

 這會產生具有範例 lockfile 剖析器、測試資料和傳遞測試的正常運作外掛程式專案。此範例外掛程式會探索`example.lock`檔案、剖析`name==version`項目，並將套件推送至 SBOM。您可以立即執行測試以查看執行中的外掛程式系統，然後修改程式碼以鎖定您的實際生態系統。

 對於包含所有選用覆寫函數 (`get_scanner_name`、`get_event_name`、`get_scanner_groups`、多事件探索等） 的進階堆疊，請使用 `--with-overrides`旗標 （稍後會詳細說明）：

```
inspector-sbomgen plugin new --with-overrides
```

### 完成您的外掛程式
<a name="sbomgen-plugin-developer-guide-completing-your-plugin"></a>

 堆疊後，產生的外掛程式檔案會包含`TODO`標記，指出在何處新增生態系統特定的邏輯。完成這些標記，將 scaffold 轉換為工作中的外掛程式：
+ 以您的實際值取代所有`TODO`標記
+ 更新 中的檔案模式`discover()`，以符合您的目標檔案
+ 在 中實作剖析邏輯`collect()`，並`sbomgen.push_package()`呼叫每個套件

### 測試您的外掛程式
<a name="sbomgen-plugin-developer-guide-test-your-plugin"></a>

 有兩種方式可以測試您的外掛程式：

 **1。內建測試機制** — `inspector-sbomgen plugin test`用於在開發期間驗證外掛程式邏輯。這會執行您的`init_test.lua`測試檔案，而不需要真正的成品來掃描：

```
inspector-sbomgen plugin test --path ./my-plugins
```

 如需撰寫測試檔案和使用 `testing.*` API [外掛程式測試指南](sbomgen-plugin-testing-guide.md) 的詳細資訊，請參閱 。

 **2. End-to-end掃描** — 使用標準 sbomgen 命令調用您的外掛程式，以驗證它對真實成品是否有效。對於此方法，您需要提供包含外掛程式目標檔案 （例如具有 `requirements.txt`或 對等項目的目錄） 和外掛程式目錄路徑的成品：

```
inspector-sbomgen directory \
    --path /path/to/test/dir \
    --plugin-dir ./my-plugins \
    --disable-native-scanners \
    -o sbom.json
```

 `--disable-native-scanners` 旗標可確保只有 Lua 外掛程式執行，因此無需內建 （原生） 掃描器的輸出，即可更輕鬆地進行測試。

## IDE 設定
<a name="sbomgen-plugin-developer-guide-ide-setup"></a>

 Sbomgen 提供 VS 程式碼中整個 `sbomgen.*` API 的程式碼完成、類型檢查和內嵌文件。

### 搭配 Lua Language Server 的 VS 程式碼
<a name="sbomgen-plugin-developer-guide-vs-code-with-lua-language-server"></a>
+ 安裝 Lua 延伸模組： [sumneko.lua](https://marketplace.visualstudio.com/items?itemName=sumneko.lua)
+ 開啟外掛程式專案中的任何`.lua`檔案

 這樣就大功告成了！`plugin new` 命令會產生 `library/sbomgen.lua` `.vscode/settings.json`，並由 Lua Language Server 自動偵測。您將立即取得：
+ 所有`sbomgen.*`函數的程式碼完成
+ 具有 類型的參數提示
+ 暫留文件
+ 類型檢查

## 外掛程式目錄結構
<a name="sbomgen-plugin-developer-guide-plugin-directory-structure"></a>

 Sbomgen 探索和集合外掛程式必須遵循下列目錄結構：

```
{plugin-dir}/
├── discovery/
│   └── {platform}/
│       └── {category}/
│           └── {ecosystem}/
│               └── init.lua          # REQUIRED entrypoint
└── collection/
    └── {platform}/
        └── {category}/
            └── {ecosystem}/
                └── init.lua          # REQUIRED entrypoint
```

 這些目錄名稱具有語意意義：sbomgen 會使用它們來衍生外掛程式的預設中繼資料，包括掃描器名稱、事件名稱、掃描器群組和平台篩選。這可減少開發人員必須寫入的樣板數量。選擇正確的值可確保您的外掛程式與 sbomgen 的掃描器選擇和執行模型正確整合。

 以下各節會更詳細地探索目錄結構，提供語意意義和慣例的指引。

### 平台
<a name="sbomgen-plugin-developer-guide-platform"></a>

 平台目錄會控制外掛程式執行所在的作業系統。


| **Value** | **使用情況** | 
| --- | --- | 
| cross-platform | 外掛程式適用於任何作業系統 （大多數外掛程式） | 
| linux | Linux 特定的偵測邏輯 | 
| windows | Windows 特定的偵測邏輯 | 
| macos | macOS 特定的偵測邏輯 | 

### Category
<a name="sbomgen-plugin-developer-guide-category"></a>

 類別目錄會決定指派給外掛程式的預設掃描器群組，這會控制它是否依預設執行，或需要明確選擇加入。如需 群組如何影響執行[掃描器選擇](#sbomgen-plugin-developer-guide-scanner-selection)的詳細資訊，請參閱 。


| **Value** | **預設群組** | **使用情況** | 
| --- | --- | --- | 
| proglang | programming-language-packages, pkg-scanner | 程式設計語言套件 (pip、npm、maven 等） | 
| os | os, pkg-scanner | 作業系統套件管理員 (dpkg、rpm、apk 等） | 
| extra-ecosystems | extra-ecosystems, pkg-scanner | 應用程式和執行時間 (nginx、curl、wordpress 等） | 

 如果您使用的類別名稱不符合上述任何項目，則會使用類別名稱本身做為群組。

### 生態系統
<a name="sbomgen-plugin-developer-guide-ecosystem"></a>

 特定套件生態系統的名稱 `python-pip` `python-poetry`（例如 、、`debian-dpkg`、`curl`)。連字號是常見的慣例，但不是嚴格的要求。

 掃描器名稱和收集器名稱直接衍生自生態系統目錄名稱。

### 事件名稱配對
<a name="sbomgen-plugin-developer-guide-event-name-pairing"></a>

 相同目錄路徑的探索和集合外掛程式會自動配對。例如， 的探索外掛程式`discovery/cross-platform/proglang/python-pip/`會自動與 配對`collection/cross-platform/proglang/python-pip/`。

 您可以在外掛程式`subscribe_to_event()`中定義 `get_event_name()`和 來覆寫此項目。

## 探索外掛程式
<a name="sbomgen-plugin-developer-guide-discovery-plugins"></a>

 探索外掛程式只需要 `discover()`函數。所有其他函數都是選用的 - 預設值衍生自目錄路徑。

 大多數探索外掛程式的運作方式是尋找名稱或路徑可識別特定生態系統的檔案，例如 `requirements.txt` Python pip、npm `package.json` 或 `Cargo.lock` Rust 貨物。`sbomgen.find_files_by_*` 函數在 Lua VM 之外執行此比對，這使得它們比在 Lua 中重複完整檔案清單明顯更快：

```
-- REQUIRED: Scans the artifact and returns a table of file paths.
function discover()
    return sbomgen.find_files_by_name({"requirements.txt"})
end
```

 `discover()` 必須傳回字串的 Lua 資料表 （陣列）。如果找不到檔案，請傳回空白資料表 `{}`。

### 常見探索模式
<a name="sbomgen-plugin-developer-guide-common-discovery-patterns"></a>


| **目標** | **建議的 函數** | 
| --- | --- | 
| 比對一或多個確切的檔案名稱 | sbomgen.find\_files\_by\_name({names}) | 
| 不區分大小寫的相符檔案名稱 | sbomgen.find\_files\_by\_name\_icase({names}) | 
| 依路徑尾碼比對 （例如 /pom.properties) | sbomgen.find\_files\_by\_suffix({suffixes}) | 
| 依全路徑 regex 比對 | sbomgen.find\_files\_by\_path\_regex({patterns}) | 
| Glob 樣式的 basename 比對 （例如 \*.lock) | sbomgen.glob\_find\_files(pattern) | 

 當您的邏輯需要篩選後 - 例如，保持檔案符合尾碼，但不包含建置輸出目錄 - 將`find_files_by_*`呼叫與 Lua 迴圈結合：

```
function discover()
    local found = {}
    for _, f in ipairs(sbomgen.find_files_by_suffix({".conda-meta.json"})) do
        if not f:match("[/\\]%.cache[/\\]") then
            table.insert(found, f)
        end
    end
    return found
end
```

 除非沒有其他配對器適合，否則請避免`sbomgen.get_file_list()`探索 - 它會將每個路徑複製到 Lua VM，並在大型成品上花費幾秒鐘的時間。如需詳細資訊[外掛程式 API 參考](sbomgen-plugin-api-reference.md)，請參閱 。

### 多事件探索
<a name="sbomgen-plugin-developer-guide-multi-event-discovery"></a>

 根據預設， 傳回的所有檔案`discover()`都會發佈至單一事件 （來自 `get_event_name()`)。如果您的掃描器需要將不同的檔案路由到不同的收集器，請改為傳回金鑰資料表：

```
function discover()
    return {
        EventNameFoundCurl = sbomgen.find_files_by_name({"curl", "curl.exe"}),
        EventNameFoundLibcurl = sbomgen.find_files_by_name({"curlver.h"}),
    }
end
```

 當 `discover()`傳回具有字串索引鍵的資料表時，會將每個索引鍵視為個別的事件名稱，且其值 （檔案路徑的資料表） 會發佈至該事件。集合外掛程式`subscribe_to_event()`會照常透過 訂閱特定事件。

 這與回溯相容 - 傳回循序資料表`{"file1", "file2"}`仍可做為單一事件模式運作。偵測是自動的：具有任何字串索引鍵的資料表是多事件，只有整數索引鍵 （或空白） 的資料表是單一事件。

 使用多事件時， `get_event_name()` 不會用於發佈 （事件名稱來自傳回的資料表金鑰）。不過，在載入外掛程式以進行碰撞偵測時仍會呼叫它，因此它應該傳回唯一值或省略使用預設值。

### 選用探索函數
<a name="sbomgen-plugin-developer-guide-optional-discovery-functions"></a>

 所有這些都具有衍生自目錄路徑的 Sane 預設值。只有在您需要覆寫時才定義它們：


| **函數** | **預設** | **覆寫時機...** | 
| --- | --- | --- | 
| get\_scanner\_name() | {ecosystem} （例如 python-pip) | 您想要自訂掃描器名稱 | 
| get\_scanner\_description() | "Lua discovery plugin: {ecosystem}" | 您想要自訂描述 | 
| get\_scanner\_groups() | 衍生自類別目錄 | 您需要非標準群組 | 
| get\_event\_name() | 從目錄路徑衍生 | 您需要自訂事件路由 | 
| get\_localhost\_scan\_paths() | 無 | 您的外掛程式需要在localhost掃描期間掃描的特定路徑 | 

### Localhost 掃描路徑
<a name="sbomgen-plugin-developer-guide-localhost-scan-paths"></a>

 當 sbomgen 執行`localhost`掃描時，它會行走使用者指定的目錄，加上掃描器宣告的任何預設路徑。根據預設，Lua 探索外掛程式不會提供任何路徑，因此使用者指定目錄以外的檔案不會出現在檔案清單中。

 定義 `get_localhost_scan_paths()` 以傳回 localhost walker 應包含的目錄或檔案路徑：

```
function get_localhost_scan_paths()
    return {
        "/usr/bin",
        "/usr/local/bin",
    }
end
```

 傳回的路徑只會在`localhost`掃描期間附加到助行器的掃描清單，不會影響 `container`、 `directory`或 `archive`掃描。

### 平台特定的掃描路徑
<a name="sbomgen-plugin-developer-guide-platform-specific-scan-paths"></a>

 當您在 Windows、macOS 和 Linux 上的不同位置即時處理檔案時， 上的 分支`sbomgen.get_platform()`並傳回主機的適當路徑：

```
function get_localhost_scan_paths()
    local platform = sbomgen.get_platform()

    if platform == sbomgen.platform.WINDOWS then
        local drive = sbomgen.get_system_drive()
        return {
            drive .. "/Program Files/MyApp/myapp.exe",
            drive .. "/Program Files (x86)/MyApp/myapp.exe",
        }
    end

    if platform == sbomgen.platform.DARWIN then
        return {"/Applications/MyApp.app/Contents/MacOS/myapp"}
    end

    -- Linux
    return {
        "/usr/bin/myapp",
        "/usr/local/bin/myapp",
    }
end
```

 在 Windows 上，使用 `sbomgen.get_system_drive()`解析系統磁碟機代號 （例如 `"C:"`)，而不是硬式編碼。對於衍生自環境變數的路徑，例如 `LOCALAPPDATA`或 `PROGRAMFILES`，重複`sbomgen.get_env_vars()`並依索引鍵查詢值。如需詳細資訊[外掛程式 API 參考](sbomgen-plugin-api-reference.md)，請參閱 。

## 集合外掛程式
<a name="sbomgen-plugin-developer-guide-collection-plugins"></a>

 集合外掛程式只需要 `collect()`函數。所有其他函數都是選用的。

 `collect(file_path)` 會在配對探索外掛程式發現的每個檔案呼叫一次。典型模式為：
+ 使用 ****`sbomgen.read_file()`（適用於載入記憶體的小型檔案） 或 `sbomgen.open_file()`（適用於line-by-line讀取的大型檔案） 讀取檔案的內容。
+ **剖析**內容：簡單資訊清單、`sbomgen.json_decode()`JSON、`sbomgen.xml_decode()`XML 或編譯二進位檔`sbomgen.search_binary()`的字串比對。
+ `sbomgen.push_package()` 使用套件的中繼資料呼叫 ****，發佈每個探索到的套件。

```
-- REQUIRED: Called once per discovered file.
-- Parse the file and call sbomgen.push_package() for each package found.
function collect(file_path)
    local content, err = sbomgen.read_file(file_path)
    if err or not content then return end

    for line in content:gmatch("[^\r\n]+") do
        local name, version = line:match("^([%w%-%_%.]+)==(.+)$")
        if name and version then
            sbomgen.push_package({
                name = name,
                version = version,
                purl_type = "pypi",
                component_type = sbomgen.component_types.LIBRARY,
            })
        end
    end
end
```

 `collect()` 不會傳回值。每次`push_package()`呼叫都需要 `name`、 `purl_type`和 `component_type`。如需所有支援的欄位[外掛程式 API 參考](sbomgen-plugin-api-reference.md)，請參閱 。

### 將中繼資料連接至元件
<a name="sbomgen-plugin-developer-guide-attaching-metadata-to-components"></a>

 Sbomgen 支援兩種將中繼資料連接至套件元件的方式：**PURL 限定詞**和 **CycloneDX 屬性**。它們有不同的用途，而且它們之間的選擇會影響 Amazon Inspector 如何識別產生的 SBOM 中的漏洞。


| **Mechanism** | **出現的位置** | **使用 進行** | 
| --- | --- | --- | 
| qualifiers | 在套件 URL 內 （例如 pkg:deb/debian/curl@7.88.1?arch=amd64) | 屬於套件身分的資料 | 
| properties | 在 SBOM 的components[].properties陣列中 | 不會變更套件識別方式的描述性中繼資料 | 

 **建議：偏好自訂中繼資料的 CycloneDX 屬性 （在您自己的命名空間下）。**屬性不會變更元件的身分，因此它們不會影響 Amazon Inspector 的漏洞識別。針對生態系統的 PURL 類型需要的案例，保留 PURL 限定詞。

#### PURL 限定詞
<a name="sbomgen-plugin-developer-guide-purl-qualifiers"></a>

 有些 PURL 限定詞對 Amazon Inspector 有語意意義，並影響漏洞識別。例如，在`deb`元件上 Inspector 使用 `arch`和 等限定詞`distro`來選取正確的漏洞饋送；在編譯二進位檔的`generic`元件上，使用 `go_toolchain`或 等限定詞來`rust_toolchain`識別使用的工具鏈。設定限定詞 Inspector 無法辨識或省略其預期值，可能會導致遺漏或錯誤歸因漏洞。

 請參閱[什麼是套件 URL？](https://docs.aws.amazon.com/inspector/latest/user/sbom-generator-purl-sbom.html) 在限定詞慣例的 Amazon Inspector 使用者指南中， Inspector 會辨識每個 PURL 類型。

 透過 上的`qualifiers`資料表設定限定詞`sbomgen.push_package()`：

```
sbomgen.push_package({
    name = "curl",
    version = "7.88.1",
    purl_type = "deb",
    namespace = "debian",
    component_type = sbomgen.component_types.LIBRARY,
    qualifiers = {
        arch = "amd64",
        distro = "debian-12",
    },
})
```

 只有在限定詞符合 Inspector 對 PURL 類型的期望時，才能設定限定詞。如果您需要記錄不屬於套件身分的中繼資料，請改用 CycloneDX 屬性。

#### CycloneDX 屬性
<a name="sbomgen-plugin-developer-guide-cyclonedx-properties"></a>

 CycloneDX 屬性是出現在 SBOM `components[].properties`陣列中的鍵值註釋。它們描述元件，而不會影響其識別方式，因此它們是外掛程式定義中繼資料的安全選擇。

 **`amazon:inspector:*`命名空間會保留給 Amazon Inspector。**具體而言：
+ `amazon:inspector:sbom_generator:*` — 保留給 sbomgen 及其內建掃描器。
+ `amazon:inspector:sbom_scanner:*` — 預留給 Amazon Inspector Scan API。

 外掛程式作者不得在這些預留命名空間內發出金鑰。寫入它們可能會干擾 Inspector 的行為，並且可能會遭到覆寫。如需預留金鑰的完整清單，請參閱[搭配 Amazon Inspector 使用 CycloneDX 命名空間](https://docs.aws.amazon.com/inspector/latest/user/cyclonedx-namespace.html)。

 定義屬性時，請使用您自己的命名空間 （通常是您的組織或外掛程式識別符）：

```
sbomgen.push_package({
    name = "requests",
    version = "2.28.1",
    purl_type = "pypi",
    component_type = sbomgen.component_types.LIBRARY,
    properties = {
        ["acme:python:manifest_path"] = file_path,
        ["acme:python:pinned"] = "true",
        ["acme:python:source"] = "requirements.txt",
    },
})
```

#### 金鑰命名規則
<a name="sbomgen-plugin-developer-guide-key-naming-rules"></a>

 屬性金鑰由 sbomgen 處理，如下所示：
+ 在 SBOM 中逐字使用**包含冒號**的金鑰。請務必在金鑰中包含至少一個冒號，以便控制命名空間。
+ **不包含冒號**的金鑰會自動加上字首 `amazon:inspector:sbom_generator:` - 將其放在預留的 Inspector 命名空間中。避免自訂屬性的此形狀。

```
properties = {
    ["acme:my_plugin:detected_via"] = "lockfile",  -- used as-is (recommended)
    detected_via                   = "lockfile",  -- becomes "amazon:inspector:sbom_generator:detected_via" (avoid)
}
```

 `sbomgen.properties.*` 常數存在，因此官方掃描器會在預留命名空間內發出一致的金鑰。它們不是自訂外掛程式的延伸點，請改用您自己的命名空間。

#### 子元件上的屬性和限定詞
<a name="sbomgen-plugin-developer-guide-properties-and-qualifiers-on-child-components"></a>

 巢狀`children`是獨立的元件。每個子系都有自己的 `properties`和 `qualifiers`資料表；父系上設定的中繼資料不會傳播到子系。在每個需要它們的子系上明確設定值。

### 選用集合函數
<a name="sbomgen-plugin-developer-guide-optional-collection-functions"></a>


| **函數** | **預設** | **覆寫時機...** | 
| --- | --- | --- | 
| get\_collector\_name() | {ecosystem} （例如 python-pip) | 您想要自訂收集器名稱 | 
| get\_collector\_description() | 空字串 | 您想要描述 | 
| subscribe\_to\_event() | 從目錄路徑衍生 | 您需要自訂事件路由 | 

## 執行您的外掛程式
<a name="sbomgen-plugin-developer-guide-running-your-plugins"></a>

 若要讓外掛程式產生套件中繼資料，必須提供 sbomgen 成品來掃描，其中包含外掛程式目標的檔案 （例如具有 `requirements.txt`、 `package.json`或同等套件資訊清單檔案的目錄）。

### 基本使用
<a name="sbomgen-plugin-developer-guide-basic-usage"></a>

```
inspector-sbomgen <artifact type> <arguments> --plugin-dir /path/to/plugins
```

 範例：

```
inspector-sbomgen directory --path /target -o /tmp/sbom.json --plugin-dir /path/to/plugins
```

### 停用原生掃描器 （僅限 Lua 模式）
<a name="sbomgen-plugin-developer-guide-with-native-scanners-disabled-lua-only-mode"></a>

```
inspector-sbomgen directory --path /target --plugin-dir /path/to/plugins --disable-native-scanners -o sbom.json
```

### 使用詳細記錄
<a name="sbomgen-plugin-developer-guide-with-verbose-logging"></a>

```
inspector-sbomgen directory --path /target --plugin-dir /path/to/plugins --verbose -o sbom.json
```

## 列出可用的掃描器
<a name="sbomgen-plugin-developer-guide-listing-available-scanners"></a>

 使用 `list-scanners` 查看 sbomgen 可用的每個掃描器。這包括內建原生掃描器、任何與 sbomgen 綁定的官方 Lua 外掛程式，以及您透過 提供的任何自訂 Lua 外掛程式`--plugin-dir`：

```
inspector-sbomgen list-scanners --plugin-dir /path/to/plugins
```

```
┌─────────────────────┬────────┬───────────────────────────────┬─────────────────────────────┐
│    SCANNER NAME     │ SOURCE │            GROUPS             │         DESCRIPTION         │
├─────────────────────┼────────┼───────────────────────────────┼─────────────────────────────┤
│ curl                │ custom │ extra-ecosystems              │ Discovers curl version      │
│                     │        │ pkg-scanner                   │ header files (curlver.h)    │
├─────────────────────┼────────┼───────────────────────────────┼─────────────────────────────┤
│ python-requirements │ custom │ pkg-scanner                   │ Discovers requirements*.txt │
│                     │        │ programming-language-packages │ files for Python pip        │
│                     │        │                               │ packages                    │
└─────────────────────┴────────┴───────────────────────────────┴─────────────────────────────┘
```

 SOURCE 欄顯示每個掃描器的來源：


| **來源** | **意義** | 
| --- | --- | 
| native | 與 sbomgen 綁定的內建掃描器 | 
| official | 與 sbomgen 綁定的 Lua 外掛程式 | 
| custom | 透過 載入的使用者提供的 Lua 外掛程式 --plugin-dir | 

 在`list-scanners`未同時`--plugin-dir`包含 `native`和 `official`掃描器的情況下執行 - 這些一律可用。`--plugin-dir` 旗標會將您的`custom`掃描器新增至清單。

 僅列出不含原生掃描器的 Lua 掃描器：

```
inspector-sbomgen list-scanners --plugin-dir /path/to/plugins --disable-native-scanners
```

## 掃描器選擇
<a name="sbomgen-plugin-developer-guide-scanner-selection"></a>

 Lua 探索外掛程式與內建原生掃描器參與相同的掃描器選擇模型。根據預設，sbomgen 會執行群組符合成品類型預設掃描器群組的所有掃描器。您可以使用三個旗標來覆寫此項目：

### 僅執行特定掃描器
<a name="sbomgen-plugin-developer-guide-run-only-specific-scanners"></a>

 `--scanners` 使用 僅執行具名掃描器。排除所有其他掃描器：

```
inspector-sbomgen directory --path /target \
    --plugin-dir /path/to/plugins \
    --scanners python-requirements \
    -o sbom.json
```

 這只會執行`python-requirements`掃描器。您可以傳遞以逗號分隔的多個掃描器名稱，或傳遞掃描器群組名稱 （例如 `programming-language-packages`)，以啟用屬於該群組的每個掃描器。

### 排除特定掃描器
<a name="sbomgen-plugin-developer-guide-exclude-specific-scanners"></a>

 使用 `--skip-scanners` 在執行其他項目時排除具名掃描器：

```
inspector-sbomgen directory --path /target \
    --plugin-dir /path/to/plugins \
    --skip-scanners python-poetry \
    -o sbom.json
```

 這會執行 以外的每個預設掃描器`python-poetry`。如同 `--scanners`，此旗標也接受群組名稱，因此傳遞會`--skip-scanners programming-language-packages`停用該群組中的每個掃描器。

**注意**  
`--scanners` 和 `--skip-scanners` 是互斥的。同時傳遞兩者會產生錯誤。

### 從非預設群組新增掃描器
<a name="sbomgen-plugin-developer-guide-add-scanners-from-non-default-groups"></a>

 預設掃描器集取決於要掃描的成品類型 （請參閱[群組如何影響選擇](#sbomgen-plugin-developer-guide-how-groups-affect-selection)下面的矩陣）。除非您選擇加入，否則其群組不屬於成品類型預設設定的掃描器將不會執行。使用 `--additional-scanners`將掃描器附加至預設設定，而不取代它：

```
inspector-sbomgen directory --path /target \
    --plugin-dir /path/to/plugins \
    --additional-scanners my-extra-scanner \
    -o sbom.json
```

 這會針對成品類型執行每個預設掃描器，加上 `my-extra-scanner`。旗標接受以逗號分隔的掃描器名稱或群組名稱清單，以及具有預設設定的堆疊，而不是取代它。使用 `list-scanners` 檢查掃描器所屬的群組。

### 群組如何影響選擇
<a name="sbomgen-plugin-developer-guide-how-groups-affect-selection"></a>

 探索外掛程式中的 `get_scanner_groups()`函數會決定掃描器所屬的群組。根據預設，掃描器是否執行取決於其群組和要掃描的成品類型。下面的矩陣顯示每個成品類型的預設掃描器集中包含哪些群組：


| **Group (分組)** | **`directory` / `archive`** | **`container`** | **`localhost`** | **`volume`** | **`binary`** | 
| --- | --- | --- | --- | --- | --- | 
| os | — | ✓ | ✓ | ✓ | — | 
| programming-language-packages | ✓ | ✓ | ✓ | ✓ | — | 
| binary | ✓ | ✓ | — | — | ✓ | 
| extra-ecosystems | — | ✓ | ✓ | ✓ | — | 
| dockerfile | ✓ | ✓ | — | — | — | 
| custom | ✓ | ✓ | ✓ | ✓ | ✓ | 
| certificate | — | — | — | — | — | 
| machine-learning | — | — | — | — | — | 
| pkg-scanner | — | — | — | — | — | 

 ✓ 表示該群組中的每個掃描器預設會針對該成品類型執行。`—` 表示群組不在預設設定中，因此其掃描器只有在透過 `--scanners`或 明確選取時才執行`--additional-scanners`。

 重要詳細資訊：
+ **`custom`** 一律在預設設定中 — 透過 載入的自訂外掛程式`--plugin-dir`會自動接收`custom`群組，因此無論成品類型為何，預設都會執行這些外掛程式。
+ **`extra-ecosystems`** `container`、 `localhost`和 `volume`掃描預設為 ，但 `directory`、 `archive`或 `binary`掃描則預設為 。對於這些類型，您必須傳遞 `--additional-scanners`（依名稱或依`extra-ecosystems`群組） 來包含它們。
+ **`pkg-scanner`** 是資訊性的：它會將掃描器標記為套件收集器，以在 中顯示`list-scanners`，但本身不會導致掃描器執行。將其與 中的執行群組 （例如 `programming-language-packages`) 配對。 `get_scanner_groups()`

 例如，傳回的外掛程式`{sbomgen.groups.EXTRA_ECOSYSTEMS, sbomgen.groups.PACKAGE_COLLECTOR}`預設會在容器、localhost 和磁碟區掃描上執行，但在目錄、封存和二進位掃描上需要 `--additional-scanners`（或 `--scanners`)。

## 外掛程式碰撞規則
<a name="sbomgen-plugin-developer-guide-plugin-collision-rules"></a>

 Sbomgen 會強制執行所有載入外掛程式的唯一中繼資料，以防止靜音覆寫並確保 SBOM 完整性。偵測到碰撞時，會**略過**稍後的外掛程式，並記錄警告。

### 檢查的項目
<a name="sbomgen-plugin-developer-guide-what-is-checked"></a>


| **中繼資料** | **Scope (範圍)** | **碰撞時** | 
| --- | --- | --- | 
| 探索事件名稱 (get\_event\_name) | 所有探索外掛程式 | 已略過第二個外掛程式 | 
| 掃描器名稱 (get\_scanner\_name) | 所有探索外掛程式 | 已略過第二個外掛程式 | 
| 收集器名稱 (get\_collector\_name) | 所有集合外掛程式 | 已略過第二個外掛程式 | 

### 允許的項目
<a name="sbomgen-plugin-developer-guide-what-is-allowed"></a>

 多個集合外掛程式**可以透過** 訂閱相同的事件`subscribe_to_event()`。這是預期的廣發模式 — 一個探索外掛程式可以饋送多個收集器，每個收集器都會執行不同的動作 （例如，一個擷取套件，另一個偵測秘密）。

### 避免碰撞
<a name="sbomgen-plugin-developer-guide-avoiding-collisions"></a>

 如果兩個外掛程式使用相同的掃描器名稱、事件名稱或收集器名稱，則會略過載入的第二個外掛程式。若要解決衝突，請在外掛程式 (`get_scanner_name()`、 或 ) 中定義適當的覆寫函數`get_event_name()`，以重新命名衝突的中繼資料`get_collector_name()`。

### 碰撞警告範例
<a name="sbomgen-plugin-developer-guide-collision-warning-example"></a>

```
[custom:python-pip] SKIPPED: discovery event name "EventNameFoundPythonRequirements"
is already registered by [official:python-pip]. Each discovery plugin must have a
unique event name. Rename get_event_name() in your plugin to use a unique name.
```

 警告會告訴您略過了哪些外掛程式、碰撞了哪些外掛程式、哪些外掛程式已經擁有該名稱，以及要變更哪個函數。

## 除錯
<a name="sbomgen-plugin-developer-guide-debugging"></a>

### 主控台記錄
<a name="sbomgen-plugin-developer-guide-console-logging"></a>

 外掛程式可以使用下列函數將訊息傳送到 sbomgen 的主控台輸出：


| **函數** | **關卡** | **預設可見？** | 
| --- | --- | --- | 
| sbomgen.log\_debug(message) | DEBUG | 否 — 需要 --verbose | 
| sbomgen.log\_info(message) | INFO | 是 | 
| sbomgen.log\_warn(message) | WARN | 是 | 
| sbomgen.log\_error(message) | ERROR | 是 | 

 來自外掛程式的所有日誌輸出都會以外掛程式的來源和路徑 （例如 `[custom:python-pip]`) 自動加上字首，因此來自不同外掛程式的訊息很容易區分。`log_info`、 `log_warn`和 `log_error`一律列印；`log_debug`只有在使用 叫用 sbomgen 時才會列印`--verbose`。

```
function discover()
    sbomgen.log_info("starting discovery")
    local files = sbomgen.find_files_by_name({"requirements.txt"})
    sbomgen.log_debug(string.format("matched %d files", #files))
    if #files == 0 then
        sbomgen.log_warn("no requirements.txt files found")
    end
    return files
end
```

### 中斷點
<a name="sbomgen-plugin-developer-guide-breakpoints"></a>

 使用 `sbomgen.breakpoint()`暫停外掛程式執行和封鎖，直到您按下 Enter 鍵為止。這充當粗略偵錯工具 — 將其與日誌陳述式結合，以檢查特定點的狀態。

```
function discover()
    local files = sbomgen.find_files_by_name({"requirements.txt"})

    sbomgen.log_info(string.format("about to inspect %d files", #files))
    sbomgen.breakpoint("before file inspection — press Enter to continue")

    local found = {}
    for _, f in ipairs(files) do
        if not f:match("[/\\]tests[/\\]") then
            table.insert(found, f)
        end
    end

    sbomgen.log_info(string.format("kept %d files after filtering", #found))
    sbomgen.breakpoint("after filtering — press Enter to continue")

    return found
end
```

 中斷點訊息會列印到 stderr。執行會暫停，直到您按下 Enter 為止，讓您有時間檢閱日誌輸出。

### 常見問題
<a name="sbomgen-plugin-developer-guide-common-issues"></a>


| **徵狀** | **原因** | **修正** | 
| --- | --- | --- | 
| 外掛程式未載入 | 缺少 init.lua | 確保進入點存在於正確的目錄深度 | 
| 「缺少必要的 函數」 | 函數名稱中的 Typo | 檢查subscribe\_to\_event是否已定義 get\_scanner\_name、get\_scanner\_description、get\_scanner\_groups、discoverget\_event\_name、get\_localhost\_scan\_paths、get\_collector\_name、collect、、 | 
| 集合外掛程式從未呼叫 | 事件名稱不相符 | 驗證get\_event\_name()並subscribe\_to\_event()傳回相同的字串 | 
| SBOM 中沒有套件 | push\_package 未呼叫或缺少必要欄位 | 確保在每次push\_package呼叫中component\_type設定 purl\_type、 name和 （包括子系）。使用sbomgen.component\_types.\*常數。 | 
| 外掛程式中的執行時間錯誤 | 執行期間的 Lua 錯誤 | 使用錯誤詳細資訊檢查 sbomgen 輸出是否有警告訊息 | 
| 「SKIPPED：探索事件名稱 ... 已註冊」 | 另一個外掛程式使用相同的事件名稱 | 重新命名get\_event\_name()為唯一值 | 
| 「SKIPPED：掃描器名稱 ... 已註冊」 | 另一個外掛程式使用相同的掃描器名稱 | 重新命名get\_scanner\_name()為唯一值 | 
| 「SKIPPED：收集器名稱 ... 已註冊」 | 另一個外掛程式使用相同的收集器名稱 | 重新命名get\_collector\_name()為唯一值 | 

## API 參考
<a name="sbomgen-plugin-developer-guide-api-reference"></a>

 完整的函數目錄會維護在配套文件中：

 **→ [外掛程式 API 參考](sbomgen-plugin-api-reference.md)** 

 API 參考涵蓋每個`sbomgen.*`函數 （檔案 I/O、二進位公用程式、套件輸出、regex、結構化剖析、Windows 登錄檔、記錄、偵錯）、測試檔案中可用的 `testing.*` API、所有內建常數 (`properties`、`groups`、`component_types`、`platform`) 和外掛程式生命週期全域。

## 錯誤處理
<a name="sbomgen-plugin-developer-guide-error-handling"></a>

 可能失敗的 API 函數會傳回兩個值：`value, err`。成功時， `err`為 `nil`。失敗時，第一個值是 `nil`，而 `err`是錯誤字串。

```
local content, err = sbomgen.read_file(path)
if err then
    sbomgen.log_error("failed to read " .. path .. ": " .. err)
    return
end
-- content is safe to use here
```

 如果外掛程式引發未處理的 Lua 錯誤，sbomgen 會記錄警告並繼續下一個檔案或外掛程式。其他外掛程式不受影響。

## 沙盒限制
<a name="sbomgen-plugin-developer-guide-sandbox-restrictions"></a>

 在標準程式庫存取權有限的沙盒 Lua VM 中執行的外掛程式：


| **Library (程式庫)** | **可用性** | **備註** | 
| --- | --- | --- | 
| base | ✓ | dofile、loadfile、 loadstring已移除 | 
| string | ✓ | 完整字串操作 | 
| table | ✓ | 完整資料表操作 | 
| math | ✓ | 完整數學程式庫 | 
| package | ✓ | require() 僅限於外掛程式目錄 | 
| io | ✗ | 請改用 sbomgen.\* I/O 函數 | 
| os | ✗ | 基於安全性而封鎖 | 
| debug | ✗ | 封鎖以防止 VM 自我檢查 | 
| coroutine | ✗ | 未載入 | 

 透過 `io.open`或 `os.execute` 的直接檔案系統存取不可用。所有檔案操作都必須經過 `sbomgen` API，以確保成品類型的一致行為，並防止外掛程式存取成品以外的檔案。

 `require()` 只能從外掛程式自己的目錄樹狀目錄中載入模組。會`require("../shared")`封鎖 等父目錄周遊。

## 在外掛程式之間共用程式碼
<a name="sbomgen-plugin-developer-guide-sharing-code-between-plugins"></a>

 您可以使用 從外掛程式的 目錄中`require()`載入協助程式模組：

```
my-ecosystem/
├── init.lua
└── helpers.lua
```

```
-- helpers.lua
local M = {}
function M.parse_version(s)
    return string.match(s, "(%d+%.%d+%.%d+)")
end
return M
```

```
-- init.lua
local helpers = require("helpers")

function subscribe_to_event() return "MyEvent" end

function collect(file_path)
    local content, err = sbomgen.read_file(file_path)
    if err then return end
    local version = helpers.parse_version(content)
    -- ...
end
```

 也支援使用 `init.lua` 的子目錄：

```
my-ecosystem/
├── init.lua
└── parsers/
    └── init.lua
```

```
local parsers = require("parsers")
```

 `require()` 僅限於您外掛程式的目錄。您無法從其他外掛程式或系統路徑載入模組。不支援第三方 Lua 程式庫 （例如來自 LuaRocks 的程式庫） – 只能載入外掛程式目錄中的本機協助程式模組。