Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
f4448a5
Merge pull request #6140 from KelvinTegelaar/dev
JohnDuprey Jun 9, 2026
0d8ca9d
Merge pull request #6153 from KelvinTegelaar/dev
JohnDuprey Jun 10, 2026
1e5f450
bookmark cleanup
Zacgoose Jun 16, 2026
3c82750
Warning when converting mailbox that is over 49GB
Zacgoose Jun 16, 2026
ca78e05
multi post action for multiple spo site cleanup
Zacgoose Jun 16, 2026
ff81970
required = true
KelvinTegelaar Jun 17, 2026
6a9fe95
Merge branch 'dev' of https://github.com/KelvinTegelaar/CIPP into dev
KelvinTegelaar Jun 17, 2026
383df0a
repair gdap role mapping action
Zacgoose Jun 17, 2026
0e110a2
Colliding query keys
Zacgoose Jun 17, 2026
61ddc97
Change "All Deviations" labels to "Selected Deviations" to prevent co…
TargetCrafter Jun 17, 2026
495f388
fix: tenant metric grid style
JohnDuprey Jun 18, 2026
5e8a4d9
Update PrivateRoute.js
Zacgoose Jun 18, 2026
f6a0132
Update standards.json
Zacgoose Jun 18, 2026
5426c48
spo version cleanup job check
Zacgoose Jun 18, 2026
ff00dbe
Merge pull request #6190 from TargetCrafter/Clarify-All-Deviations-st…
KelvinTegelaar Jun 18, 2026
a79f410
login page tweaks
Zacgoose Jun 18, 2026
539ef35
login tweaks
Zacgoose Jun 18, 2026
927714e
Sensitivity label fixes
Zacgoose Jun 18, 2026
e9e0147
Update PrivateRoute.js
Zacgoose Jun 18, 2026
cacd076
Update cipp-users.js
Zacgoose Jun 18, 2026
6fa16c5
chore: bump version to 10.5.3
JohnDuprey Jun 18, 2026
095fd97
Merge pull request #6200 from KelvinTegelaar/dev
JohnDuprey Jun 18, 2026
95c31b8
Update CIPPDBCacheTypes.json
Zacgoose Jun 19, 2026
2403bd4
Update alerts.json
Zacgoose Jun 19, 2026
b08e704
Update CippUserActions.jsx
Zacgoose Jun 22, 2026
40d3ee8
Fix formatting in alerts list component
michaelmsonne Jun 22, 2026
64c492c
Custom Test Engine Changes
Zacgoose Jun 22, 2026
aaaff43
fix: remove parameters from API call
JohnDuprey Jun 22, 2026
82e42d9
Update index.js
Zacgoose Jun 22, 2026
461c1b4
chore: bump version to 10.5.4
JohnDuprey Jun 22, 2026
f01c13a
fix: disallow creatable for CA deploy drawer
kris6673 Jun 23, 2026
e36ec40
dynamic group nesting
Zacgoose Jun 24, 2026
5d77e82
Fixes phishing resistant MFA card
Zacgoose Jun 24, 2026
1a5ead7
Update M365Licenses.json
Zacgoose Jun 24, 2026
77a9289
Timestamp parsing fixes
Zacgoose Jun 24, 2026
ab94c4c
Update index.js
Zacgoose Jun 24, 2026
4b552b7
feat: add mega gigantic huuuuge change
kris6673 Jun 24, 2026
6cd2795
Update add.jsx
Zacgoose Jun 24, 2026
a2d216a
Update CippNotificationForm.jsx
Zacgoose Jun 24, 2026
3a6d96e
Update standards.json
Zacgoose Jun 25, 2026
a494cd7
feat(defender): add MTD role toggle, fix iOS sync
kris6673 Jun 25, 2026
3bb7e86
Merge pull request #6231 from kris6673/defender-deployment
Zacgoose Jun 25, 2026
ae7341d
Update standards.json
Zacgoose Jun 25, 2026
56756d4
ISO 639-1 and ISO 3166-1 autocomplete options
Zacgoose Jun 25, 2026
28178b0
Permission repair improvements
Zacgoose Jun 26, 2026
2b7aada
Update CippIntegrationFieldMapping.jsx
Zacgoose Jun 26, 2026
7011d92
Correct importing CA policy templates from live tenants
Zacgoose Jun 26, 2026
f17fb64
Correct contact template creation and editing
Zacgoose Jun 26, 2026
ce24422
fix: check for string before localeCompare
JohnDuprey Jun 26, 2026
0c58d53
Merge pull request #6219 from kris6673/disallow-creatable
JohnDuprey Jun 26, 2026
88d9032
Merge pull request #6224 from kris6673/mega-PR
JohnDuprey Jun 26, 2026
ae25e5f
Merge pull request #6213 from michaelmsonne/patch-2
Zacgoose Jun 26, 2026
289f726
hideBulk for compliance actions as they are not available at this stage
Zacgoose Jun 26, 2026
60949a4
fix: replace remaining ListUsers queries with ListGraphRequest
JohnDuprey Jun 26, 2026
bd63730
chore: update version to 10.5.5
JohnDuprey Jun 26, 2026
ab2b5d6
Sensitivity Rule Templating and standard overhaul
Zacgoose Jun 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cipp",
"version": "10.5.2",
"version": "10.5.5",
"author": "CIPP Contributors",
"homepage": "https://cipp.app/",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion public/version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "10.5.2"
"version": "10.5.5"
}
31 changes: 26 additions & 5 deletions src/components/CippComponents/AuthMethodCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,23 @@ export const AuthMethodCard = ({ data, isLoading }) => {
return null;
}

const phishableMethods = ["mobilePhone", "email", "microsoftAuthenticatorPush"];
const phishResistantMethods = ["fido2", "windowsHelloForBusiness", "x509Certificate"];
const phishableMethods = [
"mobilePhone",
"alternateMobilePhone",
"officePhone",
"email",
"microsoftAuthenticatorPush",
"softwareOneTimePasscode",
"hardwareOneTimePasscode",
];
const passkeyMethods = [
"fido2SecurityKey",
"passKeyDeviceBound",
"passKeyDeviceBoundAuthenticator",
"passKeyDeviceBoundWindowsHello",
"x509Certificate",
];
const phishResistantMethods = [...passkeyMethods, "windowsHelloForBusiness"];

let singleFactor = 0;
let phishableCount = 0;
Expand Down Expand Up @@ -48,20 +63,26 @@ export const AuthMethodCard = ({ data, isLoading }) => {

if (hasPhishResistant) {
phishResistantCount++;
if (methods.includes("fido2") || methods.includes("x509Certificate")) {
if (methods.some((m) => passkeyMethods.includes(m))) {
passkeyCount++;
}
if (methods.includes("windowsHelloForBusiness")) {
whfbCount++;
}
} else if (hasPhishable) {
phishableCount++;
if (methods.includes("mobilePhone") || methods.includes("email")) {
if (
methods.includes("mobilePhone") ||
methods.includes("alternateMobilePhone") ||
methods.includes("officePhone") ||
methods.includes("email")
) {
phoneCount++;
}
if (
methods.includes("microsoftAuthenticatorPush") ||
methods.includes("softwareOneTimePasscode")
methods.includes("softwareOneTimePasscode") ||
methods.includes("hardwareOneTimePasscode")
) {
authenticatorCount++;
}
Expand Down
33 changes: 26 additions & 7 deletions src/components/CippComponents/AuthMethodSankey.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,23 @@ export const AuthMethodSankey = ({ data }) => {
return null;
}

// Categorize MFA methods as phishable or phish-resistant
const phishableMethods = ["mobilePhone", "email", "microsoftAuthenticatorPush"];
const phishResistantMethods = ["fido2", "windowsHelloForBusiness", "x509Certificate"];
const phishableMethods = [
"mobilePhone",
"alternateMobilePhone",
"officePhone",
"email",
"microsoftAuthenticatorPush",
"softwareOneTimePasscode",
"hardwareOneTimePasscode",
];
const passkeyMethods = [
"fido2SecurityKey",
"passKeyDeviceBound",
"passKeyDeviceBoundAuthenticator",
"passKeyDeviceBoundWindowsHello",
"x509Certificate",
];
const phishResistantMethods = [...passkeyMethods, "windowsHelloForBusiness"];

let singleFactor = 0;
let phishableCount = 0;
Expand Down Expand Up @@ -54,21 +68,26 @@ export const AuthMethodSankey = ({ data }) => {
if (hasPhishResistant) {
phishResistantCount++;
// Count specific phish-resistant methods
if (methods.includes("fido2") || methods.includes("x509Certificate")) {
if (methods.some((m) => passkeyMethods.includes(m))) {
passkeyCount++;
}
if (methods.includes("windowsHelloForBusiness")) {
whfbCount++;
}
} else if (hasPhishable) {
phishableCount++;
// Count specific phishable methods
if (methods.includes("mobilePhone") || methods.includes("email")) {
if (
methods.includes("mobilePhone") ||
methods.includes("alternateMobilePhone") ||
methods.includes("officePhone") ||
methods.includes("email")
) {
phoneCount++;
}
if (
methods.includes("microsoftAuthenticatorPush") ||
methods.includes("softwareOneTimePasscode")
methods.includes("softwareOneTimePasscode") ||
methods.includes("hardwareOneTimePasscode")
) {
authenticatorCount++;
}
Expand Down
149 changes: 74 additions & 75 deletions src/components/CippComponents/CippAppPermissionBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -462,10 +462,11 @@ const CippAppPermissionBuilder = ({
if (appTable !== undefined && appTable?.length === 0) {
setAppTable(
spPermissions?.applicationPermissions
?.sort((a, b) => a.value.localeCompare(b.value))
?.sort((a, b) => (a.value ?? "").localeCompare(b.value ?? ""))
?.map((perm) => ({
id: perm.id,
value: perm.value,
required: perm.required ?? false,
description: spInfo?.Results?.appRoles.find((role) => role.id === perm.id)
?.description,
})),
Expand All @@ -474,10 +475,11 @@ const CippAppPermissionBuilder = ({
if (delegatedTable !== undefined && delegatedTable.length === 0) {
setDelegatedTable(
spPermissions?.delegatedPermissions
?.sort((a, b) => a.value.localeCompare(b.value))
?.sort((a, b) => (a.value ?? "").localeCompare(b.value ?? ""))
?.map((perm) => ({
id: perm.id,
value: perm.value,
required: perm.required ?? false,
description:
spInfo?.Results?.publishedPermissionScopes.find((scope) => scope.id === perm.id)
?.userConsentDescription ?? "Manually added",
Expand Down Expand Up @@ -625,6 +627,7 @@ const CippAppPermissionBuilder = ({
label: "Delete Permission",
icon: <Delete />,
noConfirm: true,
condition: (row) => !row.required,
customFunction: (row) => handleRemoveRow("applicationPermissions", row),
},
]}
Expand Down Expand Up @@ -690,6 +693,7 @@ const CippAppPermissionBuilder = ({
label: "Delete Permission",
icon: <Delete />,
noConfirm: true,
condition: (row) => !row.required,
customFunction: (row) => handleRemoveRow("delegatedPermissions", row),
},
]}
Expand Down Expand Up @@ -788,7 +792,7 @@ const CippAppPermissionBuilder = ({
</div>
</Tooltip>

<Tooltip title="Reset to Default">
<Tooltip title="Reset to Current Defaults (discard unsaved changes)">
<Button
onClick={() => {
confirmReset();
Expand Down Expand Up @@ -929,79 +933,74 @@ const CippAppPermissionBuilder = ({
</Grid>
)}

{newPermissions?.MissingPermissions &&
newPermissions?.Type === "Table" &&
Object.keys(newPermissions?.MissingPermissions).length > 0 && (
<Grid container sx={{ width: "100%", mt: 3 }}>
<Grid size={{ xl: 8, xs: 12 }}>
<Alert
color="warning"
icon={<WarningAmberOutlined />}
action={
<Tooltip title="Add Missing Permissions">
<IconButton
onClick={() => {
var updatedPermissions = JSON.parse(JSON.stringify(newPermissions));
Object.keys(newPermissions?.MissingPermissions).map((perm) => {
Object.keys(newPermissions?.MissingPermissions[perm]).map(
(type) => {
if (!updatedPermissions.Permissions[perm][type]) {
updatedPermissions.Permissions[perm][type] = [];
}
newPermissions?.MissingPermissions[perm][type].map((p) => {
updatedPermissions.Permissions[perm][type].push(p);
});
},
);
});
updatedPermissions.MissingPermissions = {};
setNewPermissions(updatedPermissions);
}}
>
<SvgIcon fontSize="small">
<WrenchIcon />
</SvgIcon>
</IconButton>
</Tooltip>
}
>
<b>New Permissions Available</b>
{Object.keys(newPermissions?.MissingPermissions).map((perm) => {
// translate appid to display name
var sp = servicePrincipals?.Results?.find((sp) => sp.appId === perm);
return (
<Typography
variant="body2"
textColor="secondary"
key={`missing-${perm}`}
>
{sp?.displayName}:{" "}
{Object.keys(newPermissions?.MissingPermissions[perm]).map((type) => {
return (
<>
{newPermissions?.MissingPermissions[perm][type].length > 0 && (
<React.Fragment key={`missing-${perm}-${type}`}>
{type == "applicationPermissions"
? "Application"
: "Delegated"}{" "}
-{" "}
{newPermissions?.MissingPermissions[perm][type]
.map((p) => {
return p.value;
})
.join(", ")}
</React.Fragment>
)}
</>
);
})}
</Typography>
);
})}
</Alert>
{newPermissions?.PartnerAppDiff &&
Object.keys(newPermissions?.PartnerAppDiff).length > 0 &&
(() => {
const diff = newPermissions.PartnerAppDiff;
const appIds = Object.keys(diff);
const hasMissing = appIds.some(
(perm) =>
(diff[perm].missingApplicationPermissions?.length ?? 0) > 0 ||
(diff[perm].missingDelegatedPermissions?.length ?? 0) > 0,
);
const hasExtra = appIds.some(
(perm) =>
(diff[perm].extraApplicationPermissions?.length ?? 0) > 0 ||
(diff[perm].extraDelegatedPermissions?.length ?? 0) > 0,
);
const renderList = (perm, appKey, delKey) => {
const sp = servicePrincipals?.Results?.find((sp) => sp.appId === perm);
const app = diff[perm][appKey] ?? [];
const del = diff[perm][delKey] ?? [];
if (app.length === 0 && del.length === 0) return null;
return (
<Typography variant="body2" key={`${appKey}-${perm}`}>
{sp?.displayName ?? perm}:{" "}
{app.length > 0 && <>Application - {app.map((p) => p.value).join(", ")} </>}
{del.length > 0 && <>Delegated - {del.map((p) => p.value).join(", ")}</>}
</Typography>
);
};
return (
<Grid container sx={{ width: "100%", mt: 3 }} spacing={2}>
{hasMissing && (
<Grid size={{ xl: 8, xs: 12 }}>
<Alert color="warning" icon={<WarningAmberOutlined />}>
<b>
Permissions missing from the {appDisplayName} app registration (run
Repair Permissions to add, then a CPV refresh to apply to tenants)
</b>
{appIds.map((perm) =>
renderList(
perm,
"missingApplicationPermissions",
"missingDelegatedPermissions",
),
)}
</Alert>
</Grid>
)}
{hasExtra && (
<Grid size={{ xl: 8, xs: 12 }}>
<Alert color="info" icon={<WarningAmberOutlined />}>
<b>
Extra permissions present on the {appDisplayName} app registration that
are not part of the CIPP defaults or your additional permissions
(review and remove manually if not required)
</b>
{appIds.map((perm) =>
renderList(
perm,
"extraApplicationPermissions",
"extraDelegatedPermissions",
),
)}
</Alert>
</Grid>
)}
</Grid>
</Grid>
)}
);
})()}

<Box sx={{ mt: 3 }}>
{selectedApp &&
Expand Down
7 changes: 5 additions & 2 deletions src/components/CippComponents/CippAutocomplete.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ export const CippAutoComplete = React.forwardRef((props, ref) => {
const currentTenant = api?.tenantFilter ? api.tenantFilter : useSettings().currentTenant
useEffect(() => {
if (actionGetRequest.isSuccess && !actionGetRequest.isFetching) {
const lastPage = actionGetRequest.data?.pages[actionGetRequest.data.pages.length - 1]
// Guard against a non-paginated cache shape (e.g. when a queryKey is accidentally shared
// with a useQuery/ApiGetCall consumer that stores a plain array instead of { pages }).
const pages = actionGetRequest.data?.pages
const lastPage = Array.isArray(pages) ? pages[pages.length - 1] : undefined
const nextLinkExists = lastPage?.Metadata?.nextLink
if (nextLinkExists) {
actionGetRequest.fetchNextPage()
Expand Down Expand Up @@ -262,7 +265,7 @@ export const CippAutoComplete = React.forwardRef((props, ref) => {
finalOptions = finalOptions.filter((o) => !removeOptions.includes(o.value))
}
if (sortOptions) {
finalOptions.sort((a, b) => a.label?.localeCompare(b.label))
finalOptions.sort((a, b) => String(a.label ?? "").localeCompare(String(b.label ?? "")))
}
return finalOptions
}, [api, usedOptions, options, removeOptions, sortOptions])
Expand Down
Loading