Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1bad0c7
chore(sync): port Manage365 upstream-sync process, tooling, and histo…
ClintThomson Aug 15, 2026
5d9f17d
feat(branding): Manage365 identity — logos, icons, theme, titles, dua…
ClintThomson Aug 15, 2026
0801ea6
feat(backend): port Manage365 custom backend into Craft monorepo
ClintThomson Aug 15, 2026
08ff84f
feat(frontend): port Manage365 custom UI into monorepo
ClintThomson Aug 15, 2026
b16393a
docs(migration): mark phases 0-4 complete, record tranche log and cut…
ClintThomson Aug 15, 2026
e31af5f
fix(standards): align backend standards.json with frontend (fork vers…
ClintThomson Aug 15, 2026
8a61e07
fix(backend): repair malformed CIPPNG guard in Start-DurableCleanup (…
ClintThomson Aug 15, 2026
ba4441b
docs: reframe README as Manage365 v2
ClintThomson Aug 15, 2026
e8bfee2
ci: drop CyberDrain-only workflows and enable fork CI
ClintThomson Aug 15, 2026
8188b78
sync(upstream): merge v10.9.1
ClintThomson Oct 5, 2026
18d9c13
sync(upstream): merge v10.10.3
ClintThomson Oct 5, 2026
321b572
sync(upstream): merge v11.0.2
ClintThomson Oct 5, 2026
5b21577
fix(identity): assign licenses app-only and queue inline BEC inbox rules
ClintThomson Oct 5, 2026
759a687
fix(frontend): resolve modules renamed in the 11.0 merge
ClintThomson Oct 5, 2026
72891cd
chore(version): set Manage365 6.1.0 on CIPP baseline 11.0.2
ClintThomson Oct 5, 2026
d3116b1
chore(container): update default container image to Celeratec Manage3…
ClintThomson Oct 6, 2026
6fdd7ed
fix(ci): clear the test and analyzer failures blocking the monorepo PR
ClintThomson Oct 7, 2026
a6cef94
fix(ci): restore the toolbar, standards, and spec checks blocking PR 1
ClintThomson Oct 10, 2026
1b522d2
fix(ci): restore the table, wizard, and sign-in checks still failing …
ClintThomson Oct 10, 2026
d8acb43
fix(ci): drop preset metadata from graph runs and wait for the action…
ClintThomson Oct 10, 2026
2ad5e2b
Align Manage365 to CIPP 11.0.2
ClintThomson Oct 10, 2026
6deb84a
ci: publish Manage365 images with the upstream baseline
ClintThomson Oct 10, 2026
452c693
Merge pull request #6 from Celeratec/chore/manage365-image
ClintThomson Oct 10, 2026
12a4465
fix: restore the dashboard and ship Manage365 6.1.1
ClintThomson Oct 10, 2026
3005a3f
fix: call the app Manage365 and restore the user action menu
ClintThomson Oct 10, 2026
471cb28
fix: close the setup wizard string broken by the rename
ClintThomson Oct 10, 2026
c305358
Merge pull request #7 from Celeratec/fix/dashboard-users-icon
ClintThomson Oct 10, 2026
728ab08
Ship Manage365 6.1.3 on the CIPP 11.0.2 baseline.
ClintThomson Oct 11, 2026
f937e0e
Merge pull request #8 from Celeratec/release/6.1.3
ClintThomson Oct 11, 2026
cf9750f
Ship Manage365 6.1.4 with a smaller magenta header badge.
ClintThomson Oct 11, 2026
6421909
Merge pull request #9 from Celeratec/release/6.1.4
ClintThomson Oct 11, 2026
d33c887
Fix integration page crashes and ship Manage365 6.1.5.
ClintThomson Oct 11, 2026
ad24f89
Merge pull request #10 from Celeratec/release/6.1.5
ClintThomson Oct 11, 2026
8638b79
Wait for the new CIPP-MCP app to replicate before configuring it.
ClintThomson Oct 11, 2026
47b38ca
Merge pull request #11 from Celeratec/release/6.1.6
ClintThomson Oct 11, 2026
6308a18
Keep super-admin pages available on CIPP-NG containers.
ClintThomson Oct 12, 2026
a0b5961
Merge pull request #12 from Celeratec/release/6.1.7
ClintThomson Oct 12, 2026
49768cc
Stop the user Exchange page from crashing on missing icons.
ClintThomson Oct 12, 2026
4258b7e
Merge pull request #13 from Celeratec/release/6.1.8
ClintThomson Oct 12, 2026
5781b68
Accept the protocol name when enabling or disabling mailbox access.
ClintThomson Oct 12, 2026
16a8e06
Merge pull request #14 from Celeratec/release/6.1.9
ClintThomson Oct 12, 2026
dccdc6e
Ship Manage365 6.1.10.
ClintThomson Oct 12, 2026
5085ba1
Merge pull request #15 from Celeratec/release/6.1.10
ClintThomson Oct 12, 2026
3313172
Fix SharePoint recycle bin endpoints: verify deletion and fix pagination
cursoragent Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
17 changes: 17 additions & 0 deletions .cursor/rules/aikido_rules.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
alwaysApply: true
always_on: true
trigger: always_on
applyTo: "**"
description: Aikido MCP Security Rules
---

# Project security best practices

- Always run **aikido_full_scan** on generated, added and modified first-party code, unless the prompt explicitly says not to.
- Always provide the full file content to the scanner.
- If the scan identifies any security issues in newly generated or modified code, fix the issues using the scan result and remediation provided by **Aikido**.
- After applying fixes, run aikido_full_scan again to verify that the issues were successfully resolved, and no new issues were introduced.
- Repeat the fix-and-rescan cycle until the code passes with zero remaining or newly introduced security issues.
- If the **Aikido MCP server** required to run scans is not installed or not started successfully, inform the user with a message directing them to install it using the official Aikido MCP setup guide at:
**https://help.aikido.dev/ide-plugins/aikido-mcp**.
160 changes: 160 additions & 0 deletions .github/agents/CIPP-Endpoint-Agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
name: CIPP HTTP Endpoint Builder
description: >
Adds or changes HTTP endpoints in backend/Modules/CIPPHTTP (Invoke-List/Add/Edit/Remove/Exec*) and the
CIPPCore helpers they call, with response shapes and status codes the frontend, MCP and the response cache
depend on.
---

# CIPP HTTP Endpoint Builder

## Mission

Write or change an HTTP endpoint so that it:

- returns the body shape the frontend expects,
- returns a status code that tells the truth about what happened,
- documents its request contract through code the OpenAPI generator can read.

Status codes are not cosmetic in CIPP:

- **Response cache:** the runtime caches 2xx GET responses (for up to 600 s). An error returned as 200 is cached and served as data.
- **MCP:** the MCP tool layer treats `>= 400` as an error and anything else as a result.
- **Frontend:** the frontend retries and renders based on the status.

---

## Anatomy

```powershell
function Invoke-ExecThing {
<#
.FUNCTIONALITY
Entrypoint
.ROLE
Identity.User.ReadWrite
.DESCRIPTION
Shown in the generated API docs.
#>
[CmdletBinding()]
param($Request, $TriggerMetadata)
$Headers = $Request.Headers
$TenantFilter = $Request.Body.tenantFilter

if (-not $Request.Body.UserId) {
return ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::BadRequest
Body = @{ Results = 'UserId is required' }
})
}

try {
$Result = Set-CIPPThing -UserId $Request.Body.UserId -TenantFilter $TenantFilter -Headers $Headers
$StatusCode = [HttpStatusCode]::OK
} catch {
$ErrorMessage = Get-CippException -Exception $_
$Result = "Failed to update thing: $($ErrorMessage.NormalizedError)"
Write-LogMessage -headers $Headers -API 'ExecThing' -tenant $TenantFilter -message $Result -Sev 'Error' -LogData $ErrorMessage
$StatusCode = Get-CippErrorStatusCode -ErrorRecord $_
}

return ([HttpResponseContext]@{
StatusCode = $StatusCode
Body = @{ Results = $Result }
})
}
```

Response bodies:

- **Action endpoints** (`Add`/`Edit`/`Remove`/`Exec`): `Body = @{ Results = ... }`, where `Results` is a string or
an array of `@{ resultText = '...'; state = 'success' | 'error' }`. Use the same shape for errors. The UI reads
`Results` from error responses too.
- **List endpoints**: return the array itself: `Body = @($Data)`.
- Mark each per-item result with `state`. If `state` is missing, the UI has to guess severity from the text.

---

## Status codes

| Situation | Status |
| --- | --- |
| Success | `200 OK` (`202 Accepted` when work is queued) |
| Missing or invalid request input, or a request the business rules refuse | `400 BadRequest` |
| A resource the caller named does not exist (template, task, user to act on) | `404 NotFound` |
| A CIPP access check refuses the caller (role, allowed tenants, `Test-CIPPAccess`) | `403 Forbidden` |
| A Graph / Exchange / SharePoint / Azure / table call failed, or anything unexpected | `500 InternalServerError` |
| Several items acted on: all succeeded / some failed / all failed | `200` / `207 MultiStatus` / `500` |

Rules that follow from the table:

- **A catch never returns `OK`.** A failure that becomes "Failed ..." text inside a 200 is a bug.
`backend/Tests/Static/CatchBlockStatusCode.Tests.ps1` enforces this, with a short allow-list for deliberate fallbacks.
- **403 is only for CIPP's own access checks.** If Microsoft refuses the CIPP app in a customer tenant, that is an
upstream failure: return 500.
- **Upstream failures return 500, not 400.** The frontend does not retry 4xx, and 400 tells the caller their input was wrong.
- **Validate input before the `try`**, as an early `return` with `BadRequest` (or `NotFound`) inside an `if`. A
validation `throw` inside the `try` lands in the same catch as upstream failures, so it can't be told apart. The
OpenAPI generator marks a field required when an `if` body rejects it with `throw` or `BadRequest`, so keep the
guard as an `if`.
- **Multi-item endpoints** count what they were asked to act on and what failed, then return
`$StatusCode = Get-CippBulkStatusCode -Total $Total -Failed $Failed`. Use `$Failed++`, never `+=`. Only count real item
failures. An optional enrichment step that falls back to a default is not a failure. A List endpoint that loops
tenants follows the same rule: partial data is 207, nothing is 500.
- **Polled status endpoints and protocol endpoints** (JSON-RPC, OAuth token passthrough) can carry an error inside a
200 when the client depends on that. Leave them that way on purpose, and add them to the guard's allow-list with a reason.

---

## Helpers that endpoints call

A helper must let its caller tell an input problem apart from an upstream failure:

- Throw `[System.ArgumentException]::new('<message>')` for invalid input or a refused request.
- Throw `[System.Management.Automation.ItemNotFoundException]::new('<message>')` for a named resource that does not exist.
- Let upstream failures throw as they are.
- In the endpoint catch, `Get-CippErrorStatusCode -ErrorRecord $_` maps these to 400 / 404 / 500. It also looks inside
wrapped exceptions, because a script block run through a .NET method surfaces as
`MethodInvocationException -> RuntimeException -> ArgumentException`.
- A layer between the helper and the endpoint must not catch a typed exception and rethrow it as a string
(`throw "Failed: $($_.Exception.Message)"`). That loses the type.
- A helper that acts on several items returns one `[pscustomobject]@{ resultText = '...'; state = 'success' | 'error' }`
per item instead of swallowing failures into strings, so the caller can count them.
- Before changing what a helper returns or throws, read **every** caller. Scheduled and background callers
(`New-CIPPUserTask`, offboarding tasks, BEC containment, extension syncs) must keep collecting per-step results and
must not start aborting halfway.

---

## Frontend contract

- `ApiGetCall` / `ApiPostCall` do not retry 4xx (except 408 and 429). They retry 5xx shed responses (503) and do not
retry 500.
- Multi-row table actions (`CippApiDialog` with `multiPost: false`) send one request per row. A row that returns an
error is reported as `{ Results: [{ resultText, state: 'error' }] }` and the rest still run. `onResult` gets
`{ failed: true }` as its second argument for that row.
- `CippApiResults` renders `Results` / `resultText` / `error` from error responses. Axios treats 207 as success, and
each item's `state` decides how it is shown.
- When you change an endpoint's codes, grep `frontend/src` for `/api/<Name>`. A page that reads data only on success
needs `isError` handling (`getCippError(x.error)`) if it used to get its error inside a 200.

---

## OpenAPI

`backend/Config/openapi.json` is generated from the entrypoint AST (`build/tools/build-openapi.ps1`):

- Status codes are documented from every `[HttpStatusCode]::X` in the file.
- An endpoint that uses `Get-CippBulkStatusCode` gets 200, 207 and 500.
- Codes that only come from `Get-CippErrorStatusCode` are not visible to the generator. Write the 400/404 guards as
explicit `[HttpStatusCode]::BadRequest` / `NotFound` returns where you can.
- Ask the maintainer before regenerating the spec.

---

## Tests

- If `backend/Tests/Endpoint/Invoke-<Name>.Tests.ps1` exists, add a case for each status the change introduces.
- Pin a helper's new contract with `Should -Throw -ExceptionType ([System.ArgumentException])`, or by asserting its
per-item `state` results.
- Always run `Invoke-Pester -Path backend/Tests/Static` (it includes the catch-block guard).
14 changes: 14 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,17 @@ fix(graph): handle expired token on retry
docs: update authentication model overview
refactor(standards)!: rename remediation parameter
```

## HTTP endpoint status codes

Endpoints in `backend/Modules/CIPPHTTP` must return a status code that matches what happened. The full guide is
`.github/agents/CIPP-Endpoint-Agent.md`.

- `200` success, `400` invalid input or refused request, `404` named resource missing, `403` CIPP access check only,
`500` upstream (Graph/Exchange/SharePoint/Azure/table) or unexpected failure.
- Multi-item endpoints: `$StatusCode = Get-CippBulkStatusCode -Total $Total -Failed $Failed` (200 / 207 / 500).
- A `catch` never returns `[HttpStatusCode]::OK`. Use `$StatusCode = Get-CippErrorStatusCode -ErrorRecord $_`, which
maps `ArgumentException` to 400, `ItemNotFoundException` to 404 and anything else to 500.
- Validate input before the `try`, as an `if` that returns `BadRequest`, so it can't be mistaken for an upstream failure.
- Helpers throw `[System.ArgumentException]` / `[System.Management.Automation.ItemNotFoundException]` for input and
not-found errors, and return per-item `@{ resultText; state }` results rather than swallowing failures into strings.
16 changes: 0 additions & 16 deletions .github/workflows/Assign_Issue_Volunteer.yml

This file was deleted.

24 changes: 0 additions & 24 deletions .github/workflows/Close_Stale_Issues.yml

This file was deleted.

42 changes: 0 additions & 42 deletions .github/workflows/CodeQL_Analyser.yml

This file was deleted.

22 changes: 0 additions & 22 deletions .github/workflows/Comment_on_Issues.yml

This file was deleted.

Loading
Loading