Compare commits

...
Author SHA1 Message Date
mintlify[bot] e256438928 docs: link Prowler API key in Claude Code guide (#13265)
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
2026-11-04 17:54:41 -08:00
mintlify[bot] 74408f9015 docs: make Claude directory the preferred Claude Code plugin install (#13262)
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
2026-11-04 11:20:30 -08:00
Pedro Martín 5ad12b5ee8 fix(ui): join cross-provider breakdown on any requirement name (#13261) 2026-11-04 20:07:21 +01:00
6 changed files with 63 additions and 28 deletions

No files matched your search

+12 -4
View File
@@ -26,7 +26,7 @@ There are two ways to connect. Both end with the same MCP Server connection, the
| | 🔌 **Prowler Plugin** | ⚙️ **MCP Connection Only** |
|---|---|---|
| **What you get** | The MCP connection **plus** the official Prowler skills for cloud security tasks | The MCP connection |
| **Setup** | Two slash commands, prompts for the API key | One `claude mcp add` command |
| **Setup** | Add it from the [Claude directory](https://claude.ai/directory/prowler-cloud-for-claude-code), or run two slash commands. Prompts for the API key | One `claude mcp add` command |
| **Guided workflows** | ✅ Skills drive multi-step security work end to end | ❌ You drive the conversation |
| **Best for** | Structured cloud security work, such as taking an account to compliance | Ad-hoc queries and your own workflows |
| **Where to use it** | Claude Code CLI | Claude Code CLI, and the **recommended setup for the desktop app's [Code tab](#claude-code-in-the-desktop-app-code-tab)** |
@@ -64,10 +64,16 @@ End-to-end cloud security from inside Claude Code, powered by the Prowler MCP se
More skills are on the way. Installing the plugin keeps you current — new skills arrive with plugin updates, no extra configuration required.
</Note>
## Installation (Claude Code CLI)
## Installation
Prowler Cloud is listed in the official [Claude directory](https://claude.ai/directory/prowler-cloud-for-claude-code). Installing from the directory is the preferred method.
<Tabs>
<Tab title="From GitHub (recommended)">
<Tab title="From the Claude directory (recommended)">
1. Open the [Prowler Cloud listing](https://claude.ai/directory/prowler-cloud-for-claude-code) in the Claude directory. You can also find it in Claude under **Customize > Plugins > Discover** by searching for **Prowler Cloud**.
2. Select **Add**.
</Tab>
<Tab title="From GitHub">
Inside a Claude Code session:
```text
@@ -85,7 +91,7 @@ More skills are on the way. Installing the plugin keeps you current — new skil
</Tab>
</Tabs>
On first install, Claude Code prompts for your **Prowler API key**. The value is stored securely (macOS keychain or `~/.claude/.credentials.json`) and used to authenticate against Prowler Cloud.
On first install, Claude Code prompts for your [**Prowler API key**](/user-guide/tutorials/prowler-app-api-keys#creating-api-keys). The value is stored securely (macOS keychain or `~/.claude/.credentials.json`) and used to authenticate against Prowler Cloud.
## Verify the Installation
@@ -125,6 +131,8 @@ Claude tracks progress in a markdown report under `.prowler/` at your project ro
## Uninstalling
If you installed the plugin from GitHub or a local clone, run:
```text
/plugin uninstall prowler@prowler-plugins
/plugin marketplace remove prowler-plugins
@@ -258,6 +258,27 @@ describe.each([
});
});
describe("crossProviderToMapperInput through the generic mapper", () => {
it("joins extras onto every requirement of a framework without a dedicated mapper", () => {
const attributes = csaAttributes as CrossProviderOverviewAttributes;
const mapper = getComplianceMapper("Registry-Framework-Without-Mapper");
const { attributesData, requirementsData } =
crossProviderToMapperInput(attributes);
const mapped = collectRequirements(
mapper.mapComplianceData(attributesData, requirementsData),
);
const extras = buildRequirementExtrasMap(attributes);
expect(mapped).toHaveLength(attributes.requirements.length);
for (const requirement of mapped) {
expect(
extras.get(requirement.name),
`no extras for "${requirement.name}"`,
).toBeDefined();
}
});
});
describe("CSA grouping through the real mapper", () => {
it("groups requirements by Section into categories with correct counters", () => {
const mapper = getComplianceMapper("CSA-CCM");
@@ -12,21 +12,7 @@ import type {
CrossAccountRequirementExtras,
} from "../_types";
/** Candidate display names a framework mapper may give this requirement.
*
* Unlike the cross-provider view (whose three universal frameworks all
* compose `id - name`), cross-account serves EVERY framework, and mappers
* disagree on the composed name: CSA/CIS-Controls/DORA use `id - name`,
* CIS/CCC/PCI use the bare `id`, the generic mapper uses the bare `name`.
* The extras map registers every candidate so the join works regardless of
* which mapper renders the framework. */
const candidateRequirementNames = (id: string, name: string): string[] => {
const candidates = [id];
if (name) {
candidates.push(name, `${id} - ${name}`);
}
return candidates;
};
import { candidateRequirementNames } from "./requirement-names";
/** Display label for an account column: alias when set, uid otherwise. */
export const accountDisplayLabel = (account: CrossAccountAccountRef): string =>
@@ -14,11 +14,7 @@ import type {
ProviderCheckIdsMap,
} from "../_types";
/** The composed display name the per-scan mappers give every requirement.
* Shared with `buildRequirementExtrasMap` so the extras join never parses
* strings back apart. */
const composeRequirementName = (id: string, name: string): string =>
name ? `${id} - ${name}` : id;
import { candidateRequirementNames } from "./requirement-names";
/**
* Convert a cross-provider overview into the `{AttributesData,
@@ -84,8 +80,8 @@ export const crossProviderToMapperInput = (
};
/**
* Cross-provider context for each requirement, keyed by the exact composed
* name the mappers produce, so renderers can join per-provider statuses and
* Cross-provider context for each requirement, keyed by every name a mapper
* may give it, so renderers can join per-provider statuses and
* scan/check splits onto mapped requirements without touching the mappers.
*/
export const buildRequirementExtrasMap = (
@@ -94,12 +90,21 @@ export const buildRequirementExtrasMap = (
const extras = new Map<string, CrossProviderRequirementExtras>();
for (const requirement of attrs.requirements) {
extras.set(composeRequirementName(requirement.id, requirement.name), {
const entry: CrossProviderRequirementExtras = {
requirementId: requirement.id,
providers: requirement.providers,
checkIdsByProvider: requirement.check_ids_by_provider ?? {},
scanIdsByProvider: attrs.scan_ids_by_provider,
});
};
for (const name of candidateRequirementNames(
requirement.id,
requirement.name,
)) {
// First registration wins, so a bare-name key never hijacks an id key.
if (!extras.has(name)) {
extras.set(name, entry);
}
}
}
return extras;
@@ -0,0 +1,14 @@
/** Display names a framework mapper may give a requirement.
* Dedicated mappers compose `id - name`, CIS/CCC/PCI use the bare `id` and the
* generic mapper (any framework without a dedicated one, including registry
* frameworks) uses the bare `name`, so extras maps register all of them. */
export const candidateRequirementNames = (
id: string,
name: string,
): string[] => {
const candidates = [id];
if (name) {
candidates.push(name, `${id} - ${name}`);
}
return candidates;
};
@@ -0,0 +1 @@
Cross-provider compliance view shows the per-provider breakdown for frameworks without a dedicated UI mapper instead of "No per-provider breakdown is available"