npm package: @sanity-labs/backstage-plugin-trivy
Description: A Backstage plugin that fetches Trivy findings from a GCS bucket and displays in [Backstage](https://backstage.io/).
Latest version: 1.0.3

# Trivy Backstage Plugin

A Backstage plugin that fetches Trivy findings from a GCS bucket and displays in [Backstage](https://backstage.io/).

## Packages

- **Backend** (`@sanity-labs/backstage-plugin-trivy-backend`) - Fetches and serves scan data
- **Frontend** (`@sanity-labs/backstage-plugin-trivy`) - Entity card component

## Requirements

The backend requires Google Cloud credentials to access the storage bucket. Configure authentication using one of:

- **Service account key** (production):
  ```bash
  export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json
  ```

- **gcloud CLI** (local development):
  ```bash
  gcloud auth application-default login
  ```

- **Workload Identity** (GKE deployments)

See [Google Cloud authentication docs](https://cloud.google.com/docs/authentication/application-default-credentials) for details.

## Installation

Install both the frontend and backend packages:

```bash
# Using npm
npm install @sanity-labs/backstage-plugin-trivy @sanity-labs/backstage-plugin-trivy-backend

# Using yarn
yarn add @sanity-labs/backstage-plugin-trivy @sanity-labs/backstage-plugin-trivy-backend

# Using pnpm
pnpm add @sanity-labs/backstage-plugin-trivy @sanity-labs/backstage-plugin-trivy-backend
```

## Setup

### Backend

Add to `packages/backend/src/index.ts`:

```typescript
import trivy from '@sanity-labs/backstage-plugin-trivy-backend';

const backend = createBackend();
backend.add(trivy());
```

### Frontend

#### 1. Register the Trivy API

Add to `packages/app/src/apis.ts`:

```typescript
import {
  trivyApiRef,
  TrivyClient,
} from '@sanity-labs/backstage-plugin-trivy';
import {
  discoveryApiRef,
  fetchApiRef,
  configApiRef,
} from '@backstage/core-plugin-api';

export const apis: AnyApiFactory[] = [
  // ... existing APIs
  createApiFactory({
    api: trivyApiRef,
    deps: {
      discoveryApi: discoveryApiRef,
      fetchApi: fetchApiRef,
      configApi: configApiRef,
    },
    factory: ({ discoveryApi, fetchApi, configApi }) =>
      new TrivyClient({ discoveryApi, fetchApi, configApi }),
  }),
];
```

#### 2. Add the Entity Card

Add to `packages/app/src/components/catalog/EntityPage.tsx`:

```tsx
import {
  EntityTrivyCard,
  TrivyQueryProvider,
} from '@sanity-labs/backstage-plugin-trivy';

// Inside your entity layout
<Grid item md={6}>
  <TrivyQueryProvider>
    <EntityTrivyCard />
  </TrivyQueryProvider>
</Grid>
```

**Note:** The `TrivyQueryProvider` wrapper is required as the plugin uses React Query for data fetching.

The card displays security findings for the component's repository. It uses the entity's `github.com/project-slug` annotation or assumes the component name matches the repository name:

```yaml
metadata:
  name: my-service  # Should match the repository name
  annotations:
    github.com/project-slug: org/my-service  # Optional - uses component name if not set
```

## Configuration

Add the following to your `app-config.yaml`:

```yaml
trivy:
  # GCS bucket containing Trivy scan results (required)
  bucketName: your-bucket-name

  # Backend cache time in minutes (optional, default: 30)
  # How long the backend caches scan results in memory
  # Set to 0 to disable backend caching (useful for development)
  cacheTime: 30

  # Frontend stale time in minutes (optional, default: 5)
  # How long React Query considers data "fresh" before refetching
  # Set to 0 to always refetch (useful for development)
  staleTime: 5

  # Severity levels to display (optional, default: ["CRITICAL", "HIGH", "MEDIUM"])
  # Available levels: CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN
  severityFilter:
    - CRITICAL
    - HIGH
    - MEDIUM
```

### Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `bucketName` | string | `sanity-trivy-logs` | GCS bucket containing scan results |
| `cacheTime` | number | `30` | Backend cache duration in minutes (0 = no cache) |
| `staleTime` | number | `5` | Frontend cache duration in minutes (0 = always refetch) |
| `severityFilter` | string[] | `["CRITICAL", "HIGH", "MEDIUM"]` | Severity levels to display |

### Authentication

The backend requires Google Cloud credentials to access the storage bucket. Configure authentication using one of the methods described in the [Requirements](#requirements) section.

### Error Handling

The plugin provides clear error messages for common issues:

- **Permission Denied (403)**: Service account lacks `Storage Object Viewer` role on the bucket
- **No Scans Found (404)**: No scan results exist for the repository on the main branch
- **Failed to Fetch (500)**: Network or backend errors

Check the backend logs for detailed error information.

## GCS Bucket Structure

The backend expects scan results in the following structure:

```
gs://your-bucket/
└── repos/
    └── {repo-name}/
        └── {branch}/
            └── {scan-id}/
                ├── trivy-metadata.txt
                ├── trivy-fs-output.txt
                └── trivy-image-output.txt
```

Example:
```
gs://sanity-trivy-logs/
└── repos/
    └── my-service/
        └── main/
            └── 2024-01-01-12-00-00/
                ├── trivy-metadata.txt
                ├── trivy-fs-output.txt
                └── trivy-image-output.txt
```

### Metadata File Format

The `trivy-metadata.txt` file contains build information:

```
CircleCI Build URL: https://circleci.com/...
GitHub PR: https://github.com/org/repo/pull/123
Git Commit: abc123def456
Committer: user@example.com
```

## Development

### Local Development

For development, disable caching to always fetch fresh data:

```yaml
trivy:
  bucketName: your-bucket-name
  cacheTime: 0
  staleTime: 0
```

### Testing

Run tests:
```bash
yarn test
```

Build the plugin:
```bash
yarn build
```

Lint:
```bash
yarn lint
```

## License

Apache-2.0