mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-05-15 08:42:22 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77451d5cf8 | |||
| 5b54b47930 | |||
| a1168e3082 | |||
| f2341c9878 | |||
| 67b8e925e5 |
@@ -78,3 +78,6 @@ _data/
|
||||
|
||||
# Claude
|
||||
CLAUDE.md
|
||||
|
||||
# LLM's (Until we have a standard one)
|
||||
AGENTS.md
|
||||
|
||||
@@ -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.12.1"
|
||||
prowler_version = "5.12.2"
|
||||
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"
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ maintainers = [{name = "Prowler Engineering", email = "engineering@prowler.com"}
|
||||
name = "prowler"
|
||||
readme = "README.md"
|
||||
requires-python = ">3.9.1,<3.13"
|
||||
version = "5.12.1"
|
||||
version = "5.12.2"
|
||||
|
||||
[project.scripts]
|
||||
prowler = "prowler.__main__:prowler"
|
||||
|
||||
+10
-2
@@ -2,12 +2,20 @@
|
||||
|
||||
All notable changes to the **Prowler UI** are documented in this file.
|
||||
|
||||
|
||||
## [1.12.2] (Prowler v5.12.2)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Handle 4XX errors consistently and 204 responses properly[(#8722)](https://github.com/prowler-cloud/prowler/pull/8722)
|
||||
- Scrolling during Lighthouse AI response streaming [(#8669)](https://github.com/prowler-cloud/prowler/pull/8669)
|
||||
|
||||
## [1.12.1] (Prowler v5.12.1)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Field-level email validation message [(#8698)] (https://github.com/prowler-cloud/prowler/pull/8698)
|
||||
- POST method on auth form [(#8699)] (https://github.com/prowler-cloud/prowler/pull/8699)
|
||||
- Field-level email validation message [(#8698)](https://github.com/prowler-cloud/prowler/pull/8698)
|
||||
- POST method on auth form [(#8699)](https://github.com/prowler-cloud/prowler/pull/8699)
|
||||
|
||||
## [1.12.0] (Prowler v5.12.0)
|
||||
|
||||
|
||||
@@ -159,8 +159,7 @@ export const updateRole = async (formData: FormData, roleId: string) => {
|
||||
manage_providers: formData.get("manage_providers") === "true",
|
||||
manage_account: formData.get("manage_account") === "true",
|
||||
manage_scans: formData.get("manage_scans") === "true",
|
||||
// TODO: Add back when we have integrations ready
|
||||
// manage_integrations: formData.get("manage_integrations") === "true",
|
||||
manage_integrations: formData.get("manage_integrations") === "true",
|
||||
unlimited_visibility: formData.get("unlimited_visibility") === "true",
|
||||
},
|
||||
relationships: {},
|
||||
|
||||
@@ -147,7 +147,8 @@ export const AuthForm = ({
|
||||
} else {
|
||||
newUser.errors.forEach((error: ApiError) => {
|
||||
const errorMessage = error.detail;
|
||||
switch (error.source.pointer) {
|
||||
const pointer = error.source?.pointer;
|
||||
switch (pointer) {
|
||||
case "/data/attributes/name":
|
||||
form.setError("name", { type: "server", message: errorMessage });
|
||||
break;
|
||||
|
||||
@@ -53,7 +53,8 @@ export const SendInvitationForm = ({
|
||||
if (data?.errors && data.errors.length > 0) {
|
||||
data.errors.forEach((error: ApiError) => {
|
||||
const errorMessage = error.detail;
|
||||
switch (error.source.pointer) {
|
||||
const pointer = error.source?.pointer;
|
||||
switch (pointer) {
|
||||
case "/data/attributes/email":
|
||||
form.setError("email", {
|
||||
type: "server",
|
||||
|
||||
@@ -146,16 +146,6 @@ export const Chat = ({ hasConfig, isActive }: ChatProps) => {
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [messageValue, onFormSubmit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (messagesContainerRef.current && latestUserMsgRef.current) {
|
||||
const container = messagesContainerRef.current;
|
||||
const userMsg = latestUserMsgRef.current;
|
||||
const containerPadding = 16; // p-4 in Tailwind = 16px
|
||||
container.scrollTop =
|
||||
userMsg.offsetTop - container.offsetTop - containerPadding;
|
||||
}
|
||||
}, [messages]);
|
||||
|
||||
const suggestedActions: SuggestedAction[] = [
|
||||
{
|
||||
title: "Are there any exposed S3",
|
||||
|
||||
@@ -69,7 +69,8 @@ export const AddGroupForm = ({
|
||||
if (data?.errors && data.errors.length > 0) {
|
||||
data.errors.forEach((error: ApiError) => {
|
||||
const errorMessage = error.detail;
|
||||
switch (error.source.pointer) {
|
||||
const pointer = error.source?.pointer;
|
||||
switch (pointer) {
|
||||
case "/data/attributes/name":
|
||||
form.setError("name", {
|
||||
type: "server",
|
||||
|
||||
@@ -105,7 +105,8 @@ export const EditGroupForm = ({
|
||||
if (data?.errors && data.errors.length > 0) {
|
||||
data.errors.forEach((error: ApiError) => {
|
||||
const errorMessage = error.detail;
|
||||
switch (error.source.pointer) {
|
||||
const pointer = error.source?.pointer;
|
||||
switch (pointer) {
|
||||
case "/data/attributes/name":
|
||||
form.setError("name", {
|
||||
type: "server",
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
CustomInput,
|
||||
} from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { permissionFormFields } from "@/lib";
|
||||
import { getErrorMessage, permissionFormFields } from "@/lib";
|
||||
import { addRoleFormSchema, ApiError } from "@/types";
|
||||
|
||||
type FormValues = z.infer<typeof addRoleFormSchema>;
|
||||
@@ -113,7 +113,8 @@ export const AddRoleForm = ({
|
||||
if (data?.errors && data.errors.length > 0) {
|
||||
data.errors.forEach((error: ApiError) => {
|
||||
const errorMessage = error.detail;
|
||||
switch (error.source.pointer) {
|
||||
const pointer = error.source?.pointer;
|
||||
switch (pointer) {
|
||||
case "/data/attributes/name":
|
||||
form.setError("name", {
|
||||
type: "server",
|
||||
@@ -139,7 +140,7 @@ export const AddRoleForm = ({
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error",
|
||||
description: "An unexpected error occurred. Please try again.",
|
||||
description: getErrorMessage(error),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
CustomInput,
|
||||
} from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { permissionFormFields } from "@/lib";
|
||||
import { getErrorMessage, permissionFormFields } from "@/lib";
|
||||
import { ApiError, editRoleFormSchema } from "@/types";
|
||||
|
||||
type FormValues = z.infer<typeof editRoleFormSchema>;
|
||||
@@ -133,7 +133,8 @@ export const EditRoleForm = ({
|
||||
if (data?.errors && data.errors.length > 0) {
|
||||
data.errors.forEach((error: ApiError) => {
|
||||
const errorMessage = error.detail;
|
||||
switch (error.source.pointer) {
|
||||
const pointer = error.source?.pointer;
|
||||
switch (pointer) {
|
||||
case "/data/attributes/name":
|
||||
form.setError("name", {
|
||||
type: "server",
|
||||
@@ -159,7 +160,7 @@ export const EditRoleForm = ({
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error",
|
||||
description: "An unexpected error occurred. Please try again.",
|
||||
description: getErrorMessage(error),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,8 @@ export const useFormServerErrors = <T extends Record<string, any>>(
|
||||
) => {
|
||||
errors.forEach((error: ApiError) => {
|
||||
const errorMessage = error.detail;
|
||||
const fieldName = errorMapping?.[error.source.pointer];
|
||||
const pointer = error.source?.pointer;
|
||||
const fieldName = pointer ? errorMapping?.[pointer] : undefined;
|
||||
|
||||
if (fieldName && fieldName in form.formState.defaultValues!) {
|
||||
form.setError(fieldName as any, {
|
||||
|
||||
+49
-9
@@ -348,10 +348,27 @@ export const handleApiResponse = async (
|
||||
parse = true,
|
||||
) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null);
|
||||
const errorDetail = errorData?.errors?.[0]?.detail;
|
||||
// Read error body safely; prefer JSON, fallback to plain text
|
||||
const rawErrorText = await response.text().catch(() => "");
|
||||
let errorData: any = null;
|
||||
try {
|
||||
errorData = rawErrorText ? JSON.parse(rawErrorText) : null;
|
||||
} catch {
|
||||
errorData = null;
|
||||
}
|
||||
|
||||
// Special handling for server errors (500+)
|
||||
const errorsArray = Array.isArray(errorData?.errors)
|
||||
? (errorData.errors as any[])
|
||||
: undefined;
|
||||
const errorDetail =
|
||||
errorsArray?.[0]?.detail ||
|
||||
errorData?.error ||
|
||||
errorData?.message ||
|
||||
(rawErrorText && rawErrorText.trim()) ||
|
||||
response.statusText ||
|
||||
"Oops! Something went wrong.";
|
||||
|
||||
//5XX errors
|
||||
if (response.status >= 500) {
|
||||
throw new Error(
|
||||
errorDetail ||
|
||||
@@ -359,14 +376,37 @@ export const handleApiResponse = async (
|
||||
);
|
||||
}
|
||||
|
||||
// Client errors (4xx)
|
||||
throw new Error(
|
||||
errorDetail ||
|
||||
`Request failed (${response.status}): ${response.statusText}`,
|
||||
);
|
||||
return errorsArray
|
||||
? { error: errorDetail, errors: errorsArray, status: response.status }
|
||||
: ({ error: errorDetail, status: response.status } as any);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
// Handle empty or no-content responses gracefully (e.g., 204, empty body)
|
||||
if (response.status === 204) {
|
||||
if (pathToRevalidate && pathToRevalidate !== "") {
|
||||
revalidatePath(pathToRevalidate);
|
||||
}
|
||||
return { success: true, status: response.status } as any;
|
||||
}
|
||||
|
||||
// Read raw text to determine if there's a body to parse
|
||||
const rawText = await response.text();
|
||||
const hasBody = rawText && rawText.trim().length > 0;
|
||||
|
||||
if (!hasBody) {
|
||||
if (pathToRevalidate && pathToRevalidate !== "") {
|
||||
revalidatePath(pathToRevalidate);
|
||||
}
|
||||
return { success: true, status: response.status } as any;
|
||||
}
|
||||
|
||||
let data: any;
|
||||
try {
|
||||
data = JSON.parse(rawText);
|
||||
} catch (e) {
|
||||
// If body isn't valid JSON, return as text payload
|
||||
data = { data: rawText };
|
||||
}
|
||||
|
||||
if (pathToRevalidate && pathToRevalidate !== "") {
|
||||
revalidatePath(pathToRevalidate);
|
||||
|
||||
Reference in New Issue
Block a user