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
8 changes: 4 additions & 4 deletions skills/pncli/checkmarx.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Enables: `pncli checkmarx project list/get`, `pncli checkmarx scan list/get/stat

| Key | Env var | Description |
|-----|---------|-------------|
| `checkmarx.baseUrl` | `PNCLI_CHECKMARX_BASE_URL` | Checkmarx One API base, e.g. `https://ast.checkmarx.net` |
| `checkmarx.baseUrl` | `PNCLI_CHECKMARX_BASE_URL` | Checkmarx One API base, e.g. `https://ast.checkmarx.net/api` |
| `checkmarx.tenantName` | `PNCLI_CHECKMARX_TENANT_NAME` | IAM realm / tenant name, e.g. `mycompany` |
| `checkmarx.apiKey` | `PNCLI_CHECKMARX_API_KEY` | API key created in Checkmarx One IAM (recommended) |
| `checkmarx.clientId` | `PNCLI_CHECKMARX_CLIENT_ID` | OAuth2 client ID (alternative to API key) |
Expand All @@ -18,21 +18,21 @@ short-lived bearer token automatically. No external tools are required.
## Config file (persistent)

```
pncli config set checkmarx.baseUrl https://ast.checkmarx.net
pncli config set checkmarx.baseUrl https://ast.checkmarx.net/api
pncli config set checkmarx.tenantName mycompany
pncli config set checkmarx.apiKey <api-key>
```

## Env vars (ephemeral / CI)

```
export PNCLI_CHECKMARX_BASE_URL=https://ast.checkmarx.net
export PNCLI_CHECKMARX_BASE_URL=https://ast.checkmarx.net/api
export PNCLI_CHECKMARX_TENANT_NAME=mycompany
export PNCLI_CHECKMARX_API_KEY=<api-key>
```

## Regional deployments

For EU or other regional Checkmarx One instances, use the appropriate API base URL
(e.g. `https://eu.ast.checkmarx.net`). pncli derives the matching regional IAM host
(e.g. `https://eu.ast.checkmarx.net/api`). pncli derives the matching regional IAM host
(e.g. `https://eu.iam.checkmarx.net`) from that URL.
4 changes: 2 additions & 2 deletions src/services/checkmarx/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('CheckmarxClient', () => {
});

await expect(client.listProjects()).resolves.toEqual([]);
expect(checkmarx).toHaveBeenCalledWith('/api/projects', { params: { limit: 100 } });
expect(checkmarx).toHaveBeenCalledWith('projects', { params: { limit: 100 } });
});

it('returns an empty scan list when Checkmarx responds with null', async () => {
Expand All @@ -31,7 +31,7 @@ describe('CheckmarxClient', () => {
});

await expect(client.listScans({ projectId: 'project-id', last: 25 })).resolves.toEqual([]);
expect(checkmarx).toHaveBeenCalledWith('/api/scans', {
expect(checkmarx).toHaveBeenCalledWith('scans', {
params: { limit: 25, 'project-id': 'project-id' }
});
});
Expand Down
10 changes: 5 additions & 5 deletions src/services/checkmarx/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class CheckmarxClient {
constructor(private http: HttpClient) {}

async listProjects(): Promise<CxOneProject[]> {
const res = await this.http.checkmarx<CxOneProjectsResponse>('/api/projects', { params: { limit: 100 } });
const res = await this.http.checkmarx<CxOneProjectsResponse>('projects', { params: { limit: 100 } });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These paths now implicitly require that checkmarx.baseUrl ends with /api (e.g. https://ast.checkmarx.net/api), but skills/pncli/checkmarx.md still documents the example as https://ast.checkmarx.net (no /api).

With buildUrl('https://ast.checkmarx.net', 'projects') the result is https://ast.checkmarx.net/projects — the /api/ segment is missing, so any user who followed the documented example before this PR will silently get 404s.

CLAUDE.md calls the skills file the "onboarding contract" and says it must be updated on every credential/URL change. The fix needs to either:

a) Update the docs — change the example in skills/pncli/checkmarx.md from https://ast.checkmarx.nethttps://ast.checkmarx.net/api

Suggested change
const res = await this.http.checkmarx<CxOneProjectsResponse>('projects', { params: { limit: 100 } });
const res = await this.http.checkmarx<CxOneProjectsResponse>('projects', { params: { limit: 100 } });

(No code change needed here — the paths are correct given option a. Just noting the inline location of the dependency.)

b) Keep /api/ in the paths as a relative prefix (e.g. api/projects) and keep the documented base URL as https://ast.checkmarx.net. But this still breaks users who already have /api in their saved config, so option (a) is cleaner.

Either way, skills/pncli/checkmarx.md must be updated before merging.

const projects = res.projects ?? [];
if (res.filteredTotalCount > projects.length) {
process.stderr.write(`warning: ${res.filteredTotalCount} projects found; only showing first ${projects.length}\n`);
Expand All @@ -20,13 +20,13 @@ export class CheckmarxClient {
}

async getProject(id: string): Promise<CxOneProject> {
return this.http.checkmarx<CxOneProject>(`/api/projects/${id}`);
return this.http.checkmarx<CxOneProject>(`projects/${id}`);
}

async listScans(opts: { projectId?: string; last?: number } = {}): Promise<CxOneScan[]> {
const params: Record<string, string | number> = { limit: opts.last ?? 100 };
if (opts.projectId) params['project-id'] = opts.projectId;
const res = await this.http.checkmarx<CxOneScansResponse>('/api/scans', { params });
const res = await this.http.checkmarx<CxOneScansResponse>('scans', { params });
const scans = res.scans ?? [];
if (res.filteredTotalCount > scans.length) {
process.stderr.write(`warning: ${res.filteredTotalCount} scans found; only showing first ${scans.length}\n`);
Expand All @@ -35,11 +35,11 @@ export class CheckmarxClient {
}

async getScan(id: string): Promise<CxOneScan> {
return this.http.checkmarx<CxOneScan>(`/api/scans/${id}`);
return this.http.checkmarx<CxOneScan>(`scans/${id}`);
}

async getScanResultsStatistics(scanId: string): Promise<CxOneResultsSummary> {
return this.http.checkmarx<CxOneResultsSummary>('/api/results/summary', {
return this.http.checkmarx<CxOneResultsSummary>('results/summary', {
params: { 'scan-id': scanId }
});
}
Expand Down
Loading