diff --git a/.prettierignore b/.prettierignore index 65e5d0cc..dae24512 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,5 @@ /.github /node_modules /dist +/charts +/client \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index dcb72794..a20502b7 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,4 @@ { "singleQuote": true, "trailingComma": "all" -} \ No newline at end of file +} diff --git a/README.md b/README.md index 85d419b1..b054c8ba 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,12 @@ Broken Crystals is a benchmark application that uses modern technologies and implements a set of common security vulnerabilities. The application contains: + - React based web client - FE - http://localhost:8090 - BE - http://localhost:3000 - NodeJS server that serves the React client and provides both OpenAPI and GraphQL endpoints. -The full API documentation is available via swagger or GraphQL: + The full API documentation is available via swagger or GraphQL: - Swagger UI - http://localhost:8090/swagger - Swagger JSON file - http://localhost:8090/swagger-json - GraphiQL UI - http://localhost:8090/graphiql @@ -30,8 +31,11 @@ docker-compose --file=docker-compose.local.yml up -d #rebuild dockers docker-compose --file=docker-compose.local.yml up -d --build ``` + ## Running application with helm chart + Helm command example: + ```bash $ helm upgrade --install --namespace distributor broken \ --set snifferApiURL=https://hotel.playground.neuralegion.com \ @@ -49,6 +53,7 @@ $ helm upgrade --install --namespace distributor broken \ ``` Optionally, you can test your installation: + ```bash $ helm test broken --namespace=distributor --logs ``` @@ -69,7 +74,6 @@ $ helm test broken --namespace=distributor --logs **ingress.url** - Domain name that will be used to access app from Internet. - ## Running tests by [SecTester](https://github.com/NeuraLegion/sectester-js/) In the path [`./test`](./test) you can find tests to run with Jest. @@ -96,7 +100,8 @@ Full configuration & usage examples can be found in our [demo project](https://g ## Vulnerabilities Overview -* **Broken JWT Authentication** - The application includes multiple endpoints that generate and validate several types of JWT tokens. The main login API, used by the UI, is utilizing one of the endpoints while others are available via direct call and described in Swagger. +- **Broken JWT Authentication** - The application includes multiple endpoints that generate and validate several types of JWT tokens. The main login API, used by the UI, is utilizing one of the endpoints while others are available via direct call and described in Swagger. + - **No Algorithm bypass** - Bypasses the JWT authentication by using the “None” algorithm (implemented in main login and API authorization code). - **RSA to HMAC** - Changes the algorithm to use a “HMAC” variation and signs with the public key of the application to bypass the authentication (implemented in main login and API authorization code). - **Invalid Signature** - Changes the signature of the JWT to something different and bypasses the authentication (implemented in main login and API authorization code). @@ -107,106 +112,111 @@ Full configuration & usage examples can be found in our [demo project](https://g - **JKU Rogue Key** - Uses our publicly available JSON to check if JWT is properly signed after we set the Header in JWT to point to our JSON and sign the JWT with our private key (implemented in designated endpoint as described in Swagger). - **JWK Rogue Key** - We make a new JSON with empty values, hash it, and set it directly in the Header and we then use our private key to sign the JWT (implemented in designated endpoint as described in Swagger). -* **Brute Force Login** - Checks if the application user is using a weak password. The default setup contains user = _admin_ with password = _admin_ +- **Brute Force Login** - Checks if the application user is using a weak password. The default setup contains user = _admin_ with password = _admin_ + +- **Common Files** - Tries to find common files that shouldn’t be publicly exposed (such as “phpinfo”, “.htaccess”, “ssh-key.priv”, etc…). The application contains .htacess and Nginx.conf files under the client's root directory and additional files can be added by placing them under the public/public directory and running a build of the client. -* **Common Files** - Tries to find common files that shouldn’t be publicly exposed (such as “phpinfo”, “.htaccess”, “ssh-key.priv”, etc…). The application contains .htacess and Nginx.conf files under the client's root directory and additional files can be added by placing them under the public/public directory and running a build of the client. +- **Cookie Security** - Checks if the cookie has the “secure” and HTTP only flags. The application returns two cookies (session and bc-calls-counter cookie), both without secure and HttpOnly flags. -* **Cookie Security** - Checks if the cookie has the “secure” and HTTP only flags. The application returns two cookies (session and bc-calls-counter cookie), both without secure and HttpOnly flags. +- **Cross-Site Request Forgery (CSRF)** -* **Cross-Site Request Forgery (CSRF)** - - Checks if a form holds anti-CSRF tokens, misconfigured “CORS” and misconfigured “Origin” header - the application returns "Access-Control-Allow-Origin: *" header for all requests. The behavior can be configured in the /main.ts file. + - Checks if a form holds anti-CSRF tokens, misconfigured “CORS” and misconfigured “Origin” header - the application returns "Access-Control-Allow-Origin: \*" header for all requests. The behavior can be configured in the /main.ts file. - The same form with both authenticated and unauthenticated user - the _Email subscription_ UI forms can be used for testing this vulnerability. - Different form for an authenticated and unauthenticated user - the _Add testimonial_ form can be used for testing. The forms are only available to authenticated users. -* **Cross-Site Scripting (XSS)** - +- **Cross-Site Scripting (XSS)** - + - **Reflective XSS** can be demonstrated by using the mailing list subscription form on the landing page. - **Persistent XSS** can be demonstrated using add testimonial form on the landing page (for authenticated users only). -* **Default Login Location** - The login endpoint is available under /api/auth/login. +- **Default Login Location** - The login endpoint is available under /api/auth/login. -* **Directory Listing** - The Nginx config file under the nginx-conf directory is configured to allow directory listing. +- **Directory Listing** - The Nginx config file under the nginx-conf directory is configured to allow directory listing. -* **DOM Cross-Site Scripting** - Open the landing page with the _dummy_ query param that contains DOM content (including script), add the provided DOM into the page, and execute it. +- **DOM Cross-Site Scripting** - Open the landing page with the _dummy_ query param that contains DOM content (including script), add the provided DOM into the page, and execute it. -* **File Upload** - The application allows uploading an avatar photo of the authenticated user. The server doesn't perform any sort of validation on the uploaded file. +- **File Upload** - The application allows uploading an avatar photo of the authenticated user. The server doesn't perform any sort of validation on the uploaded file. -* **Full Path Disclosure** - All errors returned by the server include the full path of the file where the error has occurred. The errors can be triggered by passing wrong values as parameters or by modifying the bc-calls-counter cookie to a non-numeric value. +- **Full Path Disclosure** - All errors returned by the server include the full path of the file where the error has occurred. The errors can be triggered by passing wrong values as parameters or by modifying the bc-calls-counter cookie to a non-numeric value. -* **Headers Security Check** - The application is configured with misconfigured security headers. The list of headers is available in the headers.configurator.interceptor.ts file. A user can pass the _no-sec-headers_ query param to any API to prevent the server from sending the headers. +- **Headers Security Check** - The application is configured with misconfigured security headers. The list of headers is available in the headers.configurator.interceptor.ts file. A user can pass the _no-sec-headers_ query param to any API to prevent the server from sending the headers. -* **HTML Injection** - Both forms testimonial and mailing list subscription forms allow HTML injection. +- **HTML Injection** - Both forms testimonial and mailing list subscription forms allow HTML injection. -* **CSS Injection** - The login page is vulnerable to CSS Injections through a url parameter: https://brokencrystals.com/userlogin?logobgcolor=transparent. +- **CSS Injection** - The login page is vulnerable to CSS Injections through a url parameter: https://brokencrystals.com/userlogin?logobgcolor=transparent. -* **HTTP Method fuzzer** - The server supports uploading, deletion, and getting the content of a file via /put.raw addition to the URL. The actual implementation using a regular upload endpoint of the server and the /put.raw endpoint is mapped in Nginx. +- **HTTP Method fuzzer** - The server supports uploading, deletion, and getting the content of a file via /put.raw addition to the URL. The actual implementation using a regular upload endpoint of the server and the /put.raw endpoint is mapped in Nginx. -* **LDAP Injection** - The login request returns an LDAP query for the user's profile, which can be used as a query parameter in /api/users/ldap _query_ query parameter. The returned query can be modified to search for other users. If the structure of the LDAP query is changed, a detailed LDAP error will be returned (with LDAP server information and hierarchy). +- **LDAP Injection** - The login request returns an LDAP query for the user's profile, which can be used as a query parameter in /api/users/ldap _query_ query parameter. The returned query can be modified to search for other users. If the structure of the LDAP query is changed, a detailed LDAP error will be returned (with LDAP server information and hierarchy). -* **Local File Inclusion (LFI)** - The /api/files endpoint returns any file on the server from the path that is provided in the _path_ param. The UI uses this endpoint to load crystal images on the landing page. +- **Local File Inclusion (LFI)** - The /api/files endpoint returns any file on the server from the path that is provided in the _path_ param. The UI uses this endpoint to load crystal images on the landing page. -* **Mass Assignment** - You can add to user admin privilegies upon creating user or updating userdata. When you creating a new user /api/users/basic you can use additional hidden field in body request { ... "isAdmin" : true }. If you are trying to edit userdata with PUT request /api/users/one/{email}/info you can add this additional field mentioned above. For checking admin permissions there is one more endpoint: /api/users/one/{email}/adminpermission. +- **Mass Assignment** - You can add to user admin privilegies upon creating user or updating userdata. When you creating a new user /api/users/basic you can use additional hidden field in body request { ... "isAdmin" : true }. If you are trying to edit userdata with PUT request /api/users/one/{email}/info you can add this additional field mentioned above. For checking admin permissions there is one more endpoint: /api/users/one/{email}/adminpermission. -* **Open Database** - The index.html file includes a link to manifest URL, which returns the server's configuration, including a DB connection string. +- **Open Database** - The index.html file includes a link to manifest URL, which returns the server's configuration, including a DB connection string. -* **OS Command Injection** - The /api/spawn endpoint spawns a new process using the command in the _command_ query parameter. The endpoint is not referenced from UI. +- **OS Command Injection** - The /api/spawn endpoint spawns a new process using the command in the _command_ query parameter. The endpoint is not referenced from UI. -* **Remote File Inclusion (RFI)** - The /api/files endpoint returns any file on the server from the path that is provided in the _path_ param. The UI uses this endpoint to load crystal images on the landing page. +- **Remote File Inclusion (RFI)** - The /api/files endpoint returns any file on the server from the path that is provided in the _path_ param. The UI uses this endpoint to load crystal images on the landing page. -* **Secret Tokens** - The index.html file includes a link to manifest URL, which returns the server's configuration, including a Google API key. +- **Secret Tokens** - The index.html file includes a link to manifest URL, which returns the server's configuration, including a Google API key. -* **Server-Side Template Injection (SSTI)** - The endpoint /api/render receives a plain text body and renders it using the doT (http://github.com/olado/dot) templating engine. +- **Server-Side Template Injection (SSTI)** - The endpoint /api/render receives a plain text body and renders it using the doT (http://github.com/olado/dot) templating engine. -* **Server-Side Request Forgery (SSRF)** - The endpoint /api/file receives the _path_ and _type_ query parameters and returns the content of the file in _path_ with Content-Type value from the _type_ parameter. The endpoint supports relative and absolute file names, HTTP/S requests, as well as metadata URLs of Azure, Google Cloud, AWS, and DigitalOcean. -There are specific endpoints for each cloud provider as well - `/api/file/google`, `/api/file/aws`, `/api/file/azure`, `/api/file/digital_ocean`. +- **Server-Side Request Forgery (SSRF)** - The endpoint /api/file receives the _path_ and _type_ query parameters and returns the content of the file in _path_ with Content-Type value from the _type_ parameter. The endpoint supports relative and absolute file names, HTTP/S requests, as well as metadata URLs of Azure, Google Cloud, AWS, and DigitalOcean. + There are specific endpoints for each cloud provider as well - `/api/file/google`, `/api/file/aws`, `/api/file/azure`, `/api/file/digital_ocean`. -* **SQL injection (SQLi)** - The `/api/testimonials/count` endpoint receives and executes SQL query in the query parameter. Similarly, the `/api/products/views` endpoint utilizes the `x-product-name` header to update the number of views for a product. However, both of these parameters can be exploited to inject SQL code, making these endpoints vulnerable to SQL injection attacks. +- **SQL injection (SQLi)** - The `/api/testimonials/count` endpoint receives and executes SQL query in the query parameter. Similarly, the `/api/products/views` endpoint utilizes the `x-product-name` header to update the number of views for a product. However, both of these parameters can be exploited to inject SQL code, making these endpoints vulnerable to SQL injection attacks. -* **Unvalidated Redirect** - The endpoint /api/goto redirects the client to the URL provided in the _url_ query parameter. The UI references the endpoint in the header (while clicking on the site's logo) and as an href source for the Terms and Services link in the footer. +- **Unvalidated Redirect** - The endpoint /api/goto redirects the client to the URL provided in the _url_ query parameter. The UI references the endpoint in the header (while clicking on the site's logo) and as an href source for the Terms and Services link in the footer. -* **Version Control System** - The client_s build process copies SVN, GIT, and Mercurial source control directories to the client application root and they are accessible under Nginx root. +- **Version Control System** - The client_s build process copies SVN, GIT, and Mercurial source control directories to the client application root and they are accessible under Nginx root. -* **XML External Entity (XXE)** - The endpoint, POST /api/metadata, receives URL-encoded XML data in the _xml_ query parameter, processes it with enabled external entities (using libxmnl library) and returns the serialized DOM. Additionally, for a request that tries to load file:///etc/passwd as an entity, the endpoint returns a mocked up content of the file. -Additionally, the endpoint PUT /api/users/one/{email}/photo accepts SVG images, which are proccessed with libxml library and stored on the server, as well as sent back to the client. +- **XML External Entity (XXE)** - The endpoint, POST /api/metadata, receives URL-encoded XML data in the _xml_ query parameter, processes it with enabled external entities (using libxmnl library) and returns the serialized DOM. Additionally, for a request that tries to load file:///etc/passwd as an entity, the endpoint returns a mocked up content of the file. + Additionally, the endpoint PUT /api/users/one/{email}/photo accepts SVG images, which are proccessed with libxml library and stored on the server, as well as sent back to the client. -* **JavaScript Vulnerabilities Scanning** - Index.html includes an older version of the jQuery library with known vulnerabilities. +- **JavaScript Vulnerabilities Scanning** - Index.html includes an older version of the jQuery library with known vulnerabilities. -* **AO1 Vertical access controls** - The page /dashboard can be reached despite the rights of user. +- **AO1 Vertical access controls** - The page /dashboard can be reached despite the rights of user. -* **Broken Function Level Authorization** - The endpoint DELETE `/users/one/:id/photo?isAdmin=` can be used to delete any user's profile photo by enumerating the user IDs and setting the `isAdmin` query parameter to true, as there is no validation of it's value on the server side. +- **Broken Function Level Authorization** - The endpoint DELETE `/users/one/:id/photo?isAdmin=` can be used to delete any user's profile photo by enumerating the user IDs and setting the `isAdmin` query parameter to true, as there is no validation of it's value on the server side. -* **IFrame Injection** - The `/testimonials` page a URL parameter `videosrc` which directly controls the src attribute of the IFrame at the bottom of this page. Similarly, the home page takes a URL param `maptitle` which directly controls the `title` attribute of the IFrame at the CONTACT section of this page. +- **IFrame Injection** - The `/testimonials` page a URL parameter `videosrc` which directly controls the src attribute of the IFrame at the bottom of this page. Similarly, the home page takes a URL param `maptitle` which directly controls the `title` attribute of the IFrame at the CONTACT section of this page. -* **Excessive Data Exposure** - The `/api/users/one/:email` is supposed to expose only basic user information required to be displayed on the UI, but it also returns the user's phone number which is unnecessary information. +- **Excessive Data Exposure** - The `/api/users/one/:email` is supposed to expose only basic user information required to be displayed on the UI, but it also returns the user's phone number which is unnecessary information. -* **Business Constraint Bypass** - The `/api/products/latest` endpoint supports a `limit` parameter, which by default is set to 3. The `/api/products` endpoint is a password protected endpoint which returns all of the products, yet if you change the `limit` param of `/api/products/latest` to be high enough you could get the same results without the need to be authenticated. +- **Business Constraint Bypass** - The `/api/products/latest` endpoint supports a `limit` parameter, which by default is set to 3. The `/api/products` endpoint is a password protected endpoint which returns all of the products, yet if you change the `limit` param of `/api/products/latest` to be high enough you could get the same results without the need to be authenticated. + +- **ID Enumeration** - There are a few ID Enumeration vulnerabilities: -* **ID Enumeration** - There are a few ID Enumeration vulnerabilities: 1. The endpoint DELETE `/users/one/:id/photo?isAdmin=` which is used to delete a user's profile picture is vulnerable to ID Enumeration together with [Broken Function Level Authorization](#broken-function-level-authorization). 2. The `/users/id/:id` endpoint returns user info by ID, it doesn't require neither authentication nor authorization. -* **XPATH Injection** - The `/api/partners/*` endpoint contains the following XPATH injection vulnerabilities: +- **XPATH Injection** - The `/api/partners/*` endpoint contains the following XPATH injection vulnerabilities: + 1. The endpoint GET `/api/partners/partnerLogin` is supposed to login with the user's credentials in order to obtain account info. It's vulnerable to an XPATH injection using boolean based payloads. When exploited it'll retrieve data about other users as well. You can use `' or '1'='1` in the password field to exploit the EP. 2. The endpoint GET `/api/partners/searchPartners` is supposed to search partners' names by a given keyword. It's vulnerable to an XPATH injection using string detection payloads. When exploited, it can grant access to sensitive information like passwords and even lead to full data leak. You can use `')] | //password%00//` or `')] | //* | a[('` to exploit the EP. 3. The endpoint GET `/api/partners/query` is a raw XPATH injection endpoint. You can put whatever you like there. It is not referenced in the frontend, but it is an exposed API endpoint. 4. Note: All endpoints are vulnerable to error based payloads. -* **Prototype Pollution** - The `/marketplace` endpoint is vulnerable to prototype pollution using the following methods: +- **Prototype Pollution** - The `/marketplace` endpoint is vulnerable to prototype pollution using the following methods: + 1. The EP GET `/marketplace?__proto__[Test]=Test` represents the client side vulnerabillity, by parsing the URI (for portfolio filtering) and converting - it's parmeters into an object. This means that a requests like `/marketplace?__proto__[TestKey]=TestValue` will lead to a creation of `Object.TestKey`. - One can test if an attack was successful by viewing the new property created in the console. - This EP also supports prototyp pollution based DOM XSS using a payload such as `__proto__[prototypePollutionDomXss]=data:,alert(1);`. - The "legitimate" code tries to use the `prototypePollutionDomXss` parameter as a source for a script tag, so if the exploit is not used via this key it won't work. + it's parmeters into an object. This means that a requests like `/marketplace?__proto__[TestKey]=TestValue` will lead to a creation of `Object.TestKey`. + One can test if an attack was successful by viewing the new property created in the console. + This EP also supports prototyp pollution based DOM XSS using a payload such as `__proto__[prototypePollutionDomXss]=data:,alert(1);`. + The "legitimate" code tries to use the `prototypePollutionDomXss` parameter as a source for a script tag, so if the exploit is not used via this key it won't work. 2. The EP GET `/api/email/sendSupportEmail` represents the server side vulnerabillity, by having a rookie URI parsing mistake (similiar to the client side). - This means that a request such as `/api/email/sendSupportEmail?name=Bob%20Dylan&__proto__[status]=222&to=username%40email.com&subject=Help%20Request&content=Help%20me..` - will lead to a creation of `uriParams.status`, which is a parameter used in the final JSON response. + This means that a request such as `/api/email/sendSupportEmail?name=Bob%20Dylan&__proto__[status]=222&to=username%40email.com&subject=Help%20Request&content=Help%20me..` + will lead to a creation of `uriParams.status`, which is a parameter used in the final JSON response. -* **Date Manipulation** - The `/api/products?date_from={df}&date_to={dt}` endpoint fetches all products that were created between the selected dates. There is no limit on the range of dates and when a user tries to query a range larger than 2 years querying takes a significant amount of time. This EP is used by the frontend in the `/marketplace` page. +- **Date Manipulation** - The `/api/products?date_from={df}&date_to={dt}` endpoint fetches all products that were created between the selected dates. There is no limit on the range of dates and when a user tries to query a range larger than 2 years querying takes a significant amount of time. This EP is used by the frontend in the `/marketplace` page. -* **Email Injection** - The `/api/email/sendSupportEmail` is vulnerable to email injection by supplying tempred recipients. +- **Email Injection** - The `/api/email/sendSupportEmail` is vulnerable to email injection by supplying tempred recipients. To exploit the EP you can dispatch a request as such `/api/email/sendSupportEmail?name=Bob&to=username%40email.com%0aCc:%20bob@domain.com&subject=Help%20Request&content=I%20would%20like%20to%20request%20help%20regarding`. This will lead to the sending of a mail to both `username@email.com` and `bob@domain.com` (as the Cc). Note: This EP is also vulnerable to `Server side prototype pollution`, as mentioned in this README. -* **Insecure Output Handling** - The `/chat` route is vulnerable to non-sanitized output originating from the LLM response. +- **Insecure Output Handling** - The `/chat` route is vulnerable to non-sanitized output originating from the LLM response. Issue a `POST /api/chat` request with body payload like `[{"content": "Provide a minimal html markup for img tag with invalid source and onerror attribute with alert", "role": "user"}]`. The response will include raw HTML code. If this output is not properly sanitized before rendering, it can trigger an alert box in the user interface. diff --git a/docker-compose.yml b/docker-compose.yml index 836d3d2e..83d9cad2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -101,7 +101,7 @@ services: retries: 3 depends_on: - keycloak-db - + mailcatcher: image: sj26/mailcatcher container_name: mailcatcher diff --git a/eslint.config.mjs b/eslint.config.mjs index 4100420b..024b53f2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,13 +5,16 @@ export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.strict, { - ignores: ["**/charts", "**/dist", "**/client"], + ignores: ['**/charts', '**/dist', '**/client'], }, { rules: { - "@typescript-eslint/no-extraneous-class": ["error", { - allowWithDecorator: true - }] - } - } + '@typescript-eslint/no-extraneous-class': [ + 'error', + { + allowWithDecorator: true, + }, + ], + }, + }, ); diff --git a/keycloak/imports/realm-export.json b/keycloak/imports/realm-export.json index 8e936c1d..22c6be18 100644 --- a/keycloak/imports/realm-export.json +++ b/keycloak/imports/realm-export.json @@ -52,15 +52,9 @@ "description": "${role_default-roles}", "composite": true, "composites": { - "realm": [ - "offline_access", - "uma_authorization" - ], + "realm": ["offline_access", "uma_authorization"], "client": { - "account": [ - "view-profile", - "manage-account" - ] + "account": ["view-profile", "manage-account"] } }, "clientRole": false, @@ -254,9 +248,7 @@ "composite": true, "composites": { "client": { - "realm-management": [ - "query-clients" - ] + "realm-management": ["query-clients"] } }, "clientRole": true, @@ -279,10 +271,7 @@ "composite": true, "composites": { "client": { - "realm-management": [ - "query-users", - "query-groups" - ] + "realm-management": ["query-users", "query-groups"] } }, "clientRole": true, @@ -340,9 +329,7 @@ "composite": true, "composites": { "client": { - "account": [ - "manage-account-links" - ] + "account": ["manage-account-links"] } }, "clientRole": true, @@ -356,9 +343,7 @@ "composite": true, "composites": { "client": { - "account": [ - "view-consent" - ] + "account": ["view-consent"] } }, "clientRole": true, @@ -404,23 +389,16 @@ "clientRole": false, "containerId": "brokencrystals" }, - "requiredCredentials": [ - "password" - ], + "requiredCredentials": ["password"], "otpPolicyType": "totp", "otpPolicyAlgorithm": "HmacSHA1", "otpPolicyInitialCounter": 0, "otpPolicyDigits": 6, "otpPolicyLookAheadWindow": 1, "otpPolicyPeriod": 30, - "otpSupportedApplications": [ - "FreeOTP", - "Google Authenticator" - ], + "otpSupportedApplications": ["FreeOTP", "Google Authenticator"], "webAuthnPolicyRpEntityName": "keycloak", - "webAuthnPolicySignatureAlgorithms": [ - "ES256" - ], + "webAuthnPolicySignatureAlgorithms": ["ES256"], "webAuthnPolicyRpId": "", "webAuthnPolicyAttestationConveyancePreference": "not specified", "webAuthnPolicyAuthenticatorAttachment": "not specified", @@ -430,9 +408,7 @@ "webAuthnPolicyAvoidSameAuthenticatorRegister": false, "webAuthnPolicyAcceptableAaguids": [], "webAuthnPolicyPasswordlessRpEntityName": "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms": [ - "ES256" - ], + "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], "webAuthnPolicyPasswordlessRpId": "", "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", @@ -464,13 +440,9 @@ "serviceAccountClientId": "admin-cli", "disableableCredentialTypes": [], "requiredActions": [], - "realmRoles": [ - "default-roles-brokencrystals" - ], + "realmRoles": ["default-roles-brokencrystals"], "clientRoles": { - "realm-management": [ - "manage-users" - ] + "realm-management": ["manage-users"] }, "notBefore": 0, "groups": [] @@ -485,9 +457,7 @@ "serviceAccountClientId": "brokencrystals-client", "disableableCredentialTypes": [], "requiredActions": [], - "realmRoles": [ - "default-roles-brokencrystals" - ], + "realmRoles": ["default-roles-brokencrystals"], "notBefore": 0, "groups": [] } @@ -495,26 +465,20 @@ "scopeMappings": [ { "clientScope": "offline_access", - "roles": [ - "offline_access" - ] + "roles": ["offline_access"] } ], "clientScopeMappings": { "realm-management": [ { "client": "admin-cli", - "roles": [ - "manage-users" - ] + "roles": ["manage-users"] } ], "account": [ { "client": "account-console", - "roles": [ - "manage-account" - ] + "roles": ["manage-account"] } ] }, @@ -529,9 +493,7 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "/realms/brokencrystals/account/*" - ], + "redirectUris": ["/realms/brokencrystals/account/*"], "webOrigins": [], "notBefore": 0, "bearerOnly": false, @@ -547,12 +509,7 @@ "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": false, "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "profile", - "roles", - "email" - ], + "defaultClientScopes": ["web-origins", "profile", "roles", "email"], "optionalClientScopes": [ "address", "phone", @@ -570,9 +527,7 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "/realms/brokencrystals/account/*" - ], + "redirectUris": ["/realms/brokencrystals/account/*"], "webOrigins": [], "notBefore": 0, "bearerOnly": false, @@ -600,12 +555,7 @@ "config": {} } ], - "defaultClientScopes": [ - "web-origins", - "profile", - "roles", - "email" - ], + "defaultClientScopes": ["web-origins", "profile", "roles", "email"], "optionalClientScopes": [ "address", "phone", @@ -706,12 +656,7 @@ } } ], - "defaultClientScopes": [ - "web-origins", - "profile", - "roles", - "email" - ], + "defaultClientScopes": ["web-origins", "profile", "roles", "email"], "optionalClientScopes": [ "address", "phone", @@ -727,9 +672,7 @@ "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", "secret": "4bfb5df6-4647-46dd-bad1-c8b8ffd7caf4", - "redirectUris": [ - "http://localhost:3001/" - ], + "redirectUris": ["http://localhost:3001/"], "webOrigins": [], "notBefore": 0, "bearerOnly": false, @@ -810,12 +753,7 @@ } } ], - "defaultClientScopes": [ - "web-origins", - "profile", - "roles", - "email" - ], + "defaultClientScopes": ["web-origins", "profile", "roles", "email"], "optionalClientScopes": [ "address", "phone", @@ -847,12 +785,7 @@ "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": false, "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "profile", - "roles", - "email" - ], + "defaultClientScopes": ["web-origins", "profile", "roles", "email"], "optionalClientScopes": [ "address", "phone", @@ -884,12 +817,7 @@ "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": false, "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [ - "web-origins", - "profile", - "roles", - "email" - ], + "defaultClientScopes": ["web-origins", "profile", "roles", "email"], "optionalClientScopes": [ "address", "phone", @@ -907,12 +835,8 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "/admin/brokencrystals/console/*" - ], - "webOrigins": [ - "+" - ], + "redirectUris": ["/admin/brokencrystals/console/*"], + "webOrigins": ["+"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -946,12 +870,7 @@ } } ], - "defaultClientScopes": [ - "web-origins", - "profile", - "roles", - "email" - ], + "defaultClientScopes": ["web-origins", "profile", "roles", "email"], "optionalClientScopes": [ "address", "phone", @@ -1470,9 +1389,7 @@ }, "smtpServer": {}, "eventsEnabled": false, - "eventsListeners": [ - "jboss-logging" - ], + "eventsListeners": ["jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1487,9 +1404,7 @@ "subType": "anonymous", "subComponents": {}, "config": { - "max-clients": [ - "200" - ] + "max-clients": ["200"] } }, { @@ -1499,12 +1414,8 @@ "subType": "anonymous", "subComponents": {}, "config": { - "host-sending-registration-request-must-match": [ - "true" - ], - "client-uris-must-match": [ - "true" - ] + "host-sending-registration-request-must-match": ["true"], + "client-uris-must-match": ["true"] } }, { @@ -1522,9 +1433,7 @@ "subType": "anonymous", "subComponents": {}, "config": { - "allow-default-scopes": [ - "true" - ] + "allow-default-scopes": ["true"] } }, { @@ -1534,9 +1443,7 @@ "subType": "authenticated", "subComponents": {}, "config": { - "allow-default-scopes": [ - "true" - ] + "allow-default-scopes": ["true"] } }, { @@ -1593,9 +1500,7 @@ "providerId": "rsa-generated", "subComponents": {}, "config": { - "priority": [ - "100" - ] + "priority": ["100"] } }, { @@ -1604,12 +1509,8 @@ "providerId": "hmac-generated", "subComponents": {}, "config": { - "priority": [ - "100" - ], - "algorithm": [ - "HS256" - ] + "priority": ["100"], + "algorithm": ["HS256"] } }, { @@ -1618,9 +1519,7 @@ "providerId": "aes-generated", "subComponents": {}, "config": { - "priority": [ - "100" - ] + "priority": ["100"] } } ] @@ -2309,4 +2208,4 @@ }, "keycloakVersion": "13.0.1", "userManagedAccessAllowed": false -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 38585464..f0473431 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,7 @@ "@types/node": "^18.19.46", "eslint": "^9.9.1", "jest": "^29.7.0", - "prettier": "^2.8.4", + "prettier": "^3.3.3", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", @@ -11764,15 +11764,16 @@ } }, "node_modules/prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, + "license": "MIT", "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" diff --git a/package.json b/package.json index e783773b..f157a33a 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "scripts": { "prebuild": "rimraf dist", "build": "nest build", - "format": "prettier --check \"{src,public,test}/**/*.ts\"", - "format:write": "prettier --write \"{src,public,test}/**/*.ts\"", + "format": "prettier --check .", + "format:write": "prettier --write .", "start": "nest start", "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", @@ -80,7 +80,7 @@ "@types/node": "^18.19.46", "eslint": "^9.9.1", "jest": "^29.7.0", - "prettier": "^2.8.4", + "prettier": "^3.3.3", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", diff --git a/src/auth/jwt/jwt.token.with.hmac.keys.processor.ts b/src/auth/jwt/jwt.token.with.hmac.keys.processor.ts index 444771e1..245ea7aa 100644 --- a/src/auth/jwt/jwt.token.with.hmac.keys.processor.ts +++ b/src/auth/jwt/jwt.token.with.hmac.keys.processor.ts @@ -3,7 +3,10 @@ import { JwtTokenProcessor as JwtTokenProcessor } from './jwt.token.processor'; import { encode, decode } from 'jwt-simple'; export class JwtTokenWithHMACKeysProcessor extends JwtTokenProcessor { - constructor(private publicKey: string, private privateKey: string) { + constructor( + private publicKey: string, + private privateKey: string, + ) { super(new Logger(JwtTokenWithHMACKeysProcessor.name)); } diff --git a/src/auth/jwt/jwt.token.with.jwk.processor.ts b/src/auth/jwt/jwt.token.with.jwk.processor.ts index a9a4eb2b..3d15a2ab 100644 --- a/src/auth/jwt/jwt.token.with.jwk.processor.ts +++ b/src/auth/jwt/jwt.token.with.jwk.processor.ts @@ -3,7 +3,10 @@ import * as jose from 'jose'; import { JwtTokenProcessor as JwtTokenProcessor } from './jwt.token.processor'; export class JwtTokenWithJWKProcessor extends JwtTokenProcessor { - constructor(private key: string, private jwk: jose.JWK) { + constructor( + private key: string, + private jwk: jose.JWK, + ) { super(new Logger(JwtTokenWithJWKProcessor.name)); } diff --git a/src/auth/jwt/jwt.token.with.rsa.keys.processor.ts b/src/auth/jwt/jwt.token.with.rsa.keys.processor.ts index 28f47d07..077d258f 100644 --- a/src/auth/jwt/jwt.token.with.rsa.keys.processor.ts +++ b/src/auth/jwt/jwt.token.with.rsa.keys.processor.ts @@ -3,7 +3,10 @@ import { decode, encode } from 'jwt-simple'; import { JwtTokenProcessor as JwtTokenProcessor } from './jwt.token.processor'; export class JwtTokenWithRSAKeysProcessor extends JwtTokenProcessor { - constructor(private publicKey: string, private privateKey: string) { + constructor( + private publicKey: string, + private privateKey: string, + ) { super(new Logger(JwtTokenWithRSAKeysProcessor.name)); } diff --git a/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts b/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts index c9fc0bf5..b8f86fe7 100644 --- a/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts +++ b/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts @@ -3,7 +3,10 @@ import { decode, encode } from 'jwt-simple'; import { JwtTokenProcessor as JwtTokenProcessor } from './jwt.token.processor'; export class JwtTokenWithRSASignatureKeysProcessor extends JwtTokenProcessor { - constructor(private publicKey: string, private privateKey: string) { + constructor( + private publicKey: string, + private privateKey: string, + ) { super(new Logger(JwtTokenWithRSASignatureKeysProcessor.name)); } diff --git a/src/auth/jwt/jwt.token.with.sql.kid.processor.ts b/src/auth/jwt/jwt.token.with.sql.kid.processor.ts index 71b843d1..e6d56b0f 100644 --- a/src/auth/jwt/jwt.token.with.sql.kid.processor.ts +++ b/src/auth/jwt/jwt.token.with.sql.kid.processor.ts @@ -9,7 +9,10 @@ export class JwtTokenWithSqlKIDProcessor extends JwtTokenProcessor { private static readonly KID_FETCH_QUERY = (key: string, param: string) => `select key from (select '${key}' as key, ${JwtTokenWithSqlKIDProcessor.KID} as id) as keys where keys.id = '${param}'`; - constructor(private readonly em: EntityManager, private key: string) { + constructor( + private readonly em: EntityManager, + private key: string, + ) { super(new Logger(JwtTokenWithSqlKIDProcessor.name)); } diff --git a/src/utils/url.ts b/src/utils/url.ts index 7b0eea4f..b5b583bd 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -44,10 +44,10 @@ const splitUriIntoParamsPPVulnerable = ( val && !isNaN(val) && +val + '' === val ? +val // number : val === 'undefined' - ? undefined // undefined - : coerce_types[val] !== undefined - ? coerce_types[val] // true, false, null - : val; // string + ? undefined // undefined + : coerce_types[val] !== undefined + ? coerce_types[val] // true, false, null + : val; // string } if (keys_last) {