Spec 423 security compliance readiness pack implementation. Head commit: c49acba7.
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #490
364 lines
15 KiB
PHP
364 lines
15 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\TenantConfiguration\CoverageV2Readiness;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\OperationRun;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\TenantConfigurationResource;
|
|
use App\Models\TenantConfigurationResourceEvidence;
|
|
use App\Models\TenantConfigurationResourceType;
|
|
use App\Models\TenantConfigurationSupportedScope;
|
|
use App\Models\User;
|
|
use App\Services\TenantConfiguration\ResourceTypeRegistry;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\OperationRunType;
|
|
use App\Support\TenantConfiguration\CanonicalKeyKind;
|
|
use App\Support\TenantConfiguration\CaptureOutcome;
|
|
use App\Support\TenantConfiguration\ClaimState;
|
|
use App\Support\TenantConfiguration\CoverageLevel;
|
|
use App\Support\TenantConfiguration\EvidenceState;
|
|
use App\Support\TenantConfiguration\IdentityState;
|
|
use App\Support\TenantConfiguration\SourceClass;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
pest()->browser()->timeout(60_000);
|
|
|
|
it('Spec423 smokes the Coverage v2 inspect surface for Security and Compliance comparable renderable evidence', function (): void {
|
|
[$user, $environment] = spec423CoverageV2BrowserFixture();
|
|
spec423AuthenticateCoverageV2Browser($this, $user, $environment);
|
|
|
|
$page = visit(CoverageV2Readiness::getUrl(tenant: $environment, panel: 'admin'))
|
|
->resize(768, 1100)
|
|
->waitForText('Coverage v2 Readiness')
|
|
->waitForText('Spec423 Browser DLP Policy')
|
|
->assertSee('Resource type registry')
|
|
->assertSee('Resource instances')
|
|
->assertSee('DLP compliance policy')
|
|
->assertSee('Coverage level')
|
|
->assertSee('Renderable')
|
|
->assertSee('Internal only')
|
|
->assertDontSee('dlpCompliancePolicy:provider_external_id:spec423-browser')
|
|
->assertDontSee('Security and Compliance covered')
|
|
->assertDontSee('Purview coverage')
|
|
->assertDontSee('certified')
|
|
->assertDontSee('restore-ready')
|
|
->assertDontSee('customer-ready')
|
|
->assertDontSee('legal-ready')
|
|
->assertDontSee('spec423-browser-raw-secret')
|
|
->assertDontSee('spec423-browser-client-secret')
|
|
->assertDontSee('spec423-browser-dlp-incident-content')
|
|
->assertScript('typeof window.Livewire !== "undefined"', true)
|
|
->assertScript('(() => document.querySelectorAll("table tbody tr").length > 0)()', true)
|
|
->assertScript(<<<'JS'
|
|
(() => {
|
|
const row = Array.from(document.querySelectorAll('table tbody tr'))
|
|
.find((candidate) => candidate.textContent.includes('Spec423 Browser DLP Policy'));
|
|
const resourceTypeCellText = row?.querySelectorAll('td')?.[1]?.innerText ?? '';
|
|
|
|
return resourceTypeCellText.includes('DLP compliance policy')
|
|
&& ! resourceTypeCellText.includes('dlpCompliancePolicy');
|
|
})()
|
|
JS, true)
|
|
->assertScript("(() => performance.getEntriesByType('resource').filter((entry) => /graph\\.microsoft\\.com|\\/tcm\\b|provider-remote/i.test(entry.name)).length)()", 0)
|
|
->assertScript("(() => Array.from(document.querySelectorAll('main button, main a')).map((element) => element.textContent.trim()).filter(Boolean).some((label) => /^(Capture|Restore|Certify|Export|Download)$/i.test(label)))()", false)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
$page->script(<<<'JS'
|
|
(() => {
|
|
const rows = Array.from(document.querySelectorAll('table tbody tr'));
|
|
const row = rows.find((candidate) => candidate.textContent.includes('Spec423 Browser DLP Policy'));
|
|
const inspect = Array.from(row?.querySelectorAll('button, a') ?? [])
|
|
.find((element) => element.textContent.includes('Spec423 Browser DLP Policy'));
|
|
|
|
inspect?.click();
|
|
})()
|
|
JS);
|
|
|
|
$page
|
|
->waitForText('Coverage: Renderable')
|
|
->assertSee('DLP compliance policy')
|
|
->assertSee('Display name')
|
|
->assertSee('Spec423 Browser DLP Policy')
|
|
->assertSee('Review readiness')
|
|
->assertSee('Manual review required')
|
|
->assertSee('Mode / enforcement')
|
|
->assertSee('Enforce')
|
|
->assertSee('Rules')
|
|
->assertSee('BlockAccess')
|
|
->assertSee('Compare summary')
|
|
->assertSee('Previous comparable evidence')
|
|
->assertSee('Mode')
|
|
->assertSee('Redacted fields')
|
|
->assertSee('Rules.0.DlpIncidentContent')
|
|
->assertSee('Evidence: Content backed')
|
|
->assertSee('Identity: Stable')
|
|
->assertSee('Claim: Internal only')
|
|
->assertSee('View technical details')
|
|
->assertDontSee('Source: TCM')
|
|
->assertDontSee('Evidence hash')
|
|
->assertDontSee('Source contract')
|
|
->assertDontSee('Source schema hash')
|
|
->assertDontSee('Operation #')
|
|
->assertDontSee('dlpCompliancePolicy:provider_external_id:spec423-browser')
|
|
->assertScript('(() => document.querySelector("details")?.open === false)()', true)
|
|
->assertScript(<<<'JS'
|
|
(() => {
|
|
const technicalDetails = document.querySelector('details')?.textContent ?? '';
|
|
|
|
return technicalDetails.includes('Source: TCM')
|
|
&& technicalDetails.includes('Evidence hash')
|
|
&& technicalDetails.includes('Source contract')
|
|
&& technicalDetails.includes('Source schema hash')
|
|
&& technicalDetails.includes('Operation #');
|
|
})()
|
|
JS, true)
|
|
->assertDontSee('Security and Compliance covered')
|
|
->assertDontSee('Purview coverage')
|
|
->assertDontSee('certified')
|
|
->assertDontSee('restore-ready')
|
|
->assertDontSee('customer-ready')
|
|
->assertDontSee('legal-ready')
|
|
->assertDontSee('spec423-browser-raw-secret')
|
|
->assertDontSee('spec423-browser-client-secret')
|
|
->assertDontSee('spec423-browser-dlp-incident-content')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
});
|
|
|
|
/**
|
|
* @return array{0: User, 1: ManagedEnvironment}
|
|
*/
|
|
function spec423CoverageV2BrowserFixture(): array
|
|
{
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
$environment = ManagedEnvironment::factory()->active()->create([
|
|
'name' => 'Spec423 Browser Environment',
|
|
'external_id' => 'spec423-browser-environment',
|
|
]);
|
|
|
|
[$user, $environment] = createUserWithTenant(
|
|
tenant: $environment,
|
|
role: 'owner',
|
|
workspaceRole: 'owner',
|
|
clearCapabilityCaches: true,
|
|
);
|
|
|
|
$connection = ProviderConnection::factory()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'display_name' => 'Spec423 Browser Microsoft provider',
|
|
]);
|
|
|
|
TenantConfigurationSupportedScope::factory()->create([
|
|
'scope_key' => 'spec423_browser_internal_security_compliance_scope',
|
|
'display_name' => 'Spec423 Browser internal Security Compliance scope',
|
|
'minimum_coverage_level' => CoverageLevel::ContentBacked->value,
|
|
'included_resource_types' => ['dlpCompliancePolicy'],
|
|
'allow_graph_fallback' => false,
|
|
'allow_beta' => false,
|
|
'customer_claims_allowed' => false,
|
|
]);
|
|
|
|
spec423BrowserEvidenceResource(
|
|
environment: $environment,
|
|
user: $user,
|
|
connection: $connection,
|
|
canonicalType: 'dlpCompliancePolicy',
|
|
displayName: 'Spec423 Browser DLP Policy',
|
|
previousPayload: [
|
|
'DisplayName' => 'Spec423 Browser DLP Policy',
|
|
'Mode' => 'Audit',
|
|
'Locations' => ['Exchange'],
|
|
'Rules' => [['Name' => 'Rule', 'Actions' => ['NotifyUser']]],
|
|
],
|
|
latestPayload: [
|
|
'DisplayName' => 'Spec423 Browser DLP Policy',
|
|
'Mode' => 'Enforce',
|
|
'Locations' => ['Exchange'],
|
|
'clientSecret' => 'spec423-browser-client-secret',
|
|
'Rules' => [[
|
|
'Name' => 'Rule',
|
|
'Actions' => ['BlockAccess'],
|
|
'DlpIncidentContent' => 'spec423-browser-dlp-incident-content',
|
|
]],
|
|
],
|
|
);
|
|
|
|
return [$user, $environment->refresh()];
|
|
}
|
|
|
|
function spec423BrowserEvidenceResource(
|
|
ManagedEnvironment $environment,
|
|
User $user,
|
|
ProviderConnection $connection,
|
|
string $canonicalType,
|
|
string $displayName,
|
|
array $previousPayload,
|
|
array $latestPayload,
|
|
): TenantConfigurationResource {
|
|
$resourceType = TenantConfigurationResourceType::query()
|
|
->where('canonical_type', $canonicalType)
|
|
->where('source_class', SourceClass::Tcm->value)
|
|
->firstOrFail();
|
|
$previousRun = spec423BrowserRun($environment, $user, $connection, $canonicalType, minutesAgo: 5);
|
|
$run = spec423BrowserRun($environment, $user, $connection, $canonicalType);
|
|
$resource = TenantConfigurationResource::factory()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
'resource_type_id' => (int) $resourceType->getKey(),
|
|
'canonical_type' => $canonicalType,
|
|
'canonical_resource_key' => $canonicalType.':provider_external_id:spec423-browser',
|
|
'canonical_key_kind' => CanonicalKeyKind::ProviderExternalId->value,
|
|
'source_resource_id' => 'spec423-browser',
|
|
'source_display_name' => $displayName,
|
|
'source_class' => SourceClass::Tcm->value,
|
|
'source_metadata' => [
|
|
'source_contract_key' => 'spec423.synthetic.'.$canonicalType,
|
|
'source_endpoint' => '/spec423/synthetic/'.$canonicalType,
|
|
'source_version' => 'v1.0',
|
|
'registry_source_class' => SourceClass::Tcm->value,
|
|
'registry_support_state' => 'out_of_scope',
|
|
],
|
|
'identity_strategy' => 'provider.external.id.v1',
|
|
'source_identity' => [
|
|
'primary_field' => 'id',
|
|
'primary_value' => 'spec423-browser',
|
|
],
|
|
'identity_diagnostics' => [
|
|
'reason_code' => 'provider_external_id',
|
|
],
|
|
'identity_evaluated_at' => now(),
|
|
'latest_evidence_state' => EvidenceState::ContentBacked->value,
|
|
'latest_identity_state' => IdentityState::Stable->value,
|
|
'latest_claim_state' => ClaimState::InternalOnly->value,
|
|
'latest_captured_at' => now(),
|
|
]);
|
|
|
|
TenantConfigurationResourceEvidence::factory()->create([
|
|
'resource_id' => (int) $resource->getKey(),
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
'resource_type_id' => (int) $resourceType->getKey(),
|
|
'operation_run_id' => (int) $previousRun->getKey(),
|
|
'source_contract_key' => 'spec423.synthetic.'.$canonicalType,
|
|
'source_endpoint' => '/spec423/synthetic/'.$canonicalType,
|
|
'source_version' => 'v1.0',
|
|
'source_schema_hash' => 'spec423-browser-previous-schema-hash',
|
|
'source_metadata' => [
|
|
'registry_source_class' => SourceClass::Tcm->value,
|
|
'registry_support_state' => 'out_of_scope',
|
|
],
|
|
'raw_payload' => ['id' => 'spec423-browser'],
|
|
'normalized_payload' => $previousPayload,
|
|
'payload_hash' => str_repeat('a', 64),
|
|
'permission_context' => ['scopes_granted' => []],
|
|
'evidence_state' => EvidenceState::ContentBacked->value,
|
|
'coverage_level' => CoverageLevel::Comparable->value,
|
|
'capture_outcome' => CaptureOutcome::Captured->value,
|
|
'captured_at' => now()->subMinutes(5),
|
|
]);
|
|
|
|
$evidence = TenantConfigurationResourceEvidence::factory()->create([
|
|
'resource_id' => (int) $resource->getKey(),
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
'resource_type_id' => (int) $resourceType->getKey(),
|
|
'operation_run_id' => (int) $run->getKey(),
|
|
'source_contract_key' => 'spec423.synthetic.'.$canonicalType,
|
|
'source_endpoint' => '/spec423/synthetic/'.$canonicalType,
|
|
'source_version' => 'v1.0',
|
|
'source_schema_hash' => 'spec423-browser-schema-hash',
|
|
'source_metadata' => [
|
|
'registry_source_class' => SourceClass::Tcm->value,
|
|
'registry_support_state' => 'out_of_scope',
|
|
],
|
|
'raw_payload' => ['id' => 'spec423-browser', 'secret' => 'spec423-browser-raw-secret'],
|
|
'normalized_payload' => $latestPayload,
|
|
'payload_hash' => str_repeat('b', 64),
|
|
'permission_context' => ['scopes_granted' => []],
|
|
'evidence_state' => EvidenceState::ContentBacked->value,
|
|
'coverage_level' => CoverageLevel::Renderable->value,
|
|
'capture_outcome' => CaptureOutcome::Captured->value,
|
|
'captured_at' => now(),
|
|
]);
|
|
|
|
$resource->forceFill([
|
|
'latest_evidence_id' => (int) $evidence->getKey(),
|
|
'latest_payload_hash' => (string) $evidence->payload_hash,
|
|
])->save();
|
|
|
|
return $resource->refresh();
|
|
}
|
|
|
|
function spec423BrowserRun(
|
|
ManagedEnvironment $environment,
|
|
User $user,
|
|
ProviderConnection $connection,
|
|
string $canonicalType,
|
|
int $minutesAgo = 0,
|
|
): OperationRun {
|
|
$timestamp = $minutesAgo > 0 ? now()->subMinutes($minutesAgo) : now();
|
|
|
|
return OperationRun::factory()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'initiator_name' => (string) $user->name,
|
|
'type' => OperationRunType::TenantConfigurationCapture->value,
|
|
'status' => OperationRunStatus::Completed->value,
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'summary_counts' => [
|
|
'total' => 1,
|
|
'processed' => 1,
|
|
'succeeded' => 1,
|
|
'skipped' => 0,
|
|
'failed' => 0,
|
|
'errors_recorded' => 0,
|
|
],
|
|
'context' => [
|
|
'requested_resource_types' => [$canonicalType],
|
|
'outcomes' => [
|
|
['canonical_type' => $canonicalType, 'outcome' => CaptureOutcome::Captured->value],
|
|
],
|
|
'target_scope' => [
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
],
|
|
],
|
|
'started_at' => $timestamp,
|
|
'completed_at' => $timestamp,
|
|
]);
|
|
}
|
|
|
|
function spec423AuthenticateCoverageV2Browser(
|
|
mixed $test,
|
|
User $user,
|
|
ManagedEnvironment $environment,
|
|
): void {
|
|
$workspaceId = (int) $environment->workspace_id;
|
|
|
|
$test->actingAs($user)->withSession([
|
|
WorkspaceContext::SESSION_KEY => $workspaceId,
|
|
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
|
|
(string) $workspaceId => (int) $environment->getKey(),
|
|
],
|
|
]);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, $workspaceId);
|
|
session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
|
|
(string) $workspaceId => (int) $environment->getKey(),
|
|
]);
|
|
}
|