Addressed greptlie comments and suggestions

This commit is contained in:
Carlos Monastyrski
2025-08-27 04:04:39 -03:00
parent 83479a091e
commit 7b52d60036
22 changed files with 80 additions and 54 deletions
@@ -281,6 +281,7 @@ export enum EventType {
UPDATE_SSH_CERTIFICATE_TEMPLATE = "update-ssh-certificate-template",
DELETE_SSH_CERTIFICATE_TEMPLATE = "delete-ssh-certificate-template",
GET_SSH_CERTIFICATE_TEMPLATE = "get-ssh-certificate-template",
GET_AZURE_AD_TEMPLATES = "get-azure-ad-templates",
GET_SSH_HOST = "get-ssh-host",
CREATE_SSH_HOST = "create-ssh-host",
UPDATE_SSH_HOST = "update-ssh-host",
@@ -2497,6 +2498,14 @@ interface CreateCertificateTemplateEstConfig {
};
}
interface GetAzureAdCsTemplatesEvent {
type: EventType.GET_AZURE_AD_TEMPLATES;
metadata: {
caId: string;
amount: number;
};
}
interface UpdateCertificateTemplateEstConfig {
type: EventType.UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG;
metadata: {
@@ -3636,6 +3645,7 @@ export type Event =
| CreateCertificateTemplateEstConfig
| UpdateCertificateTemplateEstConfig
| GetCertificateTemplateEstConfig
| GetAzureAdCsTemplatesEvent
| AttemptCreateSlackIntegration
| AttemptReinstallSlackIntegration
| UpdateSlackIntegration
-1
View File
@@ -948,7 +948,6 @@ export const registerRoutes = async (
certificateAuthorityCrlDAL,
certificateAuthoritySecretDAL,
projectDAL,
appConnectionDAL,
kmsService,
permissionService,
pkiCollectionDAL,
@@ -1,5 +1,6 @@
import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -59,6 +60,18 @@ export const registerAzureAdCsCertificateAuthorityRouter = async (server: Fastif
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.query.projectId,
event: {
type: EventType.GET_AZURE_AD_TEMPLATES,
metadata: {
caId: req.params.caId,
amount: templates.length
}
}
});
return { templates };
}
});
@@ -1,5 +1,7 @@
/* eslint-disable no-case-declarations, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-var-requires, no-await-in-loop, no-continue */
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator/validate-url";
import { decryptAppConnectionCredentials } from "@app/services/app-connection/app-connection-fns";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
@@ -145,7 +147,7 @@ const testAdcsConnection = async (
password,
domain: credentials.domain,
workstation: "",
rejectUnauthorized: false
rejectUnauthorized: !getConfig().isDevelopmentMode // Only allow unauthorized SSL in development
});
// Check if we got a successful response
@@ -232,7 +234,7 @@ const createNtlmClient = (username: string, password: string, baseUrl: string) =
password,
domain: parsedCredentials.domain,
workstation: "",
rejectUnauthorized: false,
rejectUnauthorized: !getConfig().isDevelopmentMode, // Only allow unauthorized SSL in development,
...additionalOptions
});
},
@@ -244,7 +246,7 @@ const createNtlmClient = (username: string, password: string, baseUrl: string) =
password,
domain: parsedCredentials.domain,
workstation: "",
rejectUnauthorized: false,
rejectUnauthorized: !getConfig().isDevelopmentMode, // Only allow unauthorized SSL in development,
body,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
@@ -302,6 +304,9 @@ export const validateAzureADCSConnectionCredentials = async (appConnection: TAzu
const parsedCredentials = parseCredentials(credentials.username);
const normalizedUrl = normalizeAdcsUrl(credentials.adcsUrl);
// Validate URL to prevent DNS manipulation attacks and SSRF
await blockLocalAndPrivateIpAddresses(normalizedUrl);
// Test the connection using NTLM
await testAdcsConnection(parsedCredentials, credentials.password, normalizedUrl);
@@ -110,7 +110,7 @@ const buildSubjectDN = (commonName: string, properties?: TPkiSubscriberPropertie
const emailAddress = sanitizeComponent(properties?.emailAddress);
if (emailAddress) {
// Enhanced email validation for DN usage
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const emailRegex = new RE2(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/);
if (emailRegex.test(emailAddress) && emailAddress.length > 5 && emailAddress.length < 64) {
subject += `,E=${emailAddress}`;
}
@@ -247,7 +247,7 @@ const submitCertificateRequest = async (
}
// Check for immediate certificate issuance
const certMatch = responseText.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/);
const certMatch = responseText.match(new RE2("-----BEGIN CERTIFICATE-----[\\s\\S]*?-----END CERTIFICATE-----"));
if (certMatch) {
// Clean up the certificate format
certificate = certMatch[0].replace(new RE2("\\\\r\\\\n", "g"), "\n").replace(new RE2("\\\\r", "g"), "\n").trim();
@@ -325,15 +325,15 @@ const submitCertificateRequest = async (
// General error extraction
else {
const errorPatterns = [
/The disposition message is "([^"]*)/i,
/Denied by Policy Module[^"]*"([^"]*)/i,
/<p[^>]*class[^>]*error[^>]*>(.*?)<\/p>/i,
/<div[^>]*class[^>]*error[^>]*>(.*?)<\/div>/i,
/<span[^>]*class[^>]*error[^>]*>(.*?)<\/span>/i,
/error[^<]*:([^<]*)/i,
/denied[^<]*:([^<]*)/i,
/The\s+request\s+contains\s+no\s+certificate\s+template\s+information/i,
/The\s+template\s+is\s+missing/i
new RE2('The disposition message is "([^"]*)"', "i"),
new RE2('Denied by Policy Module[^"]*"([^"]*)"', "i"),
new RE2("<p[^>]*class[^>]*error[^>]*>(.*?)<\\/p>", "i"),
new RE2("<div[^>]*class[^>]*error[^>]*>(.*?)<\\/div>", "i"),
new RE2("<span[^>]*class[^>]*error[^>]*>(.*?)<\\/span>", "i"),
new RE2("error[^<]*:([^<]*)", "i"),
new RE2("denied[^<]*:([^<]*)", "i"),
new RE2("The\\s+request\\s+contains\\s+no\\s+certificate\\s+template\\s+information", "i"),
new RE2("The\\s+template\\s+is\\s+missing", "i")
];
// Try each pattern to find the error message
@@ -11,11 +11,16 @@ export const AzureAdCsCertificateAuthorityConfigurationSchema = z.object({
azureAdcsConnectionId: z.string().uuid().trim().describe("Azure ADCS Connection ID")
});
export const AzureAdCsCertificateAuthorityCredentialsSchema = z.object({
clientId: z.string(),
clientSecret: z.string().optional(),
certificateThumbprint: z.string().optional()
});
export const AzureAdCsCertificateAuthorityCredentialsSchema = z
.object({
clientId: z.string(),
clientSecret: z.string().optional(),
certificateThumbprint: z.string().optional()
})
.refine((data) => data.clientSecret || data.certificateThumbprint, {
message: "At least one authentication method (clientSecret or certificateThumbprint) must be provided",
path: ["clientSecret"]
});
export const AzureAdCsCertificateAuthoritySchema = BaseCertificateAuthoritySchema.extend({
type: z.literal(CaType.AZURE_AD_CS),
@@ -8,8 +8,6 @@ import {
export type TAzureAdCsCertificateAuthority = z.infer<typeof AzureAdCsCertificateAuthoritySchema>;
export type TAzureAdCsCertificateAuthorityInput = z.infer<typeof CreateAzureAdCsCertificateAuthoritySchema>;
export type TCreateAzureAdCsCertificateAuthorityDTO = z.infer<typeof CreateAzureAdCsCertificateAuthoritySchema>;
export type TUpdateAzureAdCsCertificateAuthorityDTO = z.infer<typeof UpdateAzureAdCsCertificateAuthoritySchema>;
@@ -12,11 +12,7 @@ export const CERTIFICATE_AUTHORITIES_CAPABILITIES_MAP: Record<CaType, CaCapabili
CaCapability.REVOKE_CERTIFICATES,
CaCapability.RENEW_CERTIFICATES
],
[CaType.ACME]: [
CaCapability.ISSUE_CERTIFICATES,
CaCapability.REVOKE_CERTIFICATES,
CaCapability.RENEW_CERTIFICATES
],
[CaType.ACME]: [CaCapability.ISSUE_CERTIFICATES, CaCapability.REVOKE_CERTIFICATES, CaCapability.RENEW_CERTIFICATES],
[CaType.AZURE_AD_CS]: [
CaCapability.ISSUE_CERTIFICATES,
CaCapability.RENEW_CERTIFICATES
@@ -1,7 +1,7 @@
import { TAcmeCertificateAuthority, TAcmeCertificateAuthorityInput } from "./acme/acme-certificate-authority-types";
import {
TAzureAdCsCertificateAuthority,
TAzureAdCsCertificateAuthorityInput
TCreateAzureAdCsCertificateAuthorityDTO
} from "./azure-ad-cs/azure-ad-cs-certificate-authority-types";
import { CaType } from "./certificate-authority-enums";
import {
@@ -17,7 +17,7 @@ export type TCertificateAuthority =
export type TCertificateAuthorityInput =
| TInternalCertificateAuthorityInput
| TAcmeCertificateAuthorityInput
| TAzureAdCsCertificateAuthorityInput;
| TCreateAzureAdCsCertificateAuthorityDTO;
export type TCreateCertificateAuthorityDTO = Omit<TCertificateAuthority, "id">;
@@ -98,7 +98,7 @@ const buildSubjectDN = (commonName: string, properties?: TPkiSubscriberPropertie
const emailAddress = sanitizeComponent(properties?.emailAddress);
if (emailAddress) {
// Enhanced email validation for DN usage
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const emailRegex = new RE2(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/);
if (emailRegex.test(emailAddress) && emailAddress.length > 5 && emailAddress.length < 64) {
subject += `,E=${emailAddress}`;
}
@@ -10,7 +10,6 @@ import {
} from "@app/ee/services/permission/project-permission";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
@@ -52,7 +51,6 @@ type TCertificateServiceFactoryDep = {
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "findById">;
pkiCollectionItemDAL: Pick<TPkiCollectionItemDALFactory, "create">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
@@ -70,7 +68,6 @@ export const certificateServiceFactory = ({
pkiCollectionDAL,
pkiCollectionItemDAL,
projectDAL,
appConnectionDAL,
kmsService,
permissionService
}: TCertificateServiceFactoryDep) => {
@@ -80,4 +80,4 @@ export type TPkiSubscriberProperties = {
state?: string;
locality?: string;
emailAddress?: string;
}
};