feat: upgrade to Next.js 16.1.3 (#9826)

This commit is contained in:
Alejandro Bailo
2026-01-27 14:02:31 +01:00
committed by GitHub
parent 6bb8dc6168
commit 065827cd38
49 changed files with 4699 additions and 4166 deletions

View File

@@ -1,21 +0,0 @@
.now/*
*.css
.changeset
dist
esm/*
public/*
tests/*
scripts/*
*.config.js
.DS_Store
node_modules
coverage
.next
build
next-env.d.ts
!.commitlintrc.cjs
!.lintstagedrc.cjs
!jest.config.js
!plopfile.js
!react-shim.js
!tsup.config.ts

View File

@@ -1,53 +0,0 @@
module.exports = {
env: {
node: true,
es2021: true,
},
parser: "@typescript-eslint/parser",
plugins: ["prettier", "@typescript-eslint", "simple-import-sort", "jsx-a11y"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:security/recommended-legacy",
"plugin:jsx-a11y/recommended",
"eslint-config-prettier",
"prettier",
"next/core-web-vitals",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
rules: {
// console.error are allowed but no console.log
"no-console": ["error", { allow: ["error"] }],
eqeqeq: 2,
quotes: ["error", "double", "avoid-escape"],
"@typescript-eslint/no-explicit-any": "off",
"security/detect-object-injection": "off",
"prettier/prettier": [
"error",
{
endOfLine: "auto",
tabWidth: 2,
useTabs: false,
},
],
"eol-last": ["error", "always"],
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"jsx-a11y/anchor-is-valid": [
"error",
{
components: ["Link"],
specialLink: ["hrefLeft", "hrefRight"],
aspects: ["invalidHref", "preferButton"],
},
],
"jsx-a11y/alt-text": "error",
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
},
};

View File

@@ -2,7 +2,7 @@
All notable changes to the **Prowler UI** are documented in this file.
## [1.18.0] (Prowler v5.18.0)
## [1.18.0] (Prowler UNRELEASED)
### 🔄 Changed
@@ -25,6 +25,7 @@ All notable changes to the **Prowler UI** are documented in this file.
### 🔄 Changed
- Upgrade Next.js from 15.5.9 to 16.1.3 with ESLint 9 flat config migration [(#9826)](https://github.com/prowler-cloud/prowler/pull/9826)
- Refactor Lighthouse AI MCP tool filtering from blacklist to whitelist approach for improved security [(#9802)](https://github.com/prowler-cloud/prowler/pull/9802)
- Refactor ScatterPlot as reusable generic component with TypeScript generics [(#9664)](https://github.com/prowler-cloud/prowler/pull/9664)
- Rename resource_group filter to group in Resources page and Overview cards [(#9492)](https://github.com/prowler-cloud/prowler/pull/9492)

View File

@@ -89,7 +89,7 @@ export const createApiKey = async (
const data = (await handleApiResponse(response)) as CreateApiKeyResponse;
// Revalidate the api-keys list
revalidateTag("api-keys");
revalidateTag("api-keys", "max");
return { data };
} catch (error) {
@@ -138,7 +138,7 @@ export const updateApiKey = async (
const data = (await handleApiResponse(response)) as SingleApiKeyResponse;
// Revalidate the api-keys list
revalidateTag("api-keys");
revalidateTag("api-keys", "max");
return { data };
} catch (error) {
@@ -171,7 +171,7 @@ export const revokeApiKey = async (
}
// Revalidate the api-keys list
revalidateTag("api-keys");
revalidateTag("api-keys", "max");
return { success: true };
} catch (error) {

View File

@@ -82,7 +82,7 @@ export const createNewUser = async (formData: SignUpFormData) => {
}
return parsedResponse;
} catch (error) {
} catch (_error) {
return {
errors: [
{
@@ -127,7 +127,7 @@ export const getToken = async (formData: SignInFormData) => {
accessToken,
refreshToken,
};
} catch (error) {
} catch (_error) {
throw new Error("Error in trying to get token");
}
};

View File

@@ -1,7 +1,7 @@
"use server";
import { extract } from "@extractus/feed-extractor";
import { unstable_cache } from "next/cache";
import Parser from "rss-parser";
import { z } from "zod";
import type { FeedError, FeedItem, FeedSource, ParsedFeed } from "./types";
@@ -42,44 +42,24 @@ function getFeedSources(): FeedSource[] {
async function parseSingleFeed(
source: FeedSource,
): Promise<{ items: FeedItem[]; error?: FeedError }> {
const parser = new Parser({
timeout: 10000,
headers: {
"User-Agent": "Prowler-UI/1.0",
},
});
try {
const feed = await parser.parseURL(source.url);
const feed = await extract(source.url);
// Map RSS items to our FeedItem type
const items: FeedItem[] = (feed.items || []).map((item) => {
// Validate and parse date with fallback to current date
const parsePubDate = (): string => {
const dateString = item.isoDate || item.pubDate;
if (!dateString) return new Date().toISOString();
const parsed = new Date(dateString);
return isNaN(parsed.getTime())
? new Date().toISOString()
: parsed.toISOString();
};
return {
id: item.guid || item.link || `${source.id}-${item.title}`,
title: item.title || "Untitled",
description:
item.contentSnippet || item.content || item.description || "",
link: item.link || "",
pubDate: parsePubDate(),
sourceId: source.id,
sourceName: source.name,
sourceType: source.type,
author: item.creator || item.author,
categories: item.categories || [],
contentSnippet: item.contentSnippet || undefined,
};
});
const items: FeedItem[] = (feed.entries || []).map((entry) => ({
id: entry.id || entry.link || `${source.id}-${entry.title}`,
title: entry.title || "Untitled",
description: entry.description || "",
link: entry.link || "",
pubDate: entry.published
? new Date(entry.published).toISOString()
: new Date().toISOString(),
sourceId: source.id,
sourceName: source.name,
sourceType: source.type,
author: undefined,
categories: [],
contentSnippet: entry.description?.slice(0, 500),
}));
return { items };
} catch (error) {

View File

@@ -404,7 +404,7 @@ export const pollConnectionTestStatus = async (
error: pollResult.message || "Connection test failed.",
};
}
} catch (error) {
} catch (_error) {
return { success: false, error: "Failed to check connection test status." };
}
};

View File

@@ -210,7 +210,7 @@ export const initiateSamlAuth = async (email: string) => {
errorData.errors?.[0]?.detail ||
"An error occurred during SAML authentication.",
};
} catch (error) {
} catch (_error) {
return {
success: false,
error: "Failed to connect to authentication service.",

View File

@@ -1,4 +1,4 @@
const COMPLIANCE_WATCHLIST_OVERVIEW_TYPE = {
export const COMPLIANCE_WATCHLIST_OVERVIEW_TYPE = {
WATCHLIST_OVERVIEW: "compliance-watchlist-overviews",
} as const;

View File

@@ -18,7 +18,7 @@ export type VerticalStepProps = {
title?: ReactNode;
};
const STEP_COLORS = {
export const STEP_COLORS = {
primary: "primary",
secondary: "secondary",
success: "success",

View File

@@ -7,7 +7,7 @@ import { ConnectLLMProvider } from "@/components/lighthouse/connect-llm-provider
import { SelectBedrockAuthMethod } from "@/components/lighthouse/select-bedrock-auth-method";
import type { LighthouseProvider } from "@/types/lighthouse";
const BEDROCK_AUTH_MODES = {
export const BEDROCK_AUTH_MODES = {
IAM: "iam",
API_KEY: "api_key",
} as const;

View File

@@ -50,14 +50,12 @@ export async function GET(req: Request) {
return NextResponse.redirect(new URL("/", baseUrl));
} catch (error) {
// eslint-disable-next-line no-console
console.error("SignIn error:", error);
return NextResponse.redirect(
new URL("/sign-in?error=AuthenticationFailed", baseUrl),
);
}
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error in Github callback:", error);
return NextResponse.json(
{ error: (error as Error).message },

View File

@@ -50,14 +50,12 @@ export async function GET(req: Request) {
return NextResponse.redirect(new URL("/", baseUrl));
} catch (error) {
// eslint-disable-next-line no-console
console.error("SignIn error:", error);
return NextResponse.redirect(
new URL("/sign-in?error=AuthenticationFailed", baseUrl),
);
}
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error in Google callback:", error);
return NextResponse.json(
{ error: (error as Error).message },

View File

@@ -36,7 +36,7 @@ const MAP_COLORS = {
pointHover: "var(--bg-fail)",
} as const;
const RISK_LEVELS = {
export const RISK_LEVELS = {
LOW_HIGH: "low-high",
HIGH: "high",
CRITICAL: "critical",

View File

@@ -381,7 +381,6 @@ export function ThreatMap({
}
svg.appendChild(pointsGroup);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
dimensions,
data.locations,

View File

@@ -130,7 +130,7 @@ export const JiraIntegrationForm = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",

View File

@@ -78,7 +78,7 @@ export const JiraIntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",
@@ -109,7 +109,7 @@ export const JiraIntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",
@@ -160,7 +160,7 @@ export const JiraIntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",

View File

@@ -92,7 +92,7 @@ export const S3IntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",
@@ -123,7 +123,7 @@ export const S3IntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",
@@ -158,7 +158,7 @@ export const S3IntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",

View File

@@ -93,7 +93,7 @@ export const SecurityHubIntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",
@@ -125,7 +125,7 @@ export const SecurityHubIntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",
@@ -176,7 +176,7 @@ export const SecurityHubIntegrationsManager = ({
description: result.error,
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",

View File

@@ -80,7 +80,7 @@ export const SendInvitationForm = ({
const invitationId = data?.data?.id || "";
router.push(`/invitations/check-details/?id=${invitationId}`);
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",

View File

@@ -268,7 +268,6 @@ export const VerticalSteps = React.forwardRef<
"pointer-events-none absolute top-[calc(64px*var(--idx)+1)] left-3 flex h-1/2 -translate-y-1/3 items-center px-4",
)}
style={{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
"--idx": stepIdx,
}}

View File

@@ -63,7 +63,6 @@ function InputGroupAddon({
...props
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
return (
/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */
<div
role="group"
data-slot="input-group-addon"

View File

@@ -723,7 +723,7 @@ export const PromptInput = ({
controller.textInput.clear();
}
}
} catch (error) {
} catch (_error) {
// Don't clear on error - user may want to retry
}
});

View File

@@ -7,7 +7,7 @@ export const LighthouseBanner = async () => {
const isConfigured = await isLighthouseConfigured();
return <LighthouseBannerClient isConfigured={isConfigured} />;
} catch (error) {
} catch (_error) {
return null;
}
};

View File

@@ -95,7 +95,7 @@ export const AddGroupForm = ({
description: "The group was created successfully.",
});
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",

View File

@@ -131,7 +131,7 @@ export const EditGroupForm = ({
});
router.push("/manage-groups");
}
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Error",

View File

@@ -157,7 +157,6 @@ export const ConnectAccountForm = () => {
router.push(`/providers/add-credentials?type=${providerType}&id=${id}`);
}
} catch (error: unknown) {
// eslint-disable-next-line no-console
console.error("Error during submission:", error);
toast({
variant: "destructive",

View File

@@ -153,7 +153,7 @@ export const TestConnectionForm = ({
setIsRedirecting(true);
router.push("/scans");
}
} catch (error) {
} catch (_error) {
form.setError("providerId", {
type: "server",
message: "An unexpected error occurred. Please try again.",
@@ -198,7 +198,6 @@ export const TestConnectionForm = ({
`/providers/add-credentials?type=${providerType}&id=${providerId}`,
);
} catch (error) {
// eslint-disable-next-line no-console
console.error("Failed to delete credentials:", error);
} finally {
setIsResettingCredentials(false);

View File

@@ -267,7 +267,6 @@ export const VerticalSteps = React.forwardRef<
"pointer-events-none absolute top-[calc(64px*var(--idx)+1)] left-3 flex h-1/2 -translate-y-1/3 items-center px-4",
)}
style={{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
"--idx": stepIdx,
}}

View File

@@ -268,7 +268,6 @@ export const VerticalSteps = React.forwardRef<
"pointer-events-none absolute top-[calc(64px*var(--idx)+1)] left-3 flex h-1/2 -translate-y-1/3 items-center px-4",
)}
style={{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
"--idx": stepIdx,
}}

View File

@@ -49,7 +49,6 @@ export const DataTableRowDetails = ({ entityId }: { entityId: string }) => {
});
}
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error in fetchScanDetails:", error);
} finally {
setIsLoading(false);

View File

@@ -7,7 +7,7 @@ import { cn } from "@/lib/utils";
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip";
const INFO_FIELD_VARIANTS = {
export const INFO_FIELD_VARIANTS = {
default: "default",
simple: "simple",
transparent: "transparent",

View File

@@ -351,8 +351,7 @@ function getPayloadConfigFromPayload(
}
return configLabelKey in config
? // eslint-disable-next-line security/detect-object-injection
config[configLabelKey]
? config[configLabelKey]
: config[key as keyof typeof config];
}

View File

@@ -15,7 +15,7 @@ type ToasterToast = ToastProps & {
action?: ToastActionElement;
};
const actionTypes = {
export const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",

View File

@@ -15,6 +15,14 @@
"strategy": "installed",
"generatedAt": "2025-12-15T08:24:46.195Z"
},
{
"section": "dependencies",
"name": "@extractus/feed-extractor",
"from": "7.1.7",
"to": "7.1.7",
"strategy": "installed",
"generatedAt": "2026-01-19T15:06:16.239Z"
},
{
"section": "dependencies",
"name": "@heroui/react",
@@ -74,10 +82,10 @@
{
"section": "dependencies",
"name": "@next/third-parties",
"from": "15.3.5",
"to": "15.5.9",
"from": "15.5.9",
"to": "16.1.3",
"strategy": "installed",
"generatedAt": "2025-12-15T11:18:25.093Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "dependencies",
@@ -458,10 +466,10 @@
{
"section": "dependencies",
"name": "next",
"from": "15.5.7",
"to": "15.5.9",
"from": "15.5.9",
"to": "16.1.3",
"strategy": "installed",
"generatedAt": "2025-12-15T11:18:25.093Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "dependencies",
@@ -490,10 +498,10 @@
{
"section": "dependencies",
"name": "react",
"from": "19.2.1",
"to": "19.2.2",
"from": "19.2.2",
"to": "19.2.3",
"strategy": "installed",
"generatedAt": "2025-12-15T11:18:25.093Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "dependencies",
@@ -506,10 +514,10 @@
{
"section": "dependencies",
"name": "react-dom",
"from": "19.2.1",
"to": "19.2.2",
"from": "19.2.2",
"to": "19.2.3",
"strategy": "installed",
"generatedAt": "2025-12-15T11:18:25.093Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "dependencies",
@@ -543,14 +551,6 @@
"strategy": "installed",
"generatedAt": "2025-12-16T08:33:37.278Z"
},
{
"section": "dependencies",
"name": "rss-parser",
"from": "3.13.0",
"to": "3.13.0",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
},
{
"section": "dependencies",
"name": "server-only",
@@ -663,6 +663,14 @@
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
},
{
"section": "devDependencies",
"name": "@eslint/eslintrc",
"from": "3.3.3",
"to": "3.3.3",
"strategy": "installed",
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
"name": "@iconify/react",
@@ -671,6 +679,14 @@
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
},
{
"section": "devDependencies",
"name": "@next/eslint-plugin-next",
"from": "16.1.3",
"to": "16.1.3",
"strategy": "installed",
"generatedAt": "2026-01-19T14:49:57.304Z"
},
{
"section": "devDependencies",
"name": "@playwright/test",
@@ -707,17 +723,17 @@
"section": "devDependencies",
"name": "@types/react",
"from": "19.1.13",
"to": "19.1.13",
"to": "19.2.8",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
"name": "@types/react-dom",
"from": "19.1.9",
"to": "19.1.9",
"to": "19.2.3",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
@@ -747,17 +763,17 @@
"section": "devDependencies",
"name": "@typescript-eslint/eslint-plugin",
"from": "7.18.0",
"to": "7.18.0",
"to": "8.53.0",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
"name": "@typescript-eslint/parser",
"from": "7.18.0",
"to": "7.18.0",
"to": "8.53.0",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
@@ -771,9 +787,9 @@
"section": "devDependencies",
"name": "babel-plugin-react-compiler",
"from": "19.1.0-rc.3",
"to": "19.1.0-rc.3",
"to": "1.0.0",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
@@ -787,17 +803,17 @@
"section": "devDependencies",
"name": "eslint",
"from": "8.57.1",
"to": "8.57.1",
"to": "9.39.2",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
"name": "eslint-config-next",
"from": "15.5.7",
"to": "15.5.9",
"from": "15.5.9",
"to": "16.1.3",
"strategy": "installed",
"generatedAt": "2025-12-15T11:18:25.093Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
@@ -875,9 +891,17 @@
"section": "devDependencies",
"name": "eslint-plugin-unused-imports",
"from": "3.2.0",
"to": "3.2.0",
"to": "4.3.0",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
"name": "globals",
"from": "17.0.0",
"to": "17.0.0",
"strategy": "installed",
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",

135
ui/eslint.config.mjs Normal file
View File

@@ -0,0 +1,135 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import tsPlugin from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
import prettierPlugin from "eslint-plugin-prettier";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import jsxA11y from "eslint-plugin-jsx-a11y";
import security from "eslint-plugin-security";
import unusedImports from "eslint-plugin-unused-imports";
import nextPlugin from "@next/eslint-plugin-next";
import reactPlugin from "eslint-plugin-react";
import reactHooksPlugin from "eslint-plugin-react-hooks";
import globals from "globals";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export default [
// Global ignores (replaces .eslintignore)
{
ignores: [
".now/**",
"**/*.css",
".changeset/**",
"dist/**",
"esm/**",
"public/**",
"tests/**",
"scripts/**",
"*.config.js",
"*.config.mjs",
".DS_Store",
"node_modules/**",
"coverage/**",
".next/**",
"build/**",
"next-env.d.ts",
],
},
// TypeScript and React files configuration
{
files: ["**/*.{ts,tsx,js,jsx}"],
plugins: {
"@typescript-eslint": tsPlugin,
"@next/next": nextPlugin,
react: reactPlugin,
"react-hooks": reactHooksPlugin,
prettier: prettierPlugin,
"simple-import-sort": simpleImportSort,
"jsx-a11y": jsxA11y,
security: security,
"unused-imports": unusedImports,
},
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
globals: {
...globals.browser,
...globals.node,
...globals.es2021,
React: "readonly",
},
},
settings: {
react: {
version: "detect",
},
},
rules: {
// Console rules - allow console.error but no console.log
"no-console": ["error", { allow: ["error"] }],
eqeqeq: 2,
quotes: ["error", "double", "avoid-escape"],
// TypeScript rules
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
// Security
"security/detect-object-injection": "off",
// Prettier integration
"prettier/prettier": [
"error",
{
endOfLine: "auto",
tabWidth: 2,
useTabs: false,
},
],
"eol-last": ["error", "always"],
// Import sorting
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
// Unused imports
"unused-imports/no-unused-imports": "error",
// Accessibility
"jsx-a11y/anchor-is-valid": [
"error",
{
components: ["Link"],
specialLink: ["hrefLeft", "hrefRight"],
aspects: ["invalidHref", "preferButton"],
},
],
"jsx-a11y/alt-text": "error",
// React Hooks
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
// Next.js specific rules
"@next/next/no-html-link-for-pages": "error",
"@next/next/no-img-element": "warn",
"@next/next/no-sync-scripts": "error",
},
},
];

View File

@@ -21,7 +21,6 @@ export const useLocalStorage = (
setState(JSON.parse(value));
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
}, [key]);
@@ -39,7 +38,6 @@ export const useLocalStorage = (
window.localStorage.setItem(key, JSON.stringify(valueToStore));
setState(valueToStore);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
};

View File

@@ -12,7 +12,7 @@ import {
startProgress,
} from "@/components/ui/navigation-progress/use-navigation-progress";
const NAVIGATION_TYPE = {
export const NAVIGATION_TYPE = {
PUSH: "push",
REPLACE: "replace",
TRAVERSE: "traverse",

View File

@@ -184,7 +184,7 @@ const downloadFile = async (
title: "Download Complete",
description: successMessage,
});
} catch (error) {
} catch (_error) {
toast({
variant: "destructive",
title: "Download Failed",
@@ -263,7 +263,6 @@ export const checkTaskStatus = async (
const task = await getTask(taskId);
if (task.error) {
// eslint-disable-next-line no-console
console.error(`Error retrieving task: ${task.error}`);
return { completed: false, error: task.error };
}

View File

@@ -124,7 +124,7 @@ function parseLineRange(lineRange: string): {
// Handle formats: "10-15", "10:15", "10"
// Safe regex: anchored pattern for line numbers only (no ReDoS risk)
// eslint-disable-next-line security/detect-unsafe-regex
const match = lineRange.match(/^(\d+)[-:]?(\d+)?$/);
if (match) {
const startLine = parseInt(match[1], 10);

View File

@@ -106,7 +106,7 @@ export const runTestConnection = async ({
config?.errorMessage || `Failed to connect to ${integrationType}.`;
onError?.(pollResult.error || defaultError);
}
} catch (error) {
} catch (_error) {
onError?.(
"Failed to start connection test. You can try manually using the Test Connection button.",
);

View File

@@ -125,7 +125,7 @@ export const handleApiResponse = async (
let data: any;
try {
data = JSON.parse(rawText);
} catch (e) {
} catch (_e) {
// If body isn't valid JSON, return as text payload
data = { data: rawText };
}

View File

@@ -168,7 +168,7 @@ export const convertToYaml = (config: string | object): string => {
// If it's not JSON, assume it's already YAML
return config;
}
} catch (error) {
} catch (_error) {
return config.toString();
}
};

View File

@@ -41,9 +41,8 @@ const nextConfig = {
output: "standalone",
outputFileTracingRoot: __dirname,
}),
experimental: {
reactCompiler: true,
},
// React Compiler is now stable in Next.js 16
reactCompiler: true,
turbopack: {
root: __dirname,
},

View File

@@ -2,7 +2,7 @@
"name": "prowler-next-app",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"dev": "next dev",
"build": "next build",
"start": "next start",
"start:standalone": "node .next/standalone/server.js",
@@ -10,8 +10,8 @@
"postinstall": "node scripts/postinstall.js",
"typecheck": "tsc",
"healthcheck": "pnpm run typecheck && pnpm run lint:check",
"lint:check": "eslint . --ext .ts,.tsx -c .eslintrc.cjs",
"lint:fix": "eslint . --ext .ts,.tsx -c .eslintrc.cjs --fix",
"lint:check": "eslint .",
"lint:fix": "eslint . --fix",
"format:check": "./node_modules/.bin/prettier --check ./app",
"format:write": "./node_modules/.bin/prettier --config .prettierrc.json --write ./app",
"prepare": "husky",
@@ -26,6 +26,7 @@
"dependencies": {
"@ai-sdk/react": "2.0.111",
"@aws-sdk/client-bedrock-runtime": "3.948.0",
"@extractus/feed-extractor": "7.1.7",
"@heroui/react": "2.8.4",
"@hookform/resolvers": "5.2.2",
"@internationalized/date": "3.10.0",
@@ -33,7 +34,7 @@
"@langchain/core": "1.1.15",
"@langchain/mcp-adapters": "1.0.3",
"@langchain/openai": "1.1.3",
"@next/third-parties": "15.5.9",
"@next/third-parties": "16.1.3",
"@radix-ui/react-alert-dialog": "1.1.14",
"@radix-ui/react-avatar": "1.1.11",
"@radix-ui/react-checkbox": "1.3.3",
@@ -81,18 +82,17 @@
"lucide-react": "0.543.0",
"marked": "15.0.12",
"nanoid": "5.1.6",
"next": "15.5.9",
"next": "16.1.3",
"next-auth": "5.0.0-beta.30",
"next-themes": "0.2.1",
"radix-ui": "1.4.2",
"react": "19.2.2",
"react": "19.2.3",
"react-day-picker": "9.13.0",
"react-dom": "19.2.2",
"react-dom": "19.2.3",
"react-hook-form": "7.62.0",
"react-markdown": "10.1.0",
"recharts": "2.15.4",
"require-in-the-middle": "8.0.1",
"rss-parser": "3.13.0",
"server-only": "0.0.1",
"sharp": "0.33.5",
"shiki": "3.20.0",
@@ -109,23 +109,25 @@
"zustand": "5.0.8"
},
"devDependencies": {
"@eslint/eslintrc": "3.3.3",
"@iconify/react": "5.2.1",
"@next/eslint-plugin-next": "16.1.3",
"@playwright/test": "1.56.1",
"@types/d3": "7.4.3",
"@types/geojson": "7946.0.16",
"@types/node": "24.10.8",
"@types/react": "19.1.13",
"@types/react-dom": "19.1.9",
"@types/react": "19.2.8",
"@types/react-dom": "19.2.3",
"@types/topojson-client": "3.1.5",
"@types/topojson-specification": "1.0.5",
"@types/uuid": "10.0.0",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
"@typescript-eslint/eslint-plugin": "8.53.0",
"@typescript-eslint/parser": "8.53.0",
"autoprefixer": "10.4.19",
"babel-plugin-react-compiler": "19.1.0-rc.3",
"babel-plugin-react-compiler": "1.0.0",
"dotenv-expand": "12.0.3",
"eslint": "8.57.1",
"eslint-config-next": "15.5.9",
"eslint": "9.39.2",
"eslint-config-next": "16.1.3",
"eslint-config-prettier": "10.1.5",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-jsx-a11y": "6.10.2",
@@ -135,7 +137,8 @@
"eslint-plugin-react-hooks": "7.0.1",
"eslint-plugin-security": "3.0.1",
"eslint-plugin-simple-import-sort": "12.1.1",
"eslint-plugin-unused-imports": "3.2.0",
"eslint-plugin-unused-imports": "4.3.0",
"globals": "17.0.0",
"husky": "9.1.7",
"lint-staged": "15.5.2",
"postcss": "8.4.38",
@@ -150,12 +153,12 @@
"overrides": {
"@react-types/shared": "3.26.0",
"@internationalized/date": "3.10.0",
"alert>react": "19.2.2",
"alert>react-dom": "19.2.2",
"@react-aria/ssr>react": "19.2.2",
"@react-aria/ssr>react-dom": "19.2.2",
"@react-aria/visually-hidden>react": "19.2.2",
"@react-aria/interactions>react": "19.2.2"
"alert>react": "19.2.3",
"alert>react-dom": "19.2.3",
"@react-aria/ssr>react": "19.2.3",
"@react-aria/ssr>react-dom": "19.2.3",
"@react-aria/visually-hidden>react": "19.2.3",
"@react-aria/interactions>react": "19.2.3"
}
},
"version": "0.0.1"

8337
ui/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@ const isPublicRoute = (pathname: string): boolean => {
return publicRoutes.some((route) => pathname.startsWith(route));
};
// NextAuth's auth() wrapper - renamed from middleware to proxy
export default auth((req: NextRequest & { auth: any }) => {
const { pathname } = req.nextUrl;
const user = req.auth?.user;

View File

@@ -6,13 +6,19 @@
"incremental": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"module": "esnext",
"moduleResolution": "bundler",
"noEmit": true,
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
"@/*": [
"./*"
]
},
"plugins": [
{
@@ -24,12 +30,15 @@
"strict": true,
"target": "es5"
},
"exclude": ["node_modules"],
"exclude": [
"node_modules"
],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"images.d.ts"
"images.d.ts",
".next/dev/types/**/*.ts"
]
}

View File

@@ -72,7 +72,7 @@ export interface AttackPathScansResponse {
}
// Data type constants
const DATA_TYPES = {
export const DATA_TYPES = {
STRING: "string",
NUMBER: "number",
BOOLEAN: "boolean",
@@ -168,7 +168,7 @@ export interface AttackPathQueryResult {
}
// Finding severity and status constants
const FINDING_SEVERITIES = {
export const FINDING_SEVERITIES = {
CRITICAL: "critical",
HIGH: "high",
MEDIUM: "medium",
@@ -179,7 +179,7 @@ const FINDING_SEVERITIES = {
type FindingSeverity =
(typeof FINDING_SEVERITIES)[keyof typeof FINDING_SEVERITIES];
const FINDING_STATUSES = {
export const FINDING_STATUSES = {
PASS: "PASS",
FAIL: "FAIL",
MANUAL: "MANUAL",