From 40d3ee8ec74f30c3092193e5bfb2ae99ab6c6774 Mon Sep 17 00:00:00 2001 From: Michael Morten Sonne | Microsoft MVP <49366751+michaelmsonne@users.noreply.github.com> Date: Mon, 22 Jun 2026 10:00:07 +0200 Subject: [PATCH 01/22] Fix formatting in alerts list component Signed-off-by: Michael Morten Sonne | Microsoft MVP <49366751+michaelmsonne@users.noreply.github.com> --- src/pages/security/incidents/list-check-alerts/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/security/incidents/list-check-alerts/index.js b/src/pages/security/incidents/list-check-alerts/index.js index db56faf0454e..2586b6ad7167 100644 --- a/src/pages/security/incidents/list-check-alerts/index.js +++ b/src/pages/security/incidents/list-check-alerts/index.js @@ -14,7 +14,7 @@ const Page = () => { docs.check.tech {" "} - or install the plugin now: + or install the plugin now: { > Microsoft Edge {" "} - | + | Date: Tue, 23 Jun 2026 11:59:18 +0200 Subject: [PATCH 02/22] fix: disallow creatable for CA deploy drawer --- .../CippComponents/CippCADeployDrawer.jsx | 121 +++++++++--------- 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/src/components/CippComponents/CippCADeployDrawer.jsx b/src/components/CippComponents/CippCADeployDrawer.jsx index 6b2a4a633ff4..dac9f8c2c7c8 100644 --- a/src/components/CippComponents/CippCADeployDrawer.jsx +++ b/src/components/CippComponents/CippCADeployDrawer.jsx @@ -1,92 +1,92 @@ -import { useEffect, useState, useCallback } from "react"; -import { Button, Stack } from "@mui/material"; -import { RocketLaunch } from "@mui/icons-material"; -import { useForm, useWatch } from "react-hook-form"; -import { CippOffCanvas } from "./CippOffCanvas"; -import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; -import CippFormComponent from "./CippFormComponent"; -import CippJsonView from "../CippFormPages/CippJSONView"; -import { CippApiResults } from "./CippApiResults"; -import { useSettings } from "../../hooks/use-settings"; -import { CippFormTenantSelector } from "./CippFormTenantSelector"; -import { CippFormCondition } from "./CippFormCondition"; +import { useEffect, useState, useCallback } from 'react' +import { Button, Stack } from '@mui/material' +import { RocketLaunch } from '@mui/icons-material' +import { useForm, useWatch } from 'react-hook-form' +import { CippOffCanvas } from './CippOffCanvas' +import { ApiGetCall, ApiPostCall } from '../../api/ApiCall' +import CippFormComponent from './CippFormComponent' +import CippJsonView from '../CippFormPages/CippJSONView' +import { CippApiResults } from './CippApiResults' +import { useSettings } from '../../hooks/use-settings' +import { CippFormTenantSelector } from './CippFormTenantSelector' +import { CippFormCondition } from './CippFormCondition' export const CippCADeployDrawer = ({ - buttonText = "Deploy CA Policy", + buttonText = 'Deploy CA Policy', requiredPermissions = [], PermissionButton = Button, templateId = null, // New prop for pre-supplying template ID open = null, // External control for drawer visibility onClose = null, // External close handler }) => { - const [internalDrawerVisible, setInternalDrawerVisible] = useState(false); - const formControl = useForm(); - const tenantFilter = useSettings()?.tenantFilter; - const CATemplates = ApiGetCall({ url: "/api/ListCATemplates", queryKey: "CATemplates" }); - const [JSONData, setJSONData] = useState(); - const watcher = useWatch({ control: formControl.control, name: "TemplateList" }); + const [internalDrawerVisible, setInternalDrawerVisible] = useState(false) + const formControl = useForm() + const tenantFilter = useSettings()?.tenantFilter + const CATemplates = ApiGetCall({ url: '/api/ListCATemplates', queryKey: 'CATemplates' }) + const [JSONData, setJSONData] = useState() + const watcher = useWatch({ control: formControl.control, name: 'TemplateList' }) const selectedReplaceMode = useWatch({ control: formControl.control, - name: "replacename", - }); + name: 'replacename', + }) // Use external open state if provided, otherwise use internal state - const drawerVisible = open !== null ? open : internalDrawerVisible; - const isExternallyControlled = open !== null && onClose !== null; + const drawerVisible = open !== null ? open : internalDrawerVisible + const isExternallyControlled = open !== null && onClose !== null const updateTemplate = useCallback( (templateGuid) => { if (CATemplates.isSuccess && templateGuid) { - const template = CATemplates.data.find((template) => template.GUID === templateGuid); + const template = CATemplates.data.find((template) => template.GUID === templateGuid) if (template) { - setJSONData(template); - formControl.setValue("rawjson", JSON.stringify(template, null)); + setJSONData(template) + formControl.setValue('rawjson', JSON.stringify(template, null)) } } }, [CATemplates.isSuccess, CATemplates.data, formControl.setValue] - ); + ) // Effect to set template when templateId prop is provided useEffect(() => { if (templateId && CATemplates.isSuccess) { // Find the template to get the display name - const template = CATemplates.data.find((template) => template.GUID === templateId); + const template = CATemplates.data.find((template) => template.GUID === templateId) if (template) { // Pre-select the template when drawer opens - formControl.setValue("TemplateList", { value: templateId, label: template.displayName }); - updateTemplate(templateId); + formControl.setValue('TemplateList', { value: templateId, label: template.displayName }) + updateTemplate(templateId) } } - }, [templateId, CATemplates.isSuccess, formControl, updateTemplate]); + }, [templateId, CATemplates.isSuccess, formControl, updateTemplate]) useEffect(() => { - updateTemplate(watcher?.value); - }, [updateTemplate, watcher?.value]); + updateTemplate(watcher?.value) + }, [updateTemplate, watcher?.value]) const deployPolicy = ApiPostCall({ urlFromData: true, - relatedQueryKeys: ["CATemplates", `Conditional Access Policies - ${tenantFilter}`], - }); + relatedQueryKeys: ['CATemplates', `Conditional Access Policies - ${tenantFilter}`], + }) const handleSubmit = () => { - const formData = formControl.getValues(); - console.log("Submitting CA form data:", formData); + const formData = formControl.getValues() + console.log('Submitting CA form data:', formData) deployPolicy.mutate({ - url: "/api/AddCAPolicy", - relatedQueryKeys: ["CATemplates", "Conditional Access Policies"], + url: '/api/AddCAPolicy', + relatedQueryKeys: ['CATemplates', 'Conditional Access Policies'], data: { ...formData }, - }); - }; + }) + } const handleCloseDrawer = () => { if (isExternallyControlled) { - onClose(); + onClose() } else { - setInternalDrawerVisible(false); + setInternalDrawerVisible(false) } - formControl.reset(); - }; + formControl.reset() + } return ( <> @@ -115,10 +115,10 @@ export const CippCADeployDrawer = ({ disabled={deployPolicy.isLoading} > {deployPolicy.isLoading - ? "Deploying..." + ? 'Deploying...' : deployPolicy.isSuccess - ? "Redeploy Policy" - : "Deploy Policy"} + ? 'Redeploy Policy' + : 'Deploy Policy'} + + {execSamAppPermissions.isLoading && } {execSamAppPermissions.isSuccess && ( { )} + setResetDialogOpen(false)} + title="Reset to CIPP Defaults" + variant="warning" + message="This removes all additional permissions you have layered on top of the CIPP-SAM defaults and returns the saved permission set to the built-in CIPP manifest defaults. The default permissions themselves are unaffected. You will need to complete a Permissions repair from the Permissions page, then complete a CPV refresh to finalise the chnages. Continue?" + onConfirm={() => { + handleResetToCippDefaults(); + setResetDialogOpen(false); + }} + /> ); }; From 2b7aada4535f380dae08c26418e3de47393ab2cc Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Fri, 26 Jun 2026 17:33:14 +0800 Subject: [PATCH 16/22] Update CippIntegrationFieldMapping.jsx --- src/components/CippIntegrations/CippIntegrationFieldMapping.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx b/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx index 53a88787de19..76162fef0daa 100644 --- a/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx +++ b/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx @@ -42,7 +42,7 @@ const CippIntegrationFieldMapping = () => { var missingMappings = []; fieldMapping?.data?.Mappings?.forEach((mapping) => { const exists = fieldMapping?.data?.IntegrationFields?.some( - (integrationField) => String(integrationField.value) === mapping.IntegrationId + (integrationField) => String(integrationField?.value) === mapping.IntegrationId ); if (exists) { newMappings[mapping.RowKey] = { From 7011d92f7b1b43fa30ba0a291597df193baf7974 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:25:13 +0800 Subject: [PATCH 17/22] Correct importing CA policy templates from live tenants --- .../CippComponents/CippPolicyImportDrawer.jsx | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/components/CippComponents/CippPolicyImportDrawer.jsx b/src/components/CippComponents/CippPolicyImportDrawer.jsx index 7c1630ce2eb4..ca9f5cf4bf40 100644 --- a/src/components/CippComponents/CippPolicyImportDrawer.jsx +++ b/src/components/CippComponents/CippPolicyImportDrawer.jsx @@ -47,7 +47,7 @@ export const CippPolicyImportDrawer = ({ const tenantPolicies = ApiGetCall({ url: mode === 'ConditionalAccess' - ? `/api/ListCATemplates?TenantFilter=${tenantFilter?.value || ''}` + ? `/api/ListConditionalAccessPolicies?TenantFilter=${tenantFilter?.value || ''}` : mode === 'Standards' ? `/api/listStandardTemplates?TenantFilter=${tenantFilter?.value || ''}` : `/api/ListIntunePolicy?type=ESP&TenantFilter=${tenantFilter?.value || ''}`, @@ -110,12 +110,14 @@ export const CippPolicyImportDrawer = ({ // For Conditional Access, convert RawJSON to object and send the contents let policyData = policy - // If the policy has RawJSON, parse it and use that as the data - if (policy.RawJSON) { + // If the policy has rawjson, parse it and use that as the data. + // ListConditionalAccessPolicies returns the raw Graph policy as lowercase `rawjson`. + const rawJson = policy.rawjson ?? policy.RawJSON + if (rawJson) { try { - policyData = JSON.parse(policy.RawJSON) + policyData = JSON.parse(rawJson) } catch (e) { - console.error('Failed to parse RawJSON:', e) + console.error('Failed to parse rawjson:', e) policyData = policy } } @@ -187,8 +189,19 @@ export const CippPolicyImportDrawer = ({ }, }) } else { - // For tenant policies, use the policy object directly - setViewingPolicy(policy || {}) + // For tenant policies, show the raw policy JSON when available + // (ConditionalAccess returns the Graph policy as lowercase `rawjson`). + const rawJson = policy?.rawjson ?? policy?.RawJSON + if (mode === 'ConditionalAccess' && rawJson) { + try { + setViewingPolicy(JSON.parse(rawJson)) + } catch (e) { + console.error('Failed to parse rawjson for view:', e) + setViewingPolicy(policy || {}) + } + } else { + setViewingPolicy(policy || {}) + } } setViewDialogOpen(true) } catch (error) { From f17fb64bcb94bb4bb5220540b79680602e32a2df Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:51:54 +0800 Subject: [PATCH 18/22] Correct contact template creation and editing --- .../administration/contacts-template/add.jsx | 26 +++---- .../administration/contacts-template/edit.jsx | 76 ++++++++++--------- .../contacts-template/index.jsx | 11 ++- 3 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/pages/email/administration/contacts-template/add.jsx b/src/pages/email/administration/contacts-template/add.jsx index b05da569e29e..f43bf99d7721 100644 --- a/src/pages/email/administration/contacts-template/add.jsx +++ b/src/pages/email/administration/contacts-template/add.jsx @@ -37,27 +37,27 @@ const AddContactTemplates = () => { resetForm={true} customDataformatter={(values) => { return { - DisplayName: values.displayName, + displayName: values.displayName, hidefromGAL: values.hidefromGAL, email: values.email, - FirstName: values.firstName, - LastName: values.lastName, - Title: values.jobTitle, - StreetAddress: values.streetAddress, - PostalCode: values.postalCode, - City: values.city, - State: values.state, - CountryOrRegion: values.country?.value || values.country, - Company: values.companyName, + firstName: values.firstName, + lastName: values.lastName, + jobTitle: values.jobTitle, + streetAddress: values.streetAddress, + postalCode: values.postalCode, + city: values.city, + state: values.state, + country: values.country?.value || values.country, + companyName: values.companyName, mobilePhone: values.mobilePhone, - phone: values.businessPhone, + businessPhone: values.businessPhone, website: values.website, mailTip: values.mailTip, }; }} > - ); diff --git a/src/pages/email/administration/contacts-template/edit.jsx b/src/pages/email/administration/contacts-template/edit.jsx index 987e9f45a3bb..33f40120a1c2 100644 --- a/src/pages/email/administration/contacts-template/edit.jsx +++ b/src/pages/email/administration/contacts-template/edit.jsx @@ -8,8 +8,6 @@ import { ApiGetCall } from "../../../../api/ApiCall"; import countryList from "../../../../data/countryList.json"; import { useRouter } from "next/router"; -const countryLookup = new Map(countryList.map((country) => [country.Name, country.Code])); - const EditContactTemplate = () => { const router = useRouter(); const { id } = router.query; @@ -57,32 +55,33 @@ const EditContactTemplate = () => { const contact = Array.isArray(contactTemplateInfo.data) ? contactTemplateInfo.data[0] : contactTemplateInfo.data; - const address = contact.addresses?.[0] || {}; - const phones = contact.phones || []; - // Use Map for O(1) phone lookup - const phoneMap = new Map(phones.map((p) => [p.type, p.number])); + // The template is stored as a flat object (see Invoke-AddContactTemplates), so read the + // fields directly rather than treating it as a Microsoft Graph contact. + const countryEntry = contact.country + ? countryList.find((c) => c.Code === contact.country || c.Name === contact.country) + : null; return { ContactTemplateID: id || "", displayName: contact.displayName || "", - firstName: contact.givenName || "", - lastName: contact.surname || "", + firstName: contact.firstName || "", + lastName: contact.lastName || "", email: contact.email || "", hidefromGAL: contact.hidefromGAL || false, - streetAddress: address.street || "", - postalCode: address.postalCode || "", - city: address.city || "", - state: address.state || "", - country: address.countryOrRegion ? countryLookup.get(address.countryOrRegion) || "" : "", + streetAddress: contact.streetAddress || "", + postalCode: contact.postalCode || "", + city: contact.city || "", + state: contact.state || "", + country: countryEntry ? { label: countryEntry.Name, value: countryEntry.Code } : "", companyName: contact.companyName || "", - mobilePhone: phoneMap.get("mobile") || "", - businessPhone: phoneMap.get("business") || "", + mobilePhone: contact.mobilePhone || "", + businessPhone: contact.businessPhone || "", jobTitle: contact.jobTitle || "", website: contact.website || "", mailTip: contact.mailTip || "", }; - }, [contactTemplateInfo.isSuccess, contactTemplateInfo.data]); + }, [contactTemplateInfo.isSuccess, contactTemplateInfo.data, id]); // Use callback to prevent unnecessary re-renders const resetForm = useCallback(() => { @@ -96,27 +95,30 @@ const EditContactTemplate = () => { }, [resetForm]); // Memoize custom data formatter - const customDataFormatter = useCallback((values) => { - return { - ContactTemplateID: id, - DisplayName: values.displayName, - hidefromGAL: values.hidefromGAL, - email: values.email, - FirstName: values.firstName, - LastName: values.lastName, - Title: values.jobTitle, - StreetAddress: values.streetAddress, - PostalCode: values.postalCode, - City: values.city, - State: values.state, - CountryOrRegion: values.country?.value || values.country, - Company: values.companyName, - mobilePhone: values.mobilePhone, - phone: values.businessPhone, - website: values.website, - mailTip: values.mailTip, - }; - }); + const customDataFormatter = useCallback( + (values) => { + return { + ContactTemplateID: id, + displayName: values.displayName, + hidefromGAL: values.hidefromGAL, + email: values.email, + firstName: values.firstName, + lastName: values.lastName, + jobTitle: values.jobTitle, + streetAddress: values.streetAddress, + postalCode: values.postalCode, + city: values.city, + state: values.state, + country: values.country?.value || values.country, + companyName: values.companyName, + mobilePhone: values.mobilePhone, + businessPhone: values.businessPhone, + website: values.website, + mailTip: values.mailTip, + }; + }, + [id] + ); const contactTemplate = Array.isArray(contactTemplateInfo.data) ? contactTemplateInfo.data[0] diff --git a/src/pages/email/administration/contacts-template/index.jsx b/src/pages/email/administration/contacts-template/index.jsx index d24604c9a9af..8d5fc6d69239 100644 --- a/src/pages/email/administration/contacts-template/index.jsx +++ b/src/pages/email/administration/contacts-template/index.jsx @@ -81,7 +81,14 @@ const Page = () => { target: "_self", }, ]; - const simpleColumns = ["name", "contactTemplateName", "GUID"]; + const simpleColumns = [ + "displayName", + "email", + "companyName", + "jobTitle", + "hidefromGAL", + "GUID", + ]; return ( { simpleColumns={simpleColumns} cardButton={ <> - +