diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a8621d3..22b3417 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -31,8 +31,8 @@ Please provide a brief description of your changes and the context for this inte - [ ] **New Integration Folder:** A new folder has been created for the integration. - [ ] **Updated README:** The README has been updated based on the boilerplate to reflect the new integration details. -- [ ] **custom-integration.star File:** The `custom-integration-.star` file has been created/updated as required. -- [ ] **config.json File:** The `config.json` is updated with the `name` (product name) and `type` (inbound or outbound) of integration. +- [ ] **Integration script:** The `.star` file has been created/updated as required. +- [ ] **Embedded CONFIG:** The script's `CONFIG` block declares the `name` (product name), `type` (inbound, outbound, or internal), parameters, and any shared option-set includes. --- diff --git a/.github/skills/create-custom-integration/SKILL.md b/.github/skills/create-custom-integration/SKILL.md new file mode 100644 index 0000000..f6f11d6 --- /dev/null +++ b/.github/skills/create-custom-integration/SKILL.md @@ -0,0 +1,191 @@ +--- +name: create-custom-integration +description: 'Create a new runZero custom integration from vendor API documentation and a brief description of the data to import. Use when asked to build, scaffold, generate, or add an inbound Starlark integration. Requires an explicit foreign-ID uniqueness, stability, scope, and cardinality decision before writing code so repeated ingest does not create duplicate assets.' +argument-hint: ' -- import ' +user-invocable: true +disable-model-invocation: false +--- + +# Create a Custom Integration + +Build a production-ready inbound runZero Starlark integration from vendor +documentation and a short import brief. The output is a new repository folder +containing `.star` and `README.md`, plus regenerated catalog +metadata when available. + +## Required Inputs + +Collect these before implementation: + +1. A public or accessible link to the vendor API documentation. +2. A brief statement of which vendor objects and fields should become runZero + assets, software, services, vulnerabilities, tags, or custom attributes. +3. Authentication and deployment constraints if they are not documented. + +If the documentation link or import brief is missing, ask for it. If the docs +need authentication and cannot be read, ask for exported docs, an OpenAPI file, +or representative redacted request/response samples. + +## Source of Truth + +Read these repository files before coding: + +- [Authoring rules](../../../AGENTS.md) +- [Starlark helper reference](../../../docs/starlark-helpers.md) +- [Boilerplate script](../../../boilerplate/boilerplate.star) +- [v1 to v2 migration guide](../../../docs/migration-v1-to-v2.md) +- [Identity decision reference](./references/identity-decision.md) + +Inspect one or two existing integrations with similar authentication, +pagination, and data shape. Reuse repository patterns and helper modules rather +than copying generic Python or inventing APIs. + +## Workflow + +### 1. Research the Vendor API + +Read the supplied documentation and identify: + +- Base URL and required API version. +- Authentication flow, token lifetime, and required scopes. +- Inventory endpoints and object relationships. +- Pagination mechanism, maximum page size, and termination condition. +- Rate limits, retry guidance, and relevant response headers. +- Fields for identity, hostname, IP, MAC, OS, hardware, timestamps, tags, + software, services, vulnerabilities, and custom attributes. +- Whether one response row represents an asset, an event, an interface, an + agent installation, a user/device association, or another child record. + +Prefer the documented API contract over examples or field names that merely +look plausible. Record the documentation URLs used for authentication, +pagination, object schema, and identity in the integration README. + +### 2. Complete the Asset Identity Gate + +Do not write the integration until the foreign-ID decision is explicit. +Complete the decision record from +[identity-decision.md](./references/identity-decision.md) and include it in the +new integration README under `## Asset identity`. + +The key question is not only "is this field unique?" The field must be: + +- Stable across repeated polls and normal asset lifecycle changes. +- Unique within a known scope. +- One-to-one with the asset type described in the import brief. +- Present on every record that will be imported. +- Not an event, session, interface, finding, temporary, or installation ID when + multiple such records can describe one asset. + +Choose exactly one verdict: + +1. **Authoritative foreign ID.** Use a namespaced ID such as + `vendor::`. Keep default ID matching. If MAC, + IP, or hostname are expected to churn while the vendor ID remains + authoritative, use `no-mac-break no-ip-break no-name-break`. +2. **Scoped foreign ID.** The source ID is unique only inside an account, + organization, site, or region. Include that stable scope in the namespaced + ID. Never use the raw local ID by itself. +3. **No authoritative foreign ID.** Build a deterministic, documented composite + only from stable source fields. If that composite is not guaranteed unique, + set `matchBehavior="no-id-match no-id-break"` and require at least one usable + MAC, IP, or hostname for correlation. +4. **Identity unresolved.** Stop and ask for documentation or representative + samples. Do not generate an integration that guesses. + +Never use `new_uuid()` or another random value as an imported asset ID or as a +fallback. A random fallback creates a new asset on every poll. If a record lacks +the required identity and no safe deterministic fallback exists, skip it with a +concise message that does not contain credentials or the full vendor object. + +### 3. Design the Mapping + +Write a short mapping plan before implementation: + +| Vendor object/field | runZero field | Conversion | Required | +| --- | --- | --- | --- | +| documented identity | `ImportAsset.id` | scoped/namespaced | yes | +| documented names | `hostnames` | trim and deduplicate | no | +| IP and MAC fields | `networkInterfaces` | `network_interface(...)` | no | +| remaining useful fields | `customAttributes` | `to_custom_attributes(...)` | no | + +Decide whether child records should be attached as software, services, or +vulnerabilities rather than emitted as separate assets. This cardinality check +is part of duplicate prevention. + +### 4. Implement the Integration + +Create `/.star` and `/README.md`. + +The script must: + +- Put a literal `CONFIG = {...}` first, with `minVersion: "5.1.0"` or later. +- Declare every kwarg and mark credential fields as `secret` without defaults. +- Use `OPTIONS_HTTP` and `OPTIONS_TLS` plus `get_http_options` for HTTP APIs. +- Use the runZero `http`, `kwargs`, `net`, parsing, and type helpers. +- Use `validationMode: "compile"` only for direct-protocol integrations. +- Handle all pages and stop on the documented pagination condition. +- Stream each page with `report_assets(page_assets)` and return `None` for + inventory APIs that can return large datasets. +- Skip malformed records that lack the approved identity instead of inventing + IDs. +- Keep credentials out of logs, errors, tags, and custom attributes. +- Respect documented rate limits and use bounded retry behavior. + +The README must document setup, required permissions/scopes, parameters, +imported data, pagination, known limits, validation steps, documentation links, +and the completed asset identity decision. + +### 5. Validate with Representative Data + +Validation must exercise more than compilation. + +1. Run `runzero script --filename