Compare commits

...

4 Commits
v5.16 ... 5.1.1

Author SHA1 Message Date
Prowler Bot
c172f75f1a fix(dep): address compatibility issues (#6557)
Co-authored-by: Pablo Lara <larabjj@gmail.com>
2025-01-16 14:35:06 +01:00
Prowler Bot
ec492fa13a feat(filters): add resource type filter for findings (#6525)
Co-authored-by: Pablo Lara <larabjj@gmail.com>
2025-01-15 08:43:49 +01:00
Prowler Bot
702659959c fix(Azure TDE): add filter for master DB (#6514)
Co-authored-by: johannes-engler-mw <132657752+johannes-engler-mw@users.noreply.github.com>
2025-01-14 15:25:27 -05:00
Pepe Fagoaga
fef332a591 chore(version): set next fixes 2025-01-14 18:05:04 +01:00
15 changed files with 3681 additions and 3314 deletions

View File

@@ -12,7 +12,7 @@ from prowler.lib.logger import logger
timestamp = datetime.today()
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc)
prowler_version = "5.1.0"
prowler_version = "5.1.1"
html_logo_url = "https://github.com/prowler-cloud/prowler/"
square_logo_img = "https://prowler.com/wp-content/uploads/logo-html.png"
aws_logo = "https://user-images.githubusercontent.com/38561120/235953920-3e3fba08-0795-41dc-b480-9bea57db9f2e.png"

View File

@@ -12,6 +12,8 @@ class sqlserver_tde_encryption_enabled(Check):
)
if len(databases) > 0:
for database in databases:
if database.name.lower() == "master":
continue
report = Check_Report_Azure(self.metadata())
report.subscription = subscription
report.resource_name = database.name

View File

@@ -23,7 +23,7 @@ packages = [
{include = "dashboard"}
]
readme = "README.md"
version = "5.1.0"
version = "5.1.1"
[tool.poetry.dependencies]
alive-progress = "3.2.0"

View File

@@ -177,3 +177,68 @@ class Test_sqlserver_tde_encryption_enabled:
assert result[0].resource_name == database_name
assert result[0].resource_id == database_id
assert result[0].location == "location"
def test_sql_servers_database_encryption_disabled_on_master_db(self):
sqlserver_client = mock.MagicMock
sql_server_name = "SQL Server Name"
sql_server_id = str(uuid4())
database_master_name = "MASTER"
database_master_id = str(uuid4())
database_master = Database(
id=database_master_id,
name=database_master_name,
type="type",
location="location",
managed_by="managed_by",
tde_encryption=TransparentDataEncryption(status="Disabled"),
)
database_name = "Database Name"
database_id = str(uuid4())
database = Database(
id=database_id,
name=database_name,
type="type",
location="location",
managed_by="managed_by",
tde_encryption=TransparentDataEncryption(status="Enabled"),
)
sqlserver_client.sql_servers = {
AZURE_SUBSCRIPTION_ID: [
Server(
id=sql_server_id,
name=sql_server_name,
public_network_access="",
minimal_tls_version="",
administrators=None,
auditing_policies=None,
firewall_rules=None,
databases=[database_master, database],
encryption_protector=None,
location="location",
)
]
}
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
), mock.patch(
"prowler.providers.azure.services.sqlserver.sqlserver_tde_encryption_enabled.sqlserver_tde_encryption_enabled.sqlserver_client",
new=sqlserver_client,
):
from prowler.providers.azure.services.sqlserver.sqlserver_tde_encryption_enabled.sqlserver_tde_encryption_enabled import (
sqlserver_tde_encryption_enabled,
)
check = sqlserver_tde_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Database {database_name} from SQL Server {sql_server_name} from subscription {AZURE_SUBSCRIPTION_ID} has TDE enabled"
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == database_name
assert result[0].resource_id == database_id
assert result[0].location == "location"

View File

@@ -28,7 +28,11 @@ export const getFindings = async ({
if (sort) url.searchParams.append("sort", sort);
Object.entries(filters).forEach(([key, value]) => {
if (key !== "filter[search]") {
const excludedFilters = ["region__in", "service__in", "resource_type__in"];
if (
key !== "filter[search]" &&
!excludedFilters.some((filter) => key.includes(filter))
) {
url.searchParams.append(key, String(value));
}
});
@@ -51,7 +55,7 @@ export const getFindings = async ({
}
};
export const getServicesRegions = async ({
export const getMetadataInfo = async ({
query = "",
sort = "",
filters = {},
@@ -59,13 +63,18 @@ export const getServicesRegions = async ({
const session = await auth();
const keyServer = process.env.API_BASE_URL;
const url = new URL(`${keyServer}/findings/findings_services_regions`);
const url = new URL(`${keyServer}/findings/metadata`);
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
Object.entries(filters).forEach(([key, value]) => {
if (key !== "filter[search]") {
// Define filters to exclude
const excludedFilters = ["region__in", "service__in", "resource_type__in"];
if (
key !== "filter[search]" &&
!excludedFilters.some((filter) => key.includes(filter))
) {
url.searchParams.append(key, String(value));
}
});

View File

@@ -81,25 +81,32 @@ export const addRole = async (formData: FormData) => {
const name = formData.get("name") as string;
const groups = formData.getAll("groups[]") as string[];
// Prepare base payload
const payload: any = {
data: {
type: "roles",
attributes: {
name,
manage_users: formData.get("manage_users") === "true",
manage_account: formData.get("manage_account") === "true",
manage_billing: formData.get("manage_billing") === "true",
manage_providers: formData.get("manage_providers") === "true",
manage_integrations: formData.get("manage_integrations") === "true",
manage_scans: formData.get("manage_scans") === "true",
// TODO: Add back when we have integrations ready
// manage_integrations: formData.get("manage_integrations") === "true",
unlimited_visibility: formData.get("unlimited_visibility") === "true",
},
relationships: {},
},
};
// Add relationships only if there are items
// Conditionally include manage_account and manage_billing for cloud environment
if (process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true") {
payload.data.attributes.manage_account =
formData.get("manage_account") === "true";
payload.data.attributes.manage_billing =
formData.get("manage_billing") === "true";
}
// Add provider groups relationships only if there are items
if (groups.length > 0) {
payload.data.relationships.provider_groups = {
data: groups.map((groupId: string) => ({
@@ -147,19 +154,27 @@ export const updateRole = async (formData: FormData, roleId: string) => {
type: "roles",
id: roleId,
attributes: {
...(name && { name }),
...(name && { name }), // Include name only if provided
manage_users: formData.get("manage_users") === "true",
manage_account: formData.get("manage_account") === "true",
manage_billing: formData.get("manage_billing") === "true",
manage_providers: formData.get("manage_providers") === "true",
manage_integrations: formData.get("manage_integrations") === "true",
manage_scans: formData.get("manage_scans") === "true",
// TODO: Add back when we have integrations ready
// manage_integrations: formData.get("manage_integrations") === "true",
unlimited_visibility: formData.get("unlimited_visibility") === "true",
},
relationships: {},
},
};
// Conditionally include manage_account and manage_billing for cloud environments
if (process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true") {
payload.data.attributes.manage_account =
formData.get("manage_account") === "true";
payload.data.attributes.manage_billing =
formData.get("manage_billing") === "true";
}
// Add provider groups relationships only if there are items
if (groups.length > 0) {
payload.data.relationships.provider_groups = {
data: groups.map((groupId: string) => ({
@@ -182,6 +197,7 @@ export const updateRole = async (formData: FormData, roleId: string) => {
},
body,
});
const data = await response.json();
revalidatePath("/roles");
return data;

View File

@@ -1,7 +1,7 @@
import { Spacer } from "@nextui-org/react";
import React, { Suspense } from "react";
import { getFindings, getServicesRegions } from "@/actions/findings";
import { getFindings, getMetadataInfo } from "@/actions/findings";
import { getProviders } from "@/actions/providers";
import { getScans } from "@/actions/scans";
import { filterFindings } from "@/components/filters/data-filters";
@@ -47,15 +47,17 @@ export default async function Findings({
const query = filters["filter[search]"] || "";
const servicesRegionsData = await getServicesRegions({
const metadataInfoData = await getMetadataInfo({
query,
sort: encodedSort,
filters,
});
// Extract unique regions and services from the new endpoint
const uniqueRegions = servicesRegionsData?.data?.attributes?.regions || [];
const uniqueServices = servicesRegionsData?.data?.attributes?.services || [];
const uniqueRegions = metadataInfoData?.data?.attributes?.regions || [];
const uniqueServices = metadataInfoData?.data?.attributes?.services || [];
const uniqueResourceTypes =
metadataInfoData?.data?.attributes?.resource_types || [];
// Get findings data
const providersData = await getProviders({});
const scansData = await getScans({});
@@ -72,7 +74,7 @@ export default async function Findings({
// Extract scan UUIDs with "completed" state and more than one resource
const completedScans = scansData?.data
?.filter(
(scan: any) =>
(scan: ScanProps) =>
scan.attributes.state === "completed" &&
scan.attributes.unique_resource_count > 1,
)
@@ -104,6 +106,11 @@ export default async function Findings({
labelCheckboxGroup: "Services",
values: uniqueServices,
},
{
key: "resource_type__in",
labelCheckboxGroup: "Resource Type",
values: uniqueResourceTypes,
},
{
key: "provider_uid__in",
labelCheckboxGroup: "Provider UID",

View File

@@ -1,6 +1,6 @@
import { Alert, cn } from "@nextui-org/react";
import React from "react";
import { InfoIcon } from "@/components/icons";
import {
UpdateViaCredentialsForm,
UpdateViaRoleForm,
@@ -17,33 +17,23 @@ export default function UpdateCredentialsPage({ searchParams }: Props) {
{searchParams.type === "aws" && !searchParams.via && (
<>
<div className="flex flex-col gap-4">
<p className="text-sm text-default-500">
If the provider was set up with static credentials, updates must
use static credentials. If it was set up with a role, updates must
use a role.
<p className="text-sm text-default-700">
To update provider credentials,{" "}
<strong>
the same type that was originally configured must be used.
</strong>
</p>
<Alert
color="warning"
variant="faded"
classNames={{
base: cn([
"border-1 border-default-200 dark:border-default-100",
"gap-x-4",
]),
}}
description={
<>
To update provider credentials,{" "}
<strong>
you must use the same type that was originally configured.
</strong>{" "}
</>
}
/>
<p className="text-sm text-default-500">
To switch from static credentials to a role (or vice versa), you
need to delete the provider and set it up again.
<div className="flex items-center rounded-lg border border-system-warning bg-system-warning-medium p-4 text-sm dark:text-default-300">
<InfoIcon className="mr-2 inline h-4 w-4 flex-shrink-0" />
<p>
If the provider was configured with static credentials, updates
must also use static credentials. If it was configured with a
role, updates must use a role.
</p>
</div>
<p className="text-sm text-default-700">
To switch from static credentials to a role (or vice versa), the
provider must be deleted and set up again.
</p>
<SelectViaAWS initialVia={searchParams.via} />
</div>

View File

@@ -66,17 +66,18 @@ export default async function Scans({
<>
<Spacer y={8} />
<NoProvidersConnected />
<Spacer y={4} />
</>
) : (
<>
<LaunchScanWorkflow providers={providerInfo} />
<Spacer y={4} />
<ScanWarningBar />
<Spacer y={8} />
</>
)}
<div className="grid grid-cols-12 items-start gap-4">
<div className="col-span-12">
<ScanWarningBar />
<Spacer y={4} />
<div className="flex flex-row items-center justify-between">
<DataTableFilterCustom filters={filterScans || []} />
<ButtonRefreshData

View File

@@ -33,13 +33,14 @@ export const AddRoleForm = ({
defaultValues: {
name: "",
manage_users: false,
manage_account: false,
manage_billing: false,
manage_providers: false,
manage_integrations: false,
manage_scans: false,
unlimited_visibility: false,
groups: [],
...(process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true" && {
manage_account: false,
manage_billing: false,
}),
},
});
@@ -64,7 +65,7 @@ export const AddRoleForm = ({
"manage_account",
"manage_billing",
"manage_providers",
"manage_integrations",
// "manage_integrations",
"manage_scans",
"unlimited_visibility",
];
@@ -79,18 +80,22 @@ export const AddRoleForm = ({
const onSubmitClient = async (values: FormValues) => {
const formData = new FormData();
formData.append("name", values.name);
formData.append("manage_users", String(values.manage_users));
formData.append("manage_account", String(values.manage_account));
formData.append("manage_billing", String(values.manage_billing));
formData.append("manage_providers", String(values.manage_providers));
formData.append("manage_integrations", String(values.manage_integrations));
formData.append("manage_scans", String(values.manage_scans));
formData.append(
"unlimited_visibility",
String(values.unlimited_visibility),
);
// Conditionally append manage_account and manage_billing
if (process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true") {
formData.append("manage_account", String(values.manage_account));
formData.append("manage_billing", String(values.manage_billing));
}
if (values.groups && values.groups.length > 0) {
values.groups.forEach((group) => {
formData.append("groups[]", group);

View File

@@ -95,16 +95,21 @@ export const EditRoleForm = ({
}
updatedFields.manage_users = values.manage_users;
updatedFields.manage_account = values.manage_account;
updatedFields.manage_billing = values.manage_billing;
updatedFields.manage_providers = values.manage_providers;
updatedFields.manage_integrations = values.manage_integrations;
// updatedFields.manage_integrations = values.manage_integrations;
updatedFields.manage_scans = values.manage_scans;
updatedFields.unlimited_visibility = values.unlimited_visibility;
if (process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true") {
updatedFields.manage_account = values.manage_account;
updatedFields.manage_billing = values.manage_billing;
}
if (
JSON.stringify(values.groups) !==
JSON.stringify(roleData.data.relationships?.provider_groups?.data)
JSON.stringify(
roleData.data.relationships?.provider_groups?.data.map((g) => g.id),
)
) {
updatedFields.groups = values.groups;
}

View File

@@ -1,19 +1,18 @@
import { Alert, cn } from "@nextui-org/react";
"use client";
import { InfoIcon } from "../icons";
export const ScanWarningBar = () => {
return (
<Alert
color="warning"
title="Waiting for Your Scan to Show Up?"
description="Your scan is being processed and may take a few minutes to appear on the table. It will show up shortly."
variant="faded"
isClosable
classNames={{
base: cn([
"border-1 border-default-200 dark:border-default-100",
"gap-x-4",
]),
}}
/>
<div className="flex items-center rounded-lg border border-system-warning bg-system-warning-medium p-4 text-sm dark:text-default-300">
<InfoIcon className="mr-4 inline h-4 w-4 flex-shrink-0" />
<div className="flex flex-col gap-1">
<strong>Waiting for Your Scan to Show Up?</strong>
<p>
It may take a few minutes for the scan to appear on the table and be
displayed.
</p>
</div>
</div>
);
};

6739
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"@nextui-org/react": "^2.6.11",
"@nextui-org/react": "2.4.8",
"@nextui-org/system": "2.2.1",
"@nextui-org/theme": "2.2.5",
"@radix-ui/react-alert-dialog": "^1.1.1",
@@ -89,5 +89,8 @@
"format:write": "./node_modules/.bin/prettier --config .prettierrc.json --write ./app",
"prepare": "husky"
},
"overrides": {
"@react-types/shared": "3.26.0"
},
"version": "0.0.1"
}

View File

@@ -6,7 +6,7 @@ export const addRoleFormSchema = z.object({
manage_account: z.boolean().default(false),
manage_billing: z.boolean().default(false),
manage_providers: z.boolean().default(false),
manage_integrations: z.boolean().default(false),
// manage_integrations: z.boolean().default(false),
manage_scans: z.boolean().default(false),
unlimited_visibility: z.boolean().default(false),
groups: z.array(z.string()).optional(),
@@ -18,7 +18,7 @@ export const editRoleFormSchema = z.object({
manage_account: z.boolean().default(false),
manage_billing: z.boolean().default(false),
manage_providers: z.boolean().default(false),
manage_integrations: z.boolean().default(false),
// manage_integrations: z.boolean().default(false),
manage_scans: z.boolean().default(false),
unlimited_visibility: z.boolean().default(false),
groups: z.array(z.string()).optional(),