Compare commits
24 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
7471afc79a | |
|
|
0126ae01b4 | |
|
|
563ad43f9c | |
|
|
614ac1b147 | |
|
|
250c213fc1 | |
|
|
d234f94315 | |
|
|
ec0d9f2d3e | |
|
|
ba461cb1f7 | |
|
|
d8cb73fb37 | |
|
|
e933a9b421 | |
|
|
efec1f593a | |
|
|
49b2696882 | |
|
|
0fe283d68f | |
|
|
d8f131d6da | |
|
|
add2aceb1d | |
|
|
a5262f19f5 | |
|
|
0aadb81027 | |
|
|
83284c2183 | |
|
|
cdd93fe9e8 | |
|
|
03ea022f08 | |
|
|
dd92aa26d0 | |
|
|
68172e715a | |
|
|
e6d164e1ef | |
|
|
a5331de771 |
|
|
@ -0,0 +1,93 @@
|
||||||
|
// Core Engine area map shared by core-guard.mjs and core-release.mjs (AGENTS.md, "Core Engine and engines").
|
||||||
|
// Paths are repository-relative and lowercase; the first matching prefix wins.
|
||||||
|
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
export const RELEASE_FILE = '.claude/core-release.json';
|
||||||
|
export const ALL_AREAS = 'alle';
|
||||||
|
export const GOVERNANCE = 'Governance';
|
||||||
|
export const WHOLE_REPOSITORY = 'Gesamtes Repository';
|
||||||
|
|
||||||
|
// Engine paths precede the Core areas they sit in.
|
||||||
|
export const ENGINE_PATHS = [
|
||||||
|
['Remediation Engine', ['src/remediation/', 'server/remediation/', 'tests/remediation/']],
|
||||||
|
['Context Engine', ['src/assessment-context/', 'src/features/context-layer/', 'tests/assessment-context/',
|
||||||
|
'tests/features/finding-context-layer.test.tsx', 'tests/features/pathfinder-context-layer.test.tsx',
|
||||||
|
'tests/features/context-cluster-facet.test.tsx', 'tests/context-browser-check.mjs']],
|
||||||
|
['Report Engine', ['src/reporting/', 'server/reporting/', 'tests/reporting/', 'tests/server/report-archive.test.ts',
|
||||||
|
'tests/postgres/report-read-only-runtime.test.ts']],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const CORE_AREAS = [
|
||||||
|
[GOVERNANCE, ['agents.md', 'claude.md', '.claude/hooks/', '.claude/settings.json']],
|
||||||
|
// Client-owned records (reviews, risk acceptances, review transfer on reimport). They must survive
|
||||||
|
// every reimport; their server persistence stays inside the Store repository.
|
||||||
|
['Client Records', ['src/domain/review.ts', 'src/domain/risk-acceptance.ts', 'src/domain/reimport-review-transfer.ts',
|
||||||
|
'src/persistence/review-store.ts', 'src/features/review-workspace/', 'src/features/dashboard/riskregister.tsx']],
|
||||||
|
['Disclosure', ['src/domain/result-db-disclosure.ts', 'src/exports/']],
|
||||||
|
['Ingest', ['src/importers/', 'src/recognition/', 'src/profiles/', 'src/analysis/', 'src/gpo/', 'server/gpo/',
|
||||||
|
'server/import-executor.ts', 'server/import-worker-client.ts', 'server/import-worker.ts',
|
||||||
|
'server/node-import-runtime-adapters.ts', 'server/artifacts/']],
|
||||||
|
['Normalization', ['src/domain/']],
|
||||||
|
['Evaluation', ['src/evaluation/', 'src/comparison/', 'src/scoring/']],
|
||||||
|
['Store', ['src/persistence/', 'server/postgres/', 'server/repository-factory.ts', 'docker-compose.postgres.yml']],
|
||||||
|
['Reference Catalogs', ['src/references/', 'server/reference-data/']],
|
||||||
|
['Shared Kernel', ['src/shared/', 'src/vite-env.d.ts']],
|
||||||
|
['Presentation', ['src/app/', 'src/features/', 'src/components/', 'src/styles/', 'src/hooks/', 'src/assets/',
|
||||||
|
'src/main.tsx', 'index.html']],
|
||||||
|
['Platform', ['server/', 'src/api/', 'src/platform/', 'src/legacy/', 'package.json', 'package-lock.json',
|
||||||
|
'vite.config.ts', 'vite.client.config.ts', 'vite.server.config.ts', 'vitest.config.ts', 'tsconfig.json',
|
||||||
|
'tsconfig.app.json', 'tsconfig.node.json', 'tsconfig.server.json', 'dockerfile', 'docker-compose.deploy.yml',
|
||||||
|
'deploy/', 'scripts/']],
|
||||||
|
['Core Tests', ['tests/', 'fixtures/']],
|
||||||
|
// Any other source path stays Core until it is registered as an engine above.
|
||||||
|
['Unclassified Source', ['src/']],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Spellings accepted after "Core-Freigabe:", mapped to the area names above.
|
||||||
|
const AREA_ALIASES = new Map([
|
||||||
|
['alle', ALL_AREAS], ['all', ALL_AREAS], ['core', ALL_AREAS],
|
||||||
|
['governance', GOVERNANCE],
|
||||||
|
['client records', 'Client Records'], ['kundendaten', 'Client Records'], ['reviews', 'Client Records'],
|
||||||
|
['disclosure', 'Disclosure'], ['result db', 'Disclosure'],
|
||||||
|
['ingest', 'Ingest'], ['import', 'Ingest'],
|
||||||
|
['normalization', 'Normalization'], ['normalisierung', 'Normalization'],
|
||||||
|
['evaluation', 'Evaluation'], ['bewertung', 'Evaluation'], ['scoring', 'Evaluation'],
|
||||||
|
['store', 'Store'], ['persistenz', 'Store'], ['datenbank', 'Store'],
|
||||||
|
['reference catalogs', 'Reference Catalogs'], ['referenzkataloge', 'Reference Catalogs'],
|
||||||
|
['shared kernel', 'Shared Kernel'],
|
||||||
|
['presentation', 'Presentation'], ['präsentation', 'Presentation'], ['praesentation', 'Presentation'], ['ui', 'Presentation'],
|
||||||
|
['platform', 'Platform'], ['plattform', 'Platform'],
|
||||||
|
['core tests', 'Core Tests'], ['tests', 'Core Tests'],
|
||||||
|
['unclassified source', 'Unclassified Source'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function resolveAreaName(raw) {
|
||||||
|
return AREA_ALIASES.get(raw.trim().toLowerCase().replace(/\s+/g, ' ')) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function relativeToProject(filePath, projectDir) {
|
||||||
|
const normalized = path.resolve(projectDir, filePath);
|
||||||
|
const relative = path.relative(projectDir, normalized).split(path.sep).join('/');
|
||||||
|
if (relative.startsWith('..') || path.isAbsolute(relative)) return null;
|
||||||
|
return relative.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchPrefix(relative, table) {
|
||||||
|
for (const [name, prefixes] of table) {
|
||||||
|
if (prefixes.some((prefix) => relative === prefix || relative.startsWith(prefix))) return name;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function classifyPath(relative) {
|
||||||
|
if (relative === null) return null;
|
||||||
|
if (matchPrefix(relative, ENGINE_PATHS)) return null;
|
||||||
|
return matchPrefix(relative, CORE_AREAS);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isReleased(area, releasedAreas) {
|
||||||
|
if (releasedAreas.includes(area)) return true;
|
||||||
|
// "alle" never covers Governance: the guard and these rules must always be named.
|
||||||
|
return area !== GOVERNANCE && releasedAreas.includes(ALL_AREAS);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// Core Engine change gate (AGENTS.md, "Core Engine and engines").
|
||||||
|
// PreToolUse hook: Edit/Write/NotebookEdit or a writing shell command on a Core path is blocked
|
||||||
|
// unless the user released that Core area in the current message ("Core-Freigabe: <area>").
|
||||||
|
// Engine paths (Context Engine, Report Engine) and documentation pass silently.
|
||||||
|
// Errors fail closed: the change is blocked with an explanation.
|
||||||
|
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import {
|
||||||
|
RELEASE_FILE, ALL_AREAS, WHOLE_REPOSITORY, classifyPath, isReleased, relativeToProject,
|
||||||
|
} from './core-areas.mjs';
|
||||||
|
|
||||||
|
// Repository-wide operations that rewrite tracked files regardless of path.
|
||||||
|
const TREE_WIDE_WRITES = [
|
||||||
|
/\bgit\s+(reset\s+--hard|clean\b|stash\b(?!\s+list)|checkout\s+(--\s+)?\.(\s|$)|restore\s+(--\S+\s+)*\.(\s|$)|apply\b|am\b|merge\b|rebase\b|pull\b|cherry-pick\b|revert\b)/i,
|
||||||
|
];
|
||||||
|
|
||||||
|
// A command segment that writes; only the paths inside such a segment are write candidates.
|
||||||
|
// Running, reading or testing a Core file (node tests/x.mjs, cat, grep, vitest) is never a write.
|
||||||
|
const SEGMENT_WRITE = new RegExp([
|
||||||
|
String.raw`\bsed\s+(-\S*\s+)*-i`, String.raw`\bperl\s+-\S*i`, String.raw`\btee\b`,
|
||||||
|
String.raw`(^|[\s(])(mv|cp|rm|rmdir|touch|truncate|ln|install|patch|unzip|tar)(\s|$)`, String.raw`\bgit\s+(checkout|restore|rm|mv)\b`,
|
||||||
|
String.raw`\b(Set-Content|Add-Content|Out-File|Clear-Content|Remove-Item|Move-Item|Copy-Item|New-Item|Rename-Item)\b`,
|
||||||
|
String.raw`\b(writeFile|writeFileSync|appendFile|appendFileSync|rmSync|unlinkSync|renameSync|copyFileSync)\b`,
|
||||||
|
String.raw`--write\b`,
|
||||||
|
].join('|'), 'i');
|
||||||
|
|
||||||
|
// Package-manager writes change package.json/package-lock.json without naming them.
|
||||||
|
const PACKAGE_WRITE = /\bnpm\s+(install|i|ci|uninstall|update|version|pkg\s+set)\b/i;
|
||||||
|
|
||||||
|
// Splits a command into segments at ; && || | and newlines outside quotes, and collects
|
||||||
|
// the targets of output redirections outside quotes (never /dev/null, $null or &N).
|
||||||
|
function scanShell(command) {
|
||||||
|
const segments = [];
|
||||||
|
const redirects = [];
|
||||||
|
let current = '';
|
||||||
|
let quote = null;
|
||||||
|
for (let index = 0; index < command.length; index += 1) {
|
||||||
|
const char = command[index];
|
||||||
|
if (quote) {
|
||||||
|
if (char === quote) quote = null;
|
||||||
|
current += char;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (char === '"' || char === "'") {
|
||||||
|
quote = char;
|
||||||
|
current += char;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (char === ';' || char === '\n' || char === '|' || (char === '&' && command[index + 1] === '&')) {
|
||||||
|
segments.push(current);
|
||||||
|
current = '';
|
||||||
|
if (command[index + 1] === char) index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (char === '>') {
|
||||||
|
let cursor = index + 1;
|
||||||
|
if (command[cursor] === '>') cursor += 1;
|
||||||
|
while (command[cursor] === ' ') cursor += 1;
|
||||||
|
const target = command.slice(cursor).match(/^("[^"]*"|'[^']*'|[^\s;&|<>()]+)/)?.[0] ?? '';
|
||||||
|
const bare = target.replace(/^["']|["']$/g, '');
|
||||||
|
if (bare && !/^(&\d*|\/dev\/null|\$null|nul)$/i.test(bare)) redirects.push(bare);
|
||||||
|
current += command.slice(index, cursor + target.length);
|
||||||
|
index = cursor + target.length - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current += char;
|
||||||
|
}
|
||||||
|
segments.push(current);
|
||||||
|
return { segments, redirects };
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstCoreToken(text) {
|
||||||
|
const tokens = text.match(/[A-Za-z0-9_.\-/:]+/g) ?? [];
|
||||||
|
for (const token of tokens) {
|
||||||
|
const candidate = token.replace(/^\.\//, '');
|
||||||
|
if (!/[/.]/.test(candidate)) continue;
|
||||||
|
const area = classifyPath(candidate.includes(':') ? null : candidate);
|
||||||
|
if (area) return { area, target: token };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function classifyShell(rawCommand, projectDir) {
|
||||||
|
// Absolute project paths become repository-relative so they classify like relative ones.
|
||||||
|
const projectPrefix = projectDir.replace(/\\/g, '/').replace(/\/?$/, '/').toLowerCase();
|
||||||
|
const command = rawCommand.replace(/\\/g, '/').toLowerCase().split(projectPrefix).join('');
|
||||||
|
if (TREE_WIDE_WRITES.some((pattern) => pattern.test(command))) {
|
||||||
|
return { area: WHOLE_REPOSITORY, target: 'repositoryweite Git-Operation' };
|
||||||
|
}
|
||||||
|
const { segments, redirects } = scanShell(command);
|
||||||
|
for (const target of redirects) {
|
||||||
|
const hit = firstCoreToken(target);
|
||||||
|
if (hit) return hit;
|
||||||
|
}
|
||||||
|
for (const segment of segments) {
|
||||||
|
if (PACKAGE_WRITE.test(segment)) return { area: classifyPath('package-lock.json'), target: 'package.json/package-lock.json' };
|
||||||
|
if (!SEGMENT_WRITE.test(segment)) continue;
|
||||||
|
const hit = firstCoreToken(segment);
|
||||||
|
if (hit) return hit;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readRelease(projectDir, sessionId) {
|
||||||
|
try {
|
||||||
|
const release = JSON.parse(fs.readFileSync(path.join(projectDir, RELEASE_FILE), 'utf8'));
|
||||||
|
if (release.sessionId && sessionId && release.sessionId !== sessionId) return [];
|
||||||
|
return Array.isArray(release.areas) ? release.areas : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deny(reasonForClaude, noticeForUser) {
|
||||||
|
process.stdout.write(JSON.stringify({
|
||||||
|
systemMessage: noticeForUser,
|
||||||
|
hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reasonForClaude },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function main(raw) {
|
||||||
|
const input = JSON.parse(raw);
|
||||||
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd();
|
||||||
|
const toolInput = input.tool_input ?? {};
|
||||||
|
const isShell = input.tool_name === 'Bash' || input.tool_name === 'PowerShell';
|
||||||
|
const command = isShell ? String(toolInput.command ?? '') : '';
|
||||||
|
const filePath = isShell ? null : toolInput.file_path ?? toolInput.notebook_path;
|
||||||
|
const relative = filePath ? relativeToProject(String(filePath), projectDir) : null;
|
||||||
|
|
||||||
|
// The release file arises only from the user's message; Claude never touches it.
|
||||||
|
if (relative === RELEASE_FILE || command.toLowerCase().includes('core-release.json')) {
|
||||||
|
deny(
|
||||||
|
'The Core release file is written only by the user-message hook. Do not create, edit or delete it; ask the user to write "Core-Freigabe: <area>" instead.',
|
||||||
|
'⚠ CORE-FREIGABEDATEI GESCHÜTZT – Claude darf die Freigabe nicht selbst setzen. Eine Freigabe entsteht nur durch deine Nachricht.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hit = isShell ? classifyShell(command, projectDir) : (() => {
|
||||||
|
const area = classifyPath(relative);
|
||||||
|
return area ? { area, target: relative } : null;
|
||||||
|
})();
|
||||||
|
if (!hit) return;
|
||||||
|
|
||||||
|
const released = readRelease(projectDir, input.session_id);
|
||||||
|
const allowed = hit.area === WHOLE_REPOSITORY ? released.includes(ALL_AREAS) : isReleased(hit.area, released);
|
||||||
|
const phrase = hit.area === WHOLE_REPOSITORY ? 'Core-Freigabe: alle' : `Core-Freigabe: ${hit.area}`;
|
||||||
|
if (allowed) {
|
||||||
|
process.stdout.write(JSON.stringify({ systemMessage: `🔓 Core-Änderung mit Freigabe – Bereich ${hit.area}: ${hit.target}` }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deny(
|
||||||
|
`Blocked: this changes the Core Engine (area ${hit.area}: ${hit.target}) without Core consent. Stop this part, tell the user which Core area is affected and why, and ask them to reply with "${phrase}" if they want it. Do not work around this block through another tool, path or engine.`,
|
||||||
|
`⚠ CORE-ÄNDERUNG GESPERRT – Bereich ${hit.area}: ${hit.target}. Freigabe nur durch deine Nachricht mit „${phrase}“.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw = '';
|
||||||
|
process.stdin.setEncoding('utf8');
|
||||||
|
process.stdin.on('data', (chunk) => { raw += chunk; });
|
||||||
|
process.stdin.on('end', () => {
|
||||||
|
try {
|
||||||
|
main(raw);
|
||||||
|
} catch (error) {
|
||||||
|
const detail = error instanceof Error ? error.message : 'unknown error';
|
||||||
|
deny(
|
||||||
|
`Core guard failed (${detail}); the change is blocked. Tell the user the guard could not check this change.`,
|
||||||
|
`⚠ CORE-WÄCHTER FEHLER – Änderung vorsorglich gesperrt (${detail}).`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// UserPromptSubmit hook: records an explicit "Core-Freigabe: <area>[, <area>]" from the user's message.
|
||||||
|
// The release covers only this message; every new message replaces or removes it.
|
||||||
|
// Only the user's own prompt is read, never tool output or file content.
|
||||||
|
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { RELEASE_FILE, ALL_AREAS, resolveAreaName } from './core-areas.mjs';
|
||||||
|
|
||||||
|
const RELEASE_PHRASE = /core[- ]?freigabe\s*:?\s*/gi;
|
||||||
|
const SEPARATOR = /^(\s*(,|;|\/|&|\+|\bund\b|\band\b)\s*|\s+)/i;
|
||||||
|
|
||||||
|
// Reads area names from the start of the text after the phrase; trailing prose ends the list.
|
||||||
|
// Any character other than a letter or space (dash, colon, digit, punctuation, newline) ends a name,
|
||||||
|
// so "Core-Freigabe: Shared Kernel – füge ..." releases Shared Kernel.
|
||||||
|
function readAreas(tail, unknown) {
|
||||||
|
const areas = [];
|
||||||
|
let rest = tail;
|
||||||
|
while (rest.length > 0) {
|
||||||
|
const word = rest.match(/^[\p{L}][\p{L} ]*?(?=$|[^\p{L} ]|\s+(und|and)\b)/u)?.[0] ?? '';
|
||||||
|
const candidates = word.split(' ').map((_, index, parts) => parts.slice(0, parts.length - index).join(' '));
|
||||||
|
const match = candidates.find((candidate) => resolveAreaName(candidate));
|
||||||
|
if (!match) {
|
||||||
|
if (areas.length === 0 && word) unknown.push(word.trim());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
areas.push(resolveAreaName(match));
|
||||||
|
rest = rest.slice(match.length);
|
||||||
|
const separator = rest.match(SEPARATOR);
|
||||||
|
if (!separator) break;
|
||||||
|
rest = rest.slice(separator[0].length);
|
||||||
|
}
|
||||||
|
return areas;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRelease(prompt) {
|
||||||
|
const areas = new Set();
|
||||||
|
const unknown = [];
|
||||||
|
for (const match of prompt.matchAll(RELEASE_PHRASE)) {
|
||||||
|
const tail = prompt.slice(match.index + match[0].length);
|
||||||
|
for (const area of readAreas(tail, unknown)) areas.add(area);
|
||||||
|
}
|
||||||
|
return { areas: [...areas], unknown };
|
||||||
|
}
|
||||||
|
|
||||||
|
function label(area) {
|
||||||
|
return area === ALL_AREAS ? 'alle Core-Bereiche ausser Governance' : area;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main(raw) {
|
||||||
|
const input = JSON.parse(raw);
|
||||||
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd();
|
||||||
|
const releaseFile = path.join(projectDir, RELEASE_FILE);
|
||||||
|
const { areas, unknown } = parseRelease(String(input.prompt ?? ''));
|
||||||
|
fs.rmSync(releaseFile, { force: true });
|
||||||
|
const output = {};
|
||||||
|
if (areas.length > 0) {
|
||||||
|
fs.writeFileSync(releaseFile, JSON.stringify({ sessionId: input.session_id ?? null, areas, createdAt: new Date().toISOString() }));
|
||||||
|
const text = areas.map(label).join(', ');
|
||||||
|
output.systemMessage = `✅ CORE-FREIGABE für diese Nachricht: ${text}`;
|
||||||
|
output.hookSpecificOutput = {
|
||||||
|
hookEventName: 'UserPromptSubmit',
|
||||||
|
additionalContext: `The user released these Core areas for this message only: ${text}. Other Core areas stay blocked.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (unknown.length > 0) {
|
||||||
|
output.systemMessage = [output.systemMessage, `⚠ Core-Freigabe nicht erkannt: "${unknown.join('", "')}". Gueltige Bereiche: Ingest, Normalization, Evaluation, Store, Disclosure, Reference Catalogs, Shared Kernel, Presentation, Platform, Core Tests, Governance, alle.`]
|
||||||
|
.filter(Boolean).join('\n');
|
||||||
|
}
|
||||||
|
if (Object.keys(output).length > 0) process.stdout.write(JSON.stringify(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw = '';
|
||||||
|
process.stdin.setEncoding('utf8');
|
||||||
|
process.stdin.on('data', (chunk) => { raw += chunk; });
|
||||||
|
process.stdin.on('end', () => {
|
||||||
|
try {
|
||||||
|
main(raw);
|
||||||
|
} catch (error) {
|
||||||
|
// Fail closed: without a readable message no release remains active.
|
||||||
|
try {
|
||||||
|
fs.rmSync(path.join(process.env.CLAUDE_PROJECT_DIR || process.cwd(), RELEASE_FILE), { force: true });
|
||||||
|
} catch {}
|
||||||
|
process.stdout.write(JSON.stringify({
|
||||||
|
systemMessage: `⚠ Core-Freigabe konnte nicht gelesen werden (${error instanceof Error ? error.message : 'unbekannter Fehler'}). Der Core bleibt gesperrt.`,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"UserPromptSubmit": [
|
||||||
|
{
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/core-release.mjs\"",
|
||||||
|
"timeout": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"PreToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write|NotebookEdit|Bash|PowerShell",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/core-guard.mjs\"",
|
||||||
|
"timeout": 10,
|
||||||
|
"statusMessage": "Checking Core Engine boundary"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,23 +20,14 @@ cp -r assessor-ui ~/.claude/skills/
|
||||||
|
|
||||||
## Einbindung im Projekt
|
## Einbindung im Projekt
|
||||||
|
|
||||||
Die Token- und Komponentendateien sind nicht nur Dokumentation, sondern die Implementierungsbasis:
|
Nutzerkorrektur 2026-09-18: Data Hub, Work und Pathfinder sind die verbindlichen
|
||||||
|
Ableton-inspirierten Referenzen, auch fuer Client Info, Mockups und Prototypen.
|
||||||
```bash
|
Zuerst [AGENTS.md](../../../AGENTS.md#binding-assessor-design-language) und den
|
||||||
cp .claude/skills/assessor-ui/assets/tokens.css src/styles/tokens.css
|
Vorrangabschnitt in `SKILL.md` lesen. Die aktuellen Projekt-Tokens liegen in
|
||||||
cp .claude/skills/assessor-ui/assets/components.css src/styles/components.css
|
`src/styles/ui-tokens.css`; bestehende Komponenten und Styles werden weiterverwendet.
|
||||||
```
|
Die Dateien unter `assets/` und aeltere Referenzskizzen sind historische Beispiele.
|
||||||
|
Sie duerfen nicht ueber die aktuellen Styles kopiert oder erneut in den Build
|
||||||
Einstiegspunkt des Stylesheets:
|
eingebunden werden. Die fruehere Kopieranleitung ist hiermit ausdruecklich aufgehoben.
|
||||||
|
|
||||||
```css
|
|
||||||
@layer tokens, base, shell, components, utilities;
|
|
||||||
|
|
||||||
@import "./styles/tokens.css";
|
|
||||||
@import "./styles/base.css";
|
|
||||||
@import "./styles/shell.css";
|
|
||||||
@import "./styles/components.css";
|
|
||||||
```
|
|
||||||
|
|
||||||
Theme und Dichte am Wurzelelement:
|
Theme und Dichte am Wurzelelement:
|
||||||
|
|
||||||
|
|
@ -63,7 +54,7 @@ Erlaubte Werte: `data-theme` = `dark` oder `light`, `data-density` = `compact`,
|
||||||
Der Skill triggert automatisch bei UI-Aufgaben an ASSESSOR. Erzwingen laesst er sich mit:
|
Der Skill triggert automatisch bei UI-Aufgaben an ASSESSOR. Erzwingen laesst er sich mit:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Nutze den Skill assessor-ui. Baue die Datahub-Uebersicht auf Archetyp D um.
|
Nutze den Skill assessor-ui. Gestalte Client Info mit der bestehenden Designsprache von Data Hub, Work und Pathfinder.
|
||||||
```
|
```
|
||||||
|
|
||||||
Vor dem Abschluss einer Aenderung:
|
Vor dem Abschluss einer Aenderung:
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,36 @@ description: >-
|
||||||
|
|
||||||
Verbindliches Designsystem fuer ASSESSOR. Keine Empfehlung, keine Inspiration: Vorschrift.
|
Verbindliches Designsystem fuer ASSESSOR. Keine Empfehlung, keine Inspiration: Vorschrift.
|
||||||
|
|
||||||
|
## Verbindliche Referenzen und Vorrang, Nutzerkorrektur 2026-09-18
|
||||||
|
|
||||||
|
Die aktuell vom Nutzer freigegebenen Ansichten **Data Hub, Work und Pathfinder**
|
||||||
|
sind die konkrete Ableton-inspirierte ASSESSOR-Referenz. Sie gelten ausdruecklich
|
||||||
|
auch fuer **Client Info**, einschliesslich Source Overview, Environment Detail und
|
||||||
|
Accepted Risks, sowie fuer Bildentwuerfe und Prototypen. Die drei am 2026-09-18
|
||||||
|
verworfenen generischen Client-Info-Mockups sind keine freigegebenen Vorlagen.
|
||||||
|
|
||||||
|
Massgeblich sind [AGENTS.md](../../../AGENTS.md#binding-assessor-design-language),
|
||||||
|
die aktuellen Komponenten und [ui-tokens.css](../../../src/styles/ui-tokens.css).
|
||||||
|
Die fruehere Client-Info-Designausnahme ist aufgehoben. Fachliche Inhalte,
|
||||||
|
Scoresemantik und Drilldowns bleiben erhalten; ihre Darstellung folgt denselben
|
||||||
|
Flächen-, Tile-, Button-, Typografie-, Abstands- und Zustandsregeln wie die Referenzen.
|
||||||
|
|
||||||
|
Diese datierte Festlegung hat Vorrang vor allen aelteren Beispielen und Tabellen
|
||||||
|
in diesem Skill, seinen `references/` und `assets/`. Insbesondere sind eine violette
|
||||||
|
Auswahl/Primaeraktion, kleine Fliesstextrollen, starre gekuerzte Tiles, dekorative
|
||||||
|
Donutschatten/Verlaeufe, ein erzwungener Tabellenumbau von Data Hub und eine neue
|
||||||
|
Shell keine aktuellen Vorgaben. Die aktuelle gelb/bernsteinfarbene Markierung und
|
||||||
|
Aktionsfarbe, der blaue Tastaturfokus, Inter, die 16px-Lese- und 14px-Datenrolle,
|
||||||
|
20px Panel-Inset sowie flache Tiles und volle 360-Grad-Donuts sind verbindlich.
|
||||||
|
Bestehende runde Hauptpanels behalten ihre gemeinsame Geometrie; die Tiles darin
|
||||||
|
bleiben eckig. Historische Skill-Assets niemals ueber die aktuellen Styles kopieren.
|
||||||
|
|
||||||
|
Vor jeder Abnahme ist der Entwurf bei normalem App-Zoom direkt mit Data Hub, Work
|
||||||
|
und Pathfinder abzugleichen: Er muss auch ohne Logo wie dieselbe Anwendung wirken.
|
||||||
|
Mockups verwenden nur eindeutig synthetische Beispieldaten und die echten
|
||||||
|
Referenzansichten als Stilvorlage; sie erfinden weder Kennzahlen noch eine neue
|
||||||
|
Navigation. Verankerung, Vorschlag und tatsaechliche UI-Umsetzung getrennt benennen.
|
||||||
|
|
||||||
## Zweck
|
## Zweck
|
||||||
|
|
||||||
ASSESSOR ist ein Analysewerkzeug fuer Active-Directory-Sicherheitsbefunde aus PingCastle,
|
ASSESSOR ist ein Analysewerkzeug fuer Active-Directory-Sicherheitsbefunde aus PingCastle,
|
||||||
|
|
@ -28,17 +58,17 @@ Loesung erfindet. Genau das hat den heutigen Zustand erzeugt: `Work` sieht ander
|
||||||
|
|
||||||
## Ablauf bei jeder UI-Aufgabe
|
## Ablauf bei jeder UI-Aufgabe
|
||||||
|
|
||||||
1. `assets/tokens.css` und `assets/components.css` lesen. Beide sind die Implementierungsbasis.
|
1. Die aktuellen UI-Vereinbarungen in `AGENTS.md`, `src/styles/ui-tokens.css` und die betroffenen bestehenden Komponenten lesen. Die Skill-Assets sind historische Referenzen, keine zu kopierende Implementierungsbasis.
|
||||||
2. Betroffenen Seitenarchetyp bestimmen (siehe `references/page-archetypes.md`).
|
2. Die freigegebene Referenzansicht und das bestehende Layout bestimmen. `references/page-archetypes.md` nur ergaenzend unter dem Vorrang der Nutzerkorrektur vom 2026-09-18 verwenden.
|
||||||
3. Pruefen, ob eine bestehende Komponente ausreicht. Neue Komponente nur, wenn keine Variante passt.
|
3. Pruefen, ob eine bestehende Komponente ausreicht. Neue Komponente nur, wenn keine Variante passt.
|
||||||
4. Implementieren, ausschliesslich mit Tokens und bestehenden Klassen.
|
4. Implementieren, ausschliesslich mit Tokens und bestehenden Klassen.
|
||||||
5. Gegen `references/review-checklist.md` pruefen, in Dark UND Light.
|
5. Direkt gegen Data Hub, Work und Pathfinder sowie die aktuellen AGENTS-Regeln pruefen; fuer Implementierungen in Dark UND Light. `references/review-checklist.md` unter ihrem datierten Vorranghinweis verwenden.
|
||||||
6. Abweichungen benennen, nicht stillschweigend loesen.
|
6. Abweichungen benennen, nicht stillschweigend loesen.
|
||||||
|
|
||||||
## Referenzdateien
|
## Referenzdateien
|
||||||
|
|
||||||
- `assets/tokens.css` — alle Farben, Masse, Radien, Schatten, Typografie. Einzige Quelle.
|
- `src/styles/ui-tokens.css` (Projektwurzel) — aktuelle Farben, Masse und Typografierollen.
|
||||||
- `assets/components.css` — Referenzimplementierung aller Kernkomponenten.
|
- `assets/tokens.css` und `assets/components.css` — historische Beispiele; bei Abweichungen gelten ausschliesslich die aktuellen Projektvertraege und Implementierungen.
|
||||||
- `references/state-model.md` — Zustandsdimensionen und ihre Darstellung. Vor jeder Badge-/Statusarbeit lesen.
|
- `references/state-model.md` — Zustandsdimensionen und ihre Darstellung. Vor jeder Badge-/Statusarbeit lesen.
|
||||||
- `references/components.md` — Komponentenvertraege mit festen Massen und Aufbau.
|
- `references/components.md` — Komponentenvertraege mit festen Massen und Aufbau.
|
||||||
- `references/page-archetypes.md` — Shell und die vier erlaubten Seitenlayouts.
|
- `references/page-archetypes.md` — Shell und die vier erlaubten Seitenlayouts.
|
||||||
|
|
@ -93,7 +123,9 @@ Diese Regeln gelten ohne Ausnahme. Ein Verstoss ist ein Fehler, keine Stilfrage.
|
||||||
- Buttonform ist verbindlich und ueberall dieselbe, unabhaengig von der Farbe: `--btn-radius`
|
- Buttonform ist verbindlich und ueberall dieselbe, unabhaengig von der Farbe: `--btn-radius`
|
||||||
(abgerundete Ecken), `--btn-h` als Hoehe, `--btn-pad-x` als waagerechtes Polster, senkrecht
|
(abgerundete Ecken), `--btn-h` als Hoehe, `--btn-pad-x` als waagerechtes Polster, senkrecht
|
||||||
kein eigenes Polster. Keine Ansicht setzt eine eigene Buttonhoehe, einen eigenen Radius oder
|
kein eigenes Polster. Keine Ansicht setzt eine eigene Buttonhoehe, einen eigenen Radius oder
|
||||||
ein eigenes Polster. Ausgenommen sind nur zusammengesetzte Reiter (View-Tabs, Sidebar).
|
ein eigenes Polster. Auch zusammengesetzte Reiter, Sidebar-Aktionen, Icon-, Disclosure- und
|
||||||
|
Segment-Schaltflaechen verwenden den gemeinsamen Radius. Echte Kreis-/Donut-Interaktionen und
|
||||||
|
quadratische Inhaltskacheln behalten ihre semantische Geometrie.
|
||||||
- Traegt eine Schaltflaeche Farbe, ist es `--button-accent` (Standardgelb `#E9A63C`, in beiden
|
- Traegt eine Schaltflaeche Farbe, ist es `--button-accent` (Standardgelb `#E9A63C`, in beiden
|
||||||
Skins gleich) mit `--button-accent-text`. Nie ein anderes Gelb, nie der Ton des Command Bands.
|
Skins gleich) mit `--button-accent-text`. Nie ein anderes Gelb, nie der Ton des Command Bands.
|
||||||
- Rahmen: 1 px. 2 px ausschliesslich fuer Auswahl- und Fokusrahmen.
|
- Rahmen: 1 px. 2 px ausschliesslich fuer Auswahl- und Fokusrahmen.
|
||||||
|
|
@ -103,8 +135,16 @@ Diese Regeln gelten ohne Ausnahme. Ein Verstoss ist ein Fehler, keine Stilfrage.
|
||||||
- Schatten: nur `--shadow-overlay` und `--shadow-popover`, nur an echten Overlays.
|
- Schatten: nur `--shadow-overlay` und `--shadow-popover`, nur an echten Overlays.
|
||||||
Kein Schatten an Panels, Tiles, Zeilen, Buttons, Tabellen.
|
Kein Schatten an Panels, Tiles, Zeilen, Buttons, Tabellen.
|
||||||
- Kein Glassmorphism, kein Neumorphismus, kein Farbverlauf als Flaeche, kein Hover-Lift, kein Scale-Effekt.
|
- Kein Glassmorphism, kein Neumorphismus, kein Farbverlauf als Flaeche, kein Hover-Lift, kein Scale-Effekt.
|
||||||
- Der Akzent (Violett) bedeutet ausschliesslich: Primaeraktion, aktive Ansicht, Auswahl.
|
- Violett ist als UI-Rolle vollstaendig entfernt (Nutzerentscheidung 2026-09-20, AGENTS.md `UI segments and tiles`).
|
||||||
Nie Severity, nie Erfolg, nie Markierung, nie Dekoration.
|
Aktive Ansicht, Primaeraktion und Auswahl sind `--marking-color` (Gelb/Bernstein), Tastaturfokus ist
|
||||||
|
`--focus-ring` (Blau). Kein Token, keine Komponente und kein Graph-/Chart-Ton darf violett aufloesen
|
||||||
|
oder `--accent*` fuer Auswahl oder Fokus lesen. Eine Tool-/Kategoriefarbe (z. B. ein Chart-Ton) braucht
|
||||||
|
bei Bedarf einen eigenen, dediziert nicht-violetten Tokenwert statt der fruesheren `--accent`-Wiederverwendung.
|
||||||
|
- Jeder Farbtoken hat genau eine semantische Rolle (Markierung, Fokus, Severity, Kategorie/Chart-Ton, ...).
|
||||||
|
Ein Token wird nie in einer Komponente fuer seine dokumentierte Rolle und in einer anderen Komponente
|
||||||
|
fuer eine zweite Rolle gelesen — das ist exakt der Fehler, der die violette Auswahl/Fokus-Verwechslung
|
||||||
|
von 2026-09-20 verursacht hat. Vor dem Wiederverwenden eines bestehenden Tokens dessen dokumentierte
|
||||||
|
Rolle in `ui-tokens.css` und in diesem Skill pruefen.
|
||||||
|
|
||||||
### Zustaende
|
### Zustaende
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
# Shell und Seitenarchetypen
|
# Shell und Seitenarchetypen
|
||||||
|
|
||||||
|
> Nutzerkorrektur 2026-09-18: Dieses Dokument beschreibt historische Layoutbeispiele.
|
||||||
|
> Massgeblich sind die heute freigegebenen Data-Hub-, Work- und Pathfinder-Ansichten
|
||||||
|
> und [AGENTS.md](../../../../AGENTS.md#binding-assessor-design-language), auch fuer
|
||||||
|
> Client Info. Weder die nachfolgende 18px-Topbar-Skizze noch Archetyp D autorisieren
|
||||||
|
> einen Umbau der aktuellen Shell oder den Ersatz der freigegebenen Data-Hub-Tiles
|
||||||
|
> durch eine Tabelle. Client Info verwendet dieselbe vorhandene Shell und dieselben
|
||||||
|
> Komponentenregeln; seine fachliche Anordnung darf innerhalb dieser Sprache variieren.
|
||||||
|
|
||||||
Es gibt eine Shell und vier Archetypen. Jede Seite von ASSESSOR ist genau einem Archetyp zugeordnet.
|
Es gibt eine Shell und vier Archetypen. Jede Seite von ASSESSOR ist genau einem Archetyp zugeordnet.
|
||||||
Eine fuenfte Seitenform wird nicht erfunden.
|
Eine fuenfte Seitenform wird nicht erfunden.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,31 @@
|
||||||
# Abnahme und Korrekturliste
|
# Abnahme und Korrekturliste
|
||||||
|
|
||||||
|
## Verbindlicher Abgleich seit 2026-09-18
|
||||||
|
|
||||||
|
Die Nutzerreferenzen Data Hub, Work und Pathfinder sowie
|
||||||
|
[AGENTS.md](../../../../AGENTS.md#binding-assessor-design-language) haben Vorrang
|
||||||
|
vor der historischen Checkliste unten. Das gilt auch fuer Client Info und Mockups.
|
||||||
|
Insbesondere sind die frueheren Donutschatten-/Radiusausnahmen, der Tabellenumbau
|
||||||
|
von Data Hub, gekuerzte Tile-Inhalte, die alte Titelskala und violette Auswahl keine
|
||||||
|
gueltigen Abnahmekriterien. Bewusst freie Restflaeche in einer unvollstaendigen
|
||||||
|
Tile-Reihe ist zulaessig; Tiles werden nicht zum Fuellen der Breite gestreckt.
|
||||||
|
|
||||||
|
- Ist die Ansicht ohne Logo als dieselbe Anwendung wie alle drei Referenzen erkennbar?
|
||||||
|
- Entsprechen neutrale Flaechen, eckige Tiles, transparente Segmente, Linien, Controls,
|
||||||
|
Inter-Typografie und 20px Panel-Inset den gemeinsamen aktuellen Tokens?
|
||||||
|
- Bleiben gelb/bernsteinfarbene Auswahl/Aktionen, blauer Tastaturfokus und fachliche
|
||||||
|
Statusfarben unterscheidbar? Keine neue Palette oder neue Shell fuer Client Info.
|
||||||
|
- Verwenden Donuts volle 360 Grad, flache Bogenenden und korrekt zentrierte Zahlen?
|
||||||
|
- Sind Lesetext und strukturierte Daten bei normalem Zoom in ihren 16px-/14px-Rollen
|
||||||
|
lesbar, ohne Clamping, Ellipsen, Ueberlagerung oder eine Wand aus Mikroschrift?
|
||||||
|
- Behalten Kennzahlen, Source Overview und Drilldowns ihre dokumentierte Bedeutung?
|
||||||
|
Mockups kennzeichnen synthetische Daten; verworfene Bilder sind keine Stilreferenz.
|
||||||
|
- Bei Umsetzung: beide Themes, responsive Umbrueche und betroffene Interaktionen
|
||||||
|
pruefen. Reine Dokumentationsverankerung ist keine visuelle Laufzeitabnahme.
|
||||||
|
|
||||||
|
Die folgenden Abschnitte bleiben als historische Referenz erhalten; widersprechende
|
||||||
|
Punkte werden durch die obigen Kriterien und AGENTS.md ausdruecklich ersetzt.
|
||||||
|
|
||||||
## Inhaltsverzeichnis
|
## Inhaltsverzeichnis
|
||||||
|
|
||||||
1. Automatisierbare Pruefungen
|
1. Automatisierbare Pruefungen
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,9 @@ vite.config.d.ts
|
||||||
.assessor-data/
|
.assessor-data/
|
||||||
.claude/worktrees/
|
.claude/worktrees/
|
||||||
.claude/scheduled_tasks.lock
|
.claude/scheduled_tasks.lock
|
||||||
|
.claude/core-release.json
|
||||||
.codex-patches/
|
.codex-patches/
|
||||||
.tmp/
|
.tmp/
|
||||||
|
|
||||||
|
# Lokale Download-Pakete von Reportentwuerfen (Inhalt liegt als HTML + reports/assets/ vor)
|
||||||
|
reports/*.zip
|
||||||
|
|
|
||||||
89
SECURITY.md
89
SECURITY.md
|
|
@ -1,7 +1,36 @@
|
||||||
# Sicherheitsmodell
|
# Sicherheitsmodell
|
||||||
|
|
||||||
|
## Accepted Risk: dokumentierte externe Entscheidung
|
||||||
|
|
||||||
|
Ergaenzung 2026-09-10: Der lokale Accepted-Risk-Dialog speichert begruendete,
|
||||||
|
befristete Entscheidungen mit Verantwortlichen, separater Freigabedokumentation,
|
||||||
|
Massnahmen, Wiedervorlage und vollstaendiger Versionshistorie im bestehenden
|
||||||
|
Review-JSONB. Der eigene Same-Origin-Endpunkt prueft Projektzugehoerigkeit,
|
||||||
|
Pflichtangaben und erwartete Revision; die Repository-Transaktion haengt eine
|
||||||
|
serverdatierte Version an. Allgemeine Reviewaenderungen duerfen die vorhandene
|
||||||
|
Historie nicht ersetzen. Originalbefunde und technische Bewertungen bleiben erhalten.
|
||||||
|
|
||||||
|
Namen und Freigabereferenzen sind Angaben zu einer externen Entscheidung, keine
|
||||||
|
authentifizierten Signaturen. Das Formular verifiziert weder Personen noch
|
||||||
|
Nachweisinhalte oder Freigabeberechtigungen. Referenzen bleiben inerter Text;
|
||||||
|
es gibt keine neuen Uploads, Netzfreigaben oder externen Dienste. JSONB-Historie
|
||||||
|
und Revisionsschutz sind kein Schutz gegen Datenbankadministratoren und kein
|
||||||
|
unabhaengiges Aufbewahrungsarchiv. Bestehende Loeschregeln bleiben anwendbar.
|
||||||
|
SSO, Rollen, verbindliche Freigaberegeln und manipulationsnachweisbare Aufbewahrung
|
||||||
|
bleiben Voraussetzungen fuer den Firmenbetrieb in Phase 14. Details und die
|
||||||
|
geplante normalisierte Ablage stehen in [RISK_ACCEPTANCE.md](docs/RISK_ACCEPTANCE.md).
|
||||||
|
|
||||||
## Lokale Datenverarbeitung
|
## Lokale Datenverarbeitung
|
||||||
|
|
||||||
|
HTML-Parser-Nachhaertung 2026-09-09: Attributnamen und -werte werden linear
|
||||||
|
gelesen, einschliesslich langer wertloser beziehungsweise fehlerhafter Attribute.
|
||||||
|
Dies vermeidet quadratisches Regex-Backtracking vor der eigentlichen Extraktion.
|
||||||
|
PingCastle-Verweisziele werden pro unveraendertem Markup indiziert; doppelte
|
||||||
|
Definitionen bleiben mehrdeutig, Zielreihenfolge und Quellpositionen erhalten.
|
||||||
|
Die Indizes leben nur im aktuellen Import. Workerzeit, Speichergrenzen und
|
||||||
|
Netzwerk-/Persistenzberechtigungen werden nicht erweitert. Der im Screenshot
|
||||||
|
gezeigte Report wurde nicht gelesen; die Reproduktion verwendet synthetische Daten.
|
||||||
|
|
||||||
ASSESSOR besitzt zwei kontrollierte lokale Betriebsarten:
|
ASSESSOR besitzt zwei kontrollierte lokale Betriebsarten:
|
||||||
|
|
||||||
1. Im empfohlenen Browserbetrieb liefert das lokale Node.js-Backend die React-App
|
1. Im empfohlenen Browserbetrieb liefert das lokale Node.js-Backend die React-App
|
||||||
|
|
@ -21,6 +50,45 @@ Dokumentstruktur bereit. Skriptausführung ist nicht aktiviert; aktive
|
||||||
HTML-Elemente werden vor der Tabellenauswertung entfernt. Umfangreiche oder tief verschachtelte HTML-Berichte verwenden automatisch eine begrenzte, nicht rekursive Tabellenextraktion. Excel-Formeln werden
|
HTML-Elemente werden vor der Tabellenauswertung entfernt. Umfangreiche oder tief verschachtelte HTML-Berichte verwenden automatisch eine begrenzte, nicht rekursive Tabellenextraktion. Excel-Formeln werden
|
||||||
nicht ausgewertet; verschlüsselte Archive und eingebettete VBA-Projekte werden abgewiesen. Spreadsheet-orientierte CSV-Exporte neutralisieren formelaktive Zellanfänge.
|
nicht ausgewertet; verschlüsselte Archive und eingebettete VBA-Projekte werden abgewiesen. Spreadsheet-orientierte CSV-Exporte neutralisieren formelaktive Zellanfänge.
|
||||||
|
|
||||||
|
### PurpleKnight-Berichte
|
||||||
|
|
||||||
|
Workbook-Zusammenfassungen werden mit der generischen Tabellenextraktion
|
||||||
|
kombiniert, nicht anstelle der Indikatoren verarbeitet. HTML liest ausschließlich
|
||||||
|
inert eingebettete, begrenzte Datenobjekte aus lexikalisch erkannten
|
||||||
|
`window.reportJSON`- und `window["Category_n"]`-Zuweisungen. Zeichenketten,
|
||||||
|
Kommentare, HTML-Attribute und andere Raw-Text-Elemente sind keine Datenquellen.
|
||||||
|
Beschädigte Objekte, fehlende native IDs und konkurrierende Zuweisungen werden
|
||||||
|
abgewiesen. Wiederholte IDs und abweichende native Ergebnis-/Detailzähler
|
||||||
|
erzeugen Warnungen ohne stilles Verwerfen von Vorkommen. Null-Details ohne
|
||||||
|
Ergebnisse und ausdrücklich deklarierte leere Berichte bleiben zulässig.
|
||||||
|
Originalwerte, vollständige Zeitstempel, unbekannte Felder und native Quellpfade
|
||||||
|
bleiben erhalten; Summary und Berichtskontext sind von Findings getrennt.
|
||||||
|
Es entstehen keine neuen Ausführungs-, Netzwerk- oder Datenbankrechte.
|
||||||
|
|
||||||
|
### Strukturtreue bei PingCastle
|
||||||
|
|
||||||
|
XML-Attribute werden als separate Originalwerte mit exakten Quellpfaden geführt;
|
||||||
|
ausdrückliche Leere wird nicht als nicht erhoben interpretiert. Lexikalische
|
||||||
|
HTML-Auswertung respektiert zitierte Attribute und Raw-Text-Grenzen, sodass
|
||||||
|
Tooltip-Markup weder zu scheinbaren Tabellen noch zu Zelltext wird. Aktive
|
||||||
|
Attribute werden nur als vollständige Tag-Attribute entfernt. Fehlende native
|
||||||
|
Rule IDs, Dubletten und abweichende deklarierte Trefferzahlen besitzen explizite
|
||||||
|
Diagnosen; ein angekündigter, aber nicht lesbarer Befundbestand darf nicht als
|
||||||
|
erfolgreicher Nullbefund gelten. Regeln und Originalzähler bleiben getrennt.
|
||||||
|
Diese Änderung öffnet keine aktive HTML-, Netzwerk- oder Persistenzberechtigung.
|
||||||
|
Details und Grenzen: `docs/IMPORT_SECURITY_LIMITS.md`.
|
||||||
|
|
||||||
|
### Eingebettete Maester-Berichtsdaten
|
||||||
|
|
||||||
|
Maester-HTML kann neben JSON ein minifiziertes JavaScript-Objektliteral enthalten.
|
||||||
|
Der Quellenextractor liest daraus ausschließlich statische Daten: Objekte, Arrays,
|
||||||
|
Zeichenketten (auch mehrzeilig ohne Interpolation), endliche Zahlen, Boolean und
|
||||||
|
Null sowie die festen Boolean-Schreibweisen `!0`/`!1`. Er führt keinerlei Code aus.
|
||||||
|
Aufrufe, Variablenzugriffe, Getter, Spread, berechnete Schlüssel, doppelte Schlüssel
|
||||||
|
und Template-Interpolation werden abgewiesen. Die Grenzen und Regressionen sind in
|
||||||
|
`docs/IMPORT_SECURITY_LIMITS.md` beschrieben. Importierte Skripte bleiben inaktiv;
|
||||||
|
es gibt keine neue Netzwerk-, Shell- oder Tauri-Berechtigung.
|
||||||
|
|
||||||
### Verweisziele aus Berichten
|
### Verweisziele aus Berichten
|
||||||
|
|
||||||
Berichte führen fachlich notwendige Belegstellen als Verweise mit (Purple
|
Berichte führen fachlich notwendige Belegstellen als Verweise mit (Purple
|
||||||
|
|
@ -104,6 +172,27 @@ Das kanonische Modell kann eine Quelle ausdrücklich als eingebettet, extern ode
|
||||||
nicht aufbewahrt kennzeichnen. Backupdateien benötigen gesonderten Zugriffsschutz
|
nicht aufbewahrt kennzeichnen. Backupdateien benötigen gesonderten Zugriffsschutz
|
||||||
und ein in Phase 14 festzulegendes Aufbewahrungs- und Löschkonzept.
|
und ein in Phase 14 festzulegendes Aufbewahrungs- und Löschkonzept.
|
||||||
|
|
||||||
|
## Report Engine (Nur-Lese-Grenze und Archiv)
|
||||||
|
|
||||||
|
Ergaenzung 2026-09-30 (2.10.0): Die Report Engine laeuft eigenstaendig neben ASSESSOR
|
||||||
|
und liest nur. Mit PostgreSQL nutzt sie einen eigenen kleinen Pool, dessen Sitzungen
|
||||||
|
`default_transaction_read_only=on` erzwingen. PostgreSQL lehnt damit jeden
|
||||||
|
Schreibversuch ab. `PostgresAssessorRepository.openReadOnly` verweigert eine nicht
|
||||||
|
schreibgeschuetzte Sitzung und stoesst keinen Result-DB-Neuaufbau an. Der Report-Leser
|
||||||
|
fuehrt keine Migrationen aus. Der Lese-Port stellt nur Lesemethoden bereit, und ein
|
||||||
|
Architekturtest verhindert Aufrufe schreibender Repository-Methoden. Faellt die Report
|
||||||
|
Engine beim Start aus, laeuft ASSESSOR weiter und nur Reportlaeufe werden abgelehnt.
|
||||||
|
|
||||||
|
Jeder Reportlauf wird ausserhalb der Datenbank einmalig und danach schreibgeschuetzt
|
||||||
|
archiviert: Projektion, beide HTML-Dateien und ein Manifest mit SHA-256-Werten. Das
|
||||||
|
Archiv enthaelt vollstaendige technische Kundendaten und ist ein sensibler lokaler
|
||||||
|
Speicher. Es liegt nie im Repository, im ausgelieferten Anwendungsordner oder in einem
|
||||||
|
OneDrive-Ordner (beim Start geprueft). Der Ordnername je Client ist ein Hash der
|
||||||
|
Projekt-ID, Lauf-IDs werden validiert, und Dateien werden beim Lesen gegen das Manifest
|
||||||
|
geprueft. Mit dem Client wird auch sein Archiv geloescht. `projection.json` und Manifest
|
||||||
|
werden nie zusammen mit dem Management Report ausgeliefert. Aufbewahrungsfristen
|
||||||
|
bleiben Teil der Phase-14-Entscheidungen.
|
||||||
|
|
||||||
## Tauri-Grenzen
|
## Tauri-Grenzen
|
||||||
|
|
||||||
- Die Desktop-Capability enthält ausschließlich `core:default`.
|
- Die Desktop-Capability enthält ausschließlich `core:default`.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ services:
|
||||||
ASSESSOR_HOST: 0.0.0.0
|
ASSESSOR_HOST: 0.0.0.0
|
||||||
ASSESSOR_PORT: "4173"
|
ASSESSOR_PORT: "4173"
|
||||||
ASSESSOR_ARTIFACT_ROOT: /var/lib/assessor/artifacts
|
ASSESSOR_ARTIFACT_ROOT: /var/lib/assessor/artifacts
|
||||||
|
ASSESSOR_REPORT_ARCHIVE_DIR: /var/lib/assessor/report-archive
|
||||||
ASSESSOR_POSTGRES_HOST: postgres
|
ASSESSOR_POSTGRES_HOST: postgres
|
||||||
ASSESSOR_POSTGRES_PORT: "5432"
|
ASSESSOR_POSTGRES_PORT: "5432"
|
||||||
ASSESSOR_POSTGRES_DB: ${ASSESSOR_POSTGRES_DB:?set ASSESSOR_POSTGRES_DB}
|
ASSESSOR_POSTGRES_DB: ${ASSESSOR_POSTGRES_DB:?set ASSESSOR_POSTGRES_DB}
|
||||||
|
|
@ -26,6 +27,7 @@ services:
|
||||||
- "${ASSESSOR_BIND:?set ASSESSOR_BIND}:${ASSESSOR_PUBLISH_PORT:?set ASSESSOR_PUBLISH_PORT}:4173"
|
- "${ASSESSOR_BIND:?set ASSESSOR_BIND}:${ASSESSOR_PUBLISH_PORT:?set ASSESSOR_PUBLISH_PORT}:4173"
|
||||||
volumes:
|
volumes:
|
||||||
- assessor-artifacts:/var/lib/assessor/artifacts
|
- assessor-artifacts:/var/lib/assessor/artifacts
|
||||||
|
- assessor-report-archive:/var/lib/assessor/report-archive
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
@ -53,4 +55,5 @@ services:
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
assessor-artifacts:
|
assessor-artifacts:
|
||||||
|
assessor-report-archive:
|
||||||
assessor-postgres-data:
|
assessor-postgres-data:
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
name: assessor-2_0
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
|
|
@ -18,3 +20,5 @@ services:
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
assessor-postgres-data:
|
assessor-postgres-data:
|
||||||
|
name: assessor-2_0_assessor-postgres-data
|
||||||
|
external: true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
# Client Information
|
||||||
|
|
||||||
|
Stand: 2026-10-04. Core capability seit ASSESSOR 2.14.0; Cloud-Uebersicht 2.14.2, read contract 1.1.0.
|
||||||
|
|
||||||
|
Client Information bezeichnet die bisher als scannerbezogene Zusatzinformationen behandelten Beobachtungen und Original-Berichtsabschnitte. Der vorhandene Bestand bleibt in `EnvironmentObservation` und `ScannerReportSection` gespeichert. Es gibt keine neue Datenbank, Tabelle, Migration oder zweite Kopie des Bestands. Bestehende Namen auf Persistenzebene bleiben kompatibel. Die technischen Einzelbefundfelder im Finding Reader unter Additional Information und die Stammdaten Name/Consultant sind andere bestehende Funktionen.
|
||||||
|
|
||||||
|
## Gezielter Abruf
|
||||||
|
|
||||||
|
Aktuelle Korrektur 2.14.2: Die unten historisch dokumentierten Verfuegbarkeitshinweise der Cloud-Kachel sind durch konkrete berichtete Werte bzw. `Not reported` ersetzt. Details und Grenzen stehen unter **Konkrete Cloud-Uebersicht**; die permanente Sichtbarkeit und der Results-Scope bleiben verbindlich.
|
||||||
|
|
||||||
|
Client Info besitzt den Button **Client Information**. Er oeffnet das gemeinsame Datenfenster fuer den ausgewaehlten Mandanten und die ausgewaehlten Scanlaeufe. Erst beim Oeffnen beginnt die Abfrage. Bereich, Freitext, exakter Eigenschaftsschluessel und exakte Objektidentitaet schraenken die Ergebnisse ein. Details zeigen den kompletten Originalwert, Erfassungszustand, Erfassungszeit (falls von der Quelle angegeben), Datei, Scanlauf, Quellposition und Mapping-Metadaten. Vorhandene Quellabschnitte behalten ihre Tabellen und Verweise. Apply und Reset wirken nur auf diese Abfrage, nicht auf Work.
|
||||||
|
|
||||||
|
Nutzerentscheidung 2026-10-04, umgesetzt ab 2.14.1: Die zusaetzliche **Cloud Information**-Kachel in der bestehenden Umgebungskachelreihe ist immer sichtbar, auch ohne Cloud-Nachweise. Ihre vier Felder zeigen die Verfuegbarkeit von Quellnachweisen fuer Sign-in/Conditional Access, Rollen/Berechtigungen, Application consent und externen Zugriff/Sharing. `No retained information` kennzeichnet fehlende Nachweise; `Source evidence not assessed` kennzeichnet vorhandene, nicht als beobachtet erfasste Nachweise. Keiner dieser Texte leitet aus einem Teststatus eine konkrete Konfiguration ab. Results oeffnet denselben Client-Information-Browser, eingeschraenkt auf die vier Cloud-Bereiche; Bereichsauswahl und Reset bleiben innerhalb dieses Cloud-Scope. Ohne gespeicherten Mandanten zeigt Results den bestehenden leeren Quellenhinweis, ohne eine API-Abfrage zu erfinden. Die immer sichtbare Kachel ersetzt die zuvor vorgeschlagene Bedingung, sie nur bei vorhandenen Cloud-Daten einzublenden.
|
||||||
|
|
||||||
|
Der Jetzt-Zustand ist der aktuelle **importierte** Bestand im gewaehlten Scope. Er ist kein Liveinventar. Ohne explizite Zeitraumauswahl bedeutet die API alle nicht ersetzten Importlaeufe des Mandanten, nicht automatisch den zeitlich letzten Timeslot. Unterschiedliche Quellen, widerspruechliche Werte und fehlende Zeitangaben werden nicht zu einem vermeintlich eindeutigen aktuellen Wert verschmolzen.
|
||||||
|
|
||||||
|
HTTP: `GET /api/v1/projects/{projectId}/client-information`
|
||||||
|
|
||||||
|
| Parameter | Bedeutung |
|
||||||
|
| --- | --- |
|
||||||
|
| `assessmentId` | Optionaler Timeslot, muss zum Mandanten gehoeren |
|
||||||
|
| `scanRunIds` | Exakte Scanlaeufe; wiederholter Parameter |
|
||||||
|
| `includeSuperseded` | Standard `false`; `true` erlaubt auch ersetzte historische Importlaeufe |
|
||||||
|
| `areas` | Fachliche Bereiche, wiederholbar |
|
||||||
|
| `properties` | Exakte Eigenschaftsschluessel, wiederholbar |
|
||||||
|
| `subjectTypes`, `subjectIds` | Exakter Objekttyp / Quellidentitaet, wiederholbar |
|
||||||
|
| `toolIds`, `states` | Exakte Scanner-ID / Erfassungszustaende, wiederholbar |
|
||||||
|
| `search` | Teiltext in Quellwert, Label, Eigenschaft, Objekt; bei Abschnitten auch Text/Tabellen |
|
||||||
|
| `offset`, `limit` | Beobachtungsseite; Standard 0/50, maximal 200 pro Seite |
|
||||||
|
| `sectionOffset`, `sectionLimit` | Seite primaerer Quellabschnitte; Standard 0/10, maximal 50 |
|
||||||
|
|
||||||
|
Selektoren einer Liste werden mit ODER, verschiedene Listen mit UND verknuepft. Fehlende oder leere Listen beschraenken nicht. Erlaubt sind maximal 100 Eintraege pro Liste und 500 Zeichen pro Eintrag oder Suchtext. Unbekannte Parameter/Bereiche/Zustaende, ungueltige Seitengroessen, doppelte skalare HTTP-Parameter und fremde Mandanten-/Timeslot-/Scan-Zuordnungen werden abgewiesen.
|
||||||
|
|
||||||
|
Bereichsschluessel: `directory`, `inventory`, `resilience`, `password-policy`, `certificates`, `cloud-access`, `cloud-roles`, `cloud-apps`, `cloud-sharing`, `source-metadata`, `other`. Die Zuordnung nutzt deklarierte Importmetadaten oder stabile Eigenschafts-/Objekt-/Abschnittsschluessel; keine Bewertung aus Ueberschriften.
|
||||||
|
|
||||||
|
Beispiel: `/api/v1/projects/synthetic-client/client-information?assessmentId=synthetic-slot&properties=domain.fqdn&limit=50`.
|
||||||
|
|
||||||
|
Die Antwort erhaelt den Snapshot mit `contractVersion`, `purpose: internal-analysis`, `repositoryRevision`, `scopeMode`, getrennten Gesamtsummen und Seitengrenzen. `coverage` beschreibt den vollstaendigen gefilterten Bestand, nicht nur die Seite. Beobachtungen sind nach property/id, Abschnitte nach ordinal/id sortiert. Passende Originalabschnitte bleiben vollstaendig; sie koennen weitere Werte enthalten als die ausgewaehlten Beobachtungen. Exakte, eindeutige Linkziele derselben Datei und desselben Laufs werden auch ueber Seitengrenzen mitgeliefert und in `linkedSectionIds` benannt. Sie zaehlen nicht zur primaeren Seitengroesse. Mehrdeutige Ziele werden nicht geraten.
|
||||||
|
|
||||||
|
Bei mehreren Seiten `repositoryRevision` vergleichen. Aendert sie sich, die Seitenauswahl neu beginnen; die API stellt keinen dauerhaft reservierten Snapshot-Token bereit. Jede einzelne Abfrage prueft die committed Revision vor und nach dem Lesen und wiederholt bei Aenderung hoechstens dreimal, danach folgt ein Konfliktfehler. PostgreSQL liest dafuer die Datenbankrevision, nicht nur den lokalen Prozesscache. Historischer Abruf liefert erhaltene Importbeobachtungen, keine zeitpunktbezogene Rekonstruktion geloeschter Daten oder alter Repository-Revisionen.
|
||||||
|
|
||||||
|
## Read-only-Port fuer Engines
|
||||||
|
|
||||||
|
Der veroeffentlichte Vertrag ist `src/persistence/client-information-read-port.ts` (`ClientInformationReadPort`). UI/API und spaetere Engines nutzen `queryClientInformation` mit demselben `ClientInformationQuery` aus `src/domain/client-information.ts`.
|
||||||
|
|
||||||
|
Die Backend-Komposition bindet den Port an eine **read-only** Repository-/Datenbanksitzung und reicht nur den eingefrorenen Port an die Engine weiter:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const port = clientInformationReadPortFrom(readOnlyRepository,
|
||||||
|
() => readOnlyRepository.getCommittedRevision());
|
||||||
|
const information = await port.queryClientInformation({
|
||||||
|
projectId, assessmentId, scanRunIds,
|
||||||
|
areas: ['cloud-access'], limit: 100
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Das ist Kompositionscode, kein direkter Repository-/Tabellenzugriff aus einer Engine. Engines bekommen keine Mutationsmethoden. Die Rueckgabe ist eine unabhaengige Kopie; Veraenderungen durch einen Leser veraendern den Core nicht. Optionalen Engines ausgeschaltet zu lassen hat keinen Einfluss auf diese Core-Funktion. Bestehende Context-/Report-Ports bleiben kompatibel und werden nicht still auf neue Inputs umgestellt.
|
||||||
|
|
||||||
|
Diese Daten dienen interner Analyse. Der Port ist keine Result-DB- oder Exportfreigabe. Was eine Engine anzeigt oder exportiert, muss weiter die bestehenden Disclosure- und Datenschutzregeln durchlaufen. Cloud-Testnachweise werden nicht als neue Result-DB-Zeilen oder Findings veroeffentlicht. Der neue Port fuegt keine Bewertung, Prioritaet oder Scoreberechnung hinzu.
|
||||||
|
|
||||||
|
## Neuer fachlicher Mehrwert und Grenzen
|
||||||
|
|
||||||
|
| Quelle | Bereits vorhandener Bestand | Neu in dieser Umsetzung |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| PingCastle | Directory, Inventar, Resilienz, Kennwortrichtlinien, PKI und erhaltene Originaltabellen | Gezielter einheitlicher Zugriff; keine doppelte AD-Extraktion |
|
||||||
|
| Purple Knight | Bereits erhaltene Assessment-/Reportinformationen | Derselbe Zugriff auf den vorhandenen Bestand |
|
||||||
|
| CIS-CAT | Bereits erhaltene Host-/Benchmark-/Berichtsinformationen | Derselbe Zugriff auf den vorhandenen Bestand |
|
||||||
|
| Maester | Tenant, Reportmetadaten und Testzaehler | Erhaltene Detailnachweise zu Anmeldung/Conditional Access, Rollen, App-Zustimmung und externem Zugriff/Sharing separat auffindbar |
|
||||||
|
| Andere Profile | Nur tatsaechlich bereits erzeugte Beobachtungen/Abschnitte | Gemeinsamer Zugriff; keine behauptete neue Quellabdeckung |
|
||||||
|
|
||||||
|
Maester HTML (`ResultDetail.TestResult`) sowie Excel/CSV mit exakter bekannter Regel-ID und getrennten Status-/Detailspalten werden erweitert. CSV `Result` plus `ResultDetail` und Excel/CSV `Status` plus `Result` werden unterstuetzt. Die generische Tabellenpipeline verarbeitet weiterhin saemtliche Zeilen. JSON/XML und beliebige unbekannte Exportspalten erhalten durch diesen Schritt keine neue Cloud-Semantik.
|
||||||
|
|
||||||
|
Die Eigenschaften haben die Form `cloud.<group>.<native-id-in-lowercase>.reported_evidence`; etwa `cloud.authentication.eidsca.af01.reported_evidence`. Der vollstaendige Originaltext bleibt unveraendert; enthaltene Markdown-Tabellen werden als Originalbelege mitgefuehrt. Metadaten nennen `informationKind: reported-test-evidence`, `inventoryComplete: false`, Regel-ID, Quellstatus und Mapping-Version. Dies sind die vom jeweiligen Test berichteten Informationen, **kein vollstaendiges Konfigurationsinventar** und keine Ermittlung der effektiv wirksamen Richtlinie. Ein nativer Teststatus allein erzeugt keinen Konfigurationswert. Nicht ausgefuehrte/fehlerhafte Tests mit Detailtext bleiben `not-assessed`; fehlende Nachweise werden nicht zu `false` oder `0`.
|
||||||
|
|
||||||
|
Exakte freigegebene IDs der Version 1.0.0:
|
||||||
|
|
||||||
|
- Authentication: EIDSCA.AF01-06, AG01-03, AM01-04, AM06-07, AM09-10, AP01, AS04, AT01-02, AV01 (jeweils Praefix EIDSCA.).
|
||||||
|
- Conditional Access: MT.1001, MT.1003-1021, MT.1035, MT.1036, MT.1038.
|
||||||
|
- Rollen: MT.1025-1032; CISA.MS.AAD.7.1-7.6.
|
||||||
|
- Zustimmung: EIDSCA.AP08-10, CP01, CP03-04, CR01-04; CISA.MS.AAD.5.1-5.4.
|
||||||
|
- Externer Zugriff/Sharing: EIDSCA.AP04, AP07, ST08, ST09; CISA.MS.SHAREPOINT.1.1-1.3, 2.1-2.2, 3.1-3.3.
|
||||||
|
|
||||||
|
Die genaue Liste steht deklarativ im Importmodul; Titel-/Aehnlichkeitssuche erweitert sie nicht. Quellpruefung 2026-10-04: offizieller [Maester-Testkatalog](https://maester.dev/docs/tests/), exemplarisch [EIDSCA.AF01](https://maester.dev/docs/tests/EIDSCA.AF01/), [MT.1005](https://maester.dev/docs/tests/MT.1005/) und [MT.1032](https://maester.dev/docs/tests/MT.1032/). Offline-Zusammenfassung der fachlichen Zuordnung: Der Katalog ordnet diese IDs Tests fuer Authentifizierungsmethoden, Conditional Access, privilegierte Rollen, Zustimmungseinstellungen und Gast-/Sharing-Einstellungen zu. Er garantiert keine vollstaendige Konfiguration im Export; allein die tatsaechlich enthaltenen Originalwerte sind massgeblich. Kein Onlineaufruf ist fuer Import oder Abfrage erforderlich.
|
||||||
|
|
||||||
|
Das Maester-Profil wird auf 1.2.0 und der HTML-Extractor auf 1.2.0 angehoben, der Tabellen-Extractor startet mit 1.0.0. Der historische Verarbeitungsmanifest bleibt erhalten, der Vergleichsschluessel unveraendert. Fruehere Maester-Importe empfehlen regulaeren Reimport. Es erfolgt kein automatischer Kunden-Reimport. Supported Maester reports enthalten weiterhin keinen nutzbaren nativen numerischen Overall Score; der deklarierte Grund und die gemeinsame Fallback-Scoringlogik bleiben unveraendert.
|
||||||
|
|
||||||
|
## Verifikation und verbleibende Grenzen
|
||||||
|
|
||||||
|
### Konkrete Cloud-Uebersicht (2026-10-04, 2.14.2)
|
||||||
|
|
||||||
|
Nutzerkorrektur: Die dauerhaft sichtbare Cloud-Kachel zeigt je Bereich einen belegten Istwert statt Verfuegbarkeitshinweisen. Weitere Fakten und Originale bleiben in Results. Fehlende, nicht ausgefuehrte oder nicht eindeutig erkennbare Angaben erscheinen als `Not reported`. Konflikte werden genannt; mehrere Scan-/Tenant-Scopes werden nicht zusammengerechnet. Passed/Failed, Empfehlungen und generische Scores erzeugen keine Konfiguration.
|
||||||
|
|
||||||
|
Die reine Core-Normalisierung `cloudInformationFacts` projiziert bereits gespeicherte Maester-Testdetails. Lesevertrag **1.1.0** ergaenzt `cloudFacts` fuer den gesamten gefilterten Scope vor Pagination: kanonische Property, Original-/Anzeigewert, `reported-setting`/`reported-subset`, Beobachtungs-ID, Scanlauf, Datei, Regel-ID, Quellposition, Quellzeit und Mapping-Version. Engines lesen weiterhin ausschliesslich den veroeffentlichten read-only Port, ohne interne Module zu importieren. Propertyfilter beziehen sich weiter auf die erhaltenen Beobachtungs-Properties; `cloudFacts` enthaelt nur Fakten dieser gefilterten Beobachtungen. Es gibt keine DB-Schreibvorgaenge oder Schemaaenderung. Bereits erhaltene passende Nachweise profitieren ohne Reimport; fehlen die in 2.14.0 eingefuehrten Nachweise, ist weiter ein regulaerer Reimport erforderlich. Profil-/Extractor-Versionen und nativer Scorevertrag bleiben unveraendert.
|
||||||
|
|
||||||
|
Offline-Mapping 1.0.0, Quellpruefung 2026-10-04:
|
||||||
|
|
||||||
|
- EIDSCA.AF01/AM01/AT01/AV01: State von FIDO2, Authenticator, Temporary Access Pass und Voice. AP08/CR01/AP10: Consent-Policy, Admin-Request-State, App-Registrierung. AP04/AP07/ST08/ST09: Gast-Einladungen, Gastrolle, Gruppenbesitz/-Zugriff. Exakte native erste Saetze liefern den berichteten Wert, einschliesslich ausdruecklich nicht konfigurierter Angaben und des kombinierten Ist-/Sollwertsatzes. Alternativ ist nur eine eindeutige zweispaltige `Setting`/`Actual Value`-Tabelle fuer das exakt benannte Feld erlaubt, nie `Expected Value`.
|
||||||
|
- CISA.MS.AAD.7.4: exakte Tabelle `Role / Principal Type / Display Name / Status` mit `No Expiration` oder expliziter Satz ueber keine entsprechenden Zuweisungen. MT.1025-1028: Anzahl exakt berichteter Listenzeilen fuer den jeweiligen Principal-Typ. Diese Zahlen beschreiben nur die berichtete Pruefteilmenge. MT.1031: expliziter PIM-Management-Satz; derselbe Helper-Satz unter MT.1032 erzeugt keine PIM-Aussage.
|
||||||
|
- MT.1007: eindeutige berichtete All-User-MFA-Policy-Links oder expliziter Satz ueber keine entsprechende Policy. Keine Gesamtzahl aktiver/Report-only-Policies. CISA.MS.SHAREPOINT.1.1: nur ausdruecklicher `Current setting`-Wert.
|
||||||
|
|
||||||
|
Offizielle Quellen: [EIDSCA-Generator AF01](https://github.com/maester365/maester/blob/main/powershell/internal/eidsca/Test-MtEidscaAF01.ps1) und entsprechende AP08/AP07/CR01-Generatoren im selben Verzeichnis; [permanente Rollen](https://github.com/maester365/maester/blob/main/powershell/public/cisa/entra/Test-MtCisaPermanentRoleAssignment.ps1), [privilegierte Zuweisungen](https://github.com/maester365/maester/blob/main/powershell/public/maester/entra/Test-MtPrivPermanentDirectoryRole.ps1), [PIM-Alerts](https://github.com/maester365/maester/blob/main/powershell/public/maester/entra/Test-MtPimAlertsExists.ps1), [All-User MFA](https://github.com/maester365/maester/blob/main/powershell/public/maester/entra/Test-MtCaMfaForAllUsers.ps1), [Graph-Markdown](https://github.com/maester365/maester/blob/main/powershell/internal/Get-GraphObjectMarkdown.ps1), [SharePoint Sharing](https://github.com/maester365/maester/blob/main/powershell/public/cisa/spo/Test-MtCisaSpoSharing.ps1). Microsoft beschreibt die bekannten [Consent-Policies](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent) und [Gastrollen](https://learn.microsoft.com/en-us/entra/identity/users/users-restrict-guest-permissions). Das Mapping kuerzt deren Anzeige; exakte Originalwerte bleiben im Vertrag und in Results. Keine Online-Abfrage zur Laufzeit.
|
||||||
|
|
||||||
|
Synthetische Tests decken alle drei erweiterten Formate, Originalwerte/Tabellen/Quellpositionen, unbekannte IDs, fehlende/bare Statusnachweise, Error/Skipped, false/0/Leerwerte, Konflikte, Pagination/Links, Mandanten-/Timeslotgrenzen, explizite Historie, konkurrierende Revisionen, read-only PostgreSQL-SQL, HTTP-Validierung und den tatsaechlichen Client-Info-Button ab. UI-Pruefungen umfassen beide Themes, Apply/Reset, Fehler/Retry, spaete Antworten und ausgeschaltete Engines. Keine Kundenberichte oder Kundendaten wurden fuer diese Implementierungspruefung gelesen.
|
||||||
|
|
||||||
|
Filterung erfolgt derzeit ueber den vorhandenen scoped Snapshot im Backend. Es gibt keine SQL-Filterindizes oder serverseitige Stream-Pagination; sehr grosse Scopes koennen deshalb trotz begrenzter Antwortseiten Arbeitsspeicher beanspruchen. Vollstaendige Cloud-Inventare aus weiteren echten Exportstrukturen erfordern eine gesonderte, quellengestuetzte Profilerweiterung und bleiben offen.
|
||||||
|
|
@ -3,6 +3,12 @@
|
||||||
Stand: 2026-07-17
|
Stand: 2026-07-17
|
||||||
Zugehörige Planphase: Phase 8
|
Zugehörige Planphase: Phase 8
|
||||||
|
|
||||||
|
## Statusklarstellung 2026-09-11
|
||||||
|
|
||||||
|
Der folgende Text dokumentiert den historischen Stand, nicht die Abnahme eines belastbaren objektgenauen Timeslotvergleichs. Insbesondere ist die unten beschriebene Regel `resolved = im Ziellauf nicht mehr vorhanden` kein ausreichender Behebungsnachweis. Die aktuelle Timeslot-Projektion kann Quell-/Verarbeitungsprüfungen umgehen; die automatisch verwendeten Regel-/Definitionsmatches garantieren keine gemeinsame Objektidentität. Die hier historisch genannten Regel-/Asset- und Merkmalsstufen sind kein Nachweis ihrer aktuellen automatischen Verdrahtung.
|
||||||
|
|
||||||
|
Die Erweiterung in [TIMESLOT_COMPARISON_PLAN.md](TIMESLOT_COMPARISON_PLAN.md) definiert den Gegenprüfungs-, Identitäts- und Fortschreibungsvertrag sowie P0-P6 mit Abnahmetests. P0 ist abgeschlossen; P1/P2 sind in Arbeit. Der additive Domänenvertrag und `compareProjectTimeslotEvidence` arbeiten bereits profilgebunden mit echten Scannerläufen, Dateiprovenienz, Processingkompatibilität, eindeutigem Scope und neuerer Reportzeit. Abwesenheit bleibt konservativ ungeklärt. Dieser sichere Pfad ist noch nicht an Persistenz/API oder Oberfläche angebunden und ersetzt daher die unten dokumentierte Legacyengine im Produkt noch nicht; die frühere Beschreibung bleibt zur Nachvollziehbarkeit erhalten.
|
||||||
|
|
||||||
## Zweck
|
## Zweck
|
||||||
|
|
||||||
Die Vergleichsengine ordnet Finding-Vorkommen aus zwei Scan-Läufen reproduzierbar ein. Sie arbeitet ausschließlich auf dem kanonischen Assessment-Modell und benötigt weder React noch Browser-, Tauri- oder Datenbankfunktionen.
|
Die Vergleichsengine ordnet Finding-Vorkommen aus zwei Scan-Läufen reproduzierbar ein. Sie arbeitet ausschließlich auf dem kanonischen Assessment-Modell und benötigt weder React noch Browser-, Tauri- oder Datenbankfunktionen.
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,13 @@ dependencies, runs the complete test suite and production build, validates the
|
||||||
resolved Compose configuration, checks port/project isolation, backs up an
|
resolved Compose configuration, checks port/project isolation, backs up an
|
||||||
existing target environment, then builds and starts only that environment.
|
existing target environment, then builds and starts only that environment.
|
||||||
|
|
||||||
Compose project scoping produces independent PostgreSQL and artifact volumes.
|
Compose project scoping produces independent PostgreSQL, artifact and report-archive volumes.
|
||||||
|
The Report Engine (2.10.0) archives every report run write-once under
|
||||||
|
`ASSESSOR_REPORT_ARCHIVE_DIR` (`/var/lib/assessor/report-archive` in Compose). The archive
|
||||||
|
holds complete technical customer data, is deleted together with its client and must be
|
||||||
|
included in backup and retention decisions like the artifact volume. The Report Engine
|
||||||
|
reads PostgreSQL through its own pool with `default_transaction_read_only=on`; it runs
|
||||||
|
no migrations and writes nothing to the database.
|
||||||
PostgreSQL has no host-published port. `restart: unless-stopped` preserves both
|
PostgreSQL has no host-published port. `restart: unless-stopped` preserves both
|
||||||
environments across Docker daemon and server restarts.
|
environments across Docker daemon and server restarts.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
# GPO-Backup-Vergleich mit Baselinevarianten
|
||||||
|
|
||||||
|
Stand: 2026-09-16 · Phase 7/13B · Evaluator `policy-baseline-evaluation@1.9.0`
|
||||||
|
|
||||||
|
## Prüfgegenstand
|
||||||
|
|
||||||
|
Geprüft wird die importierte GPO-Backup-Konfiguration gegen die ausdrücklich referenzierten Microsoft-Baselineanalysen. Das ist kein Nachweis der effektiv angewendeten Gerätekonfiguration, der Erreichbarkeit eines Dienstes oder einer tatsächlich ausnutzbaren Überprivilegierung. Baselinepakete bleiben getrennte Prüfungen; ein Ergebnis gilt nicht automatisch für andere Pakete, Betriebssysteme oder Rollen.
|
||||||
|
|
||||||
|
Ein PingCastle-Scan ist keine Voraussetzung. OS-Inventar und Domain Functional Level dürfen eine einzelne GPO weder automatisch einer Zielrolle zuordnen noch ihre Anwendung beweisen. Die vorhandene Vorlagenauswahl akzeptiert nur einen eindeutigen, expliziten Vergleichskontext; beobachtungsgebundene Inventarkontexte wählen keine Rolle aus. Unbekannte Vorlagen bleiben Kandidaten. Eine explizite Auswahl ohne passende Vorlage bleibt ungeklärt.
|
||||||
|
|
||||||
|
## Operativer Scope und Score
|
||||||
|
|
||||||
|
Die normale GPO-Analyse ist auf Fehlkonfigurationen innerhalb des tatsächlich konfigurierten, technisch vergleichbaren und ausdrücklich anwendbaren Kundenscopes begrenzt. Eine fehlende Microsoft-Empfehlung bleibt als `Not configured` transparent sichtbar, erzeugt aber weder ein operatives Finding noch eine Standard-Remediation oder einen Scoreabzug. Die vollständige Einführung einer Microsoft-Baseline ist ein eigener, ausdrücklich beauftragter Hardening-Scope.
|
||||||
|
|
||||||
|
Präzisierung vom selben Tag: `Not configured` bleibt ein score-neutrales Policy-Urteil. `Duplicates` und `Conflicts` bleiben eigenständige Diagnosekategorien der technischen Policy-Einstellung, sind aber keine alternativen Urteile, die Pass, Fail oder Review required verdecken. Die Mitgliedsvergleiche werden genau einmal je technischer Policy-Identität zusammengeführt: Weicht mindestens ein konfigurierter, eindeutig vergleichbarer In-Scope-Wert nachweislich ab, lautet das Gesamturteil der Policy-Einstellung `Fail`; Conflict-/Duplicate-Merkmale bleiben zusätzlich sichtbar. Bestehen alle anwendbaren Mitglieder, lautet das Urteil `Pass`. Sind Anwendbarkeit, Vorrang oder Vergleich ungeklärt, bleibt das Urteil `Review required`. Mehrere Quellzeilen, doppelte Vorkommen und Diagnosemarker vervielfachen den Scorebeitrag nicht.
|
||||||
|
|
||||||
|
Der GPO-Score ist deshalb ausschließlich als Konformität der konfigurierten, vergleichbaren In-Scope-Einstellungen zu bezeichnen. Er ist kein Nachweis vollständiger Microsoft-Baseline-Umsetzung oder -Abdeckung.
|
||||||
|
|
||||||
|
## Entscheidungsvertrag
|
||||||
|
|
||||||
|
Jede berücksichtigte Vorlage wird für dieselbe aufgelöste technische Einstellung einschließlich Computer-/Benutzerbereich einzeln verglichen. Der neutrale Vergleich verwendet exakte, typisierte Gleichheit. Bei Kontenlisten ist die Reihenfolge unerheblich; Originalwerte bleiben erhalten. Registrytypen und die Reihenfolge von REG_MULTI_SZ bleiben relevant. Abweichend bedeutet hier nicht automatisch schwächer: Eine gesonderte fachliche Regel wäre nötig, um eine strengere Einstellung als zulässig zu behandeln.
|
||||||
|
|
||||||
|
| Nachgewiesener Zustand | Ergebnis in Work |
|
||||||
|
| --- | --- |
|
||||||
|
| Konfiguriertes IST erfüllt jede berücksichtigte Variante | Pass |
|
||||||
|
| Konfiguriertes IST weicht von jeder berücksichtigten Variante ab | Fail / Filter Deviation |
|
||||||
|
| Einzelurteile widersprechen sich | Review required |
|
||||||
|
| Wertvergleich, Kandidatenabdeckung oder explizite Anwendbarkeit ist ungeklärt | Review required |
|
||||||
|
| Empfohlenes Setting besitzt im analysierten Backupsatz keinen beobachteten konfigurierten Wert | Not configured; score-neutral |
|
||||||
|
| Der analysierte Backupsatz ist nur ein ausgewählter Teilbestand | Not configured mit Scope-Diagnose; keine behauptete Abwesenheit in Domäne oder effektivem Gerätezustand |
|
||||||
|
| Für die technische Einstellung existiert überhaupt kein Baselinewert | Kein erfundener Pass-/Fail-Vergleich |
|
||||||
|
|
||||||
|
### Verbindliche Registry-Zustandsregeln
|
||||||
|
|
||||||
|
Registry.pol-Anweisungen sind Sollzustände und keine fehlenden Baselinewerte. Innerhalb des analysierten Backup-Sets gelten deshalb folgende Regeln; sie behaupten weiterhin keinen effektiven Registry-Zustand auf einem Gerät:
|
||||||
|
|
||||||
|
| Baseline-Sollzustand | Zustand im analysierten Kunden-Backup | Urteil |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Einzelnen Wert löschen | Zielwert nicht konfiguriert oder ausdrücklich gelöscht | Pass |
|
||||||
|
| Einzelnen Wert löschen | Zielwert wird gesetzt | Fail |
|
||||||
|
| Alle Werte eines Schlüssels löschen | Kein Wert für das Schlüsselziel konfiguriert oder ausdrückliche Delete-all-Anweisung | Pass |
|
||||||
|
| Alle Werte eines Schlüssels löschen | Ein Wert für das Schlüsselziel wird gesetzt | Fail |
|
||||||
|
| Schlüssel anlegen | Schlüsselanlage ist konfiguriert | Pass |
|
||||||
|
| Schlüssel anlegen | Schlüsselanlage fehlt | Fail |
|
||||||
|
| Typisierten leeren Text setzen | Wert existiert mit identischem Registrytyp und Inhalt `""` | Pass |
|
||||||
|
| Typisierten leeren Text setzen | Wert fehlt, besitzt einen anderen Typ oder ist nicht leer | Fail |
|
||||||
|
|
||||||
|
Ein leerer `REG_SZ`- oder `REG_EXPAND_SZ`-Inhalt ist damit ein expliziter Wert und niemals allein wegen seiner Länge „Baselinewert nicht verfügbar“. Delete-, Delete-all- und Create-key-Anweisungen erhalten einen typisierten Registry-Zustand (`absent`, `key-empty`, `key-present`) in der gespeicherten Vergleichsevidenz. Andere Sonderoperationen bleiben fail-closed `Review required`, bis ihre Semantik separat vereinbart und getestet ist.
|
||||||
|
|
||||||
|
Einträge außerhalb der Vereinigung aller erlaubten Kontenlisten weichen zwangsläufig von jeder dieser Varianten ab. Die Vereinigung ist dennoch **kein neuer Sollwert**. Auch innerhalb der Vereinigung ist bei exaktem Vergleich ein Fail möglich, wenn keine einzelne Variante vollständig erfüllt wird. Ein Treffer bei nur einer von mehreren widersprüchlichen Varianten ist kein Gesamt-Pass.
|
||||||
|
|
||||||
|
Eine fehlende oder unlesbare Variante neben vorhandenen Empfehlungen wird nicht herausgefiltert, um Einigkeit herzustellen. Strukturell unvollständige oder nicht erklärbar partielle Baselineanalysen blockieren das Gesamturteil. Partielle Diagnosen werden auf den betroffenen Bereitstellungsmechanismus begrenzt: Eine fehlerhafte Security-Template-Zeile blockiert keinen separat technisch aufgelösten Registry-Policy-Vergleich. Für eine Security-Template-Einstellung bleibt dieselbe Diagnose dagegen ein Blocker. Nicht unterstützte Sektionen besitzen keine vergleichbare Identität und bleiben als Abdeckungslücke am Lauf sichtbar, ohne einen unabhängigen aufgelösten Wert zu entwerten. Ausschließlich fehlende/mehrdeutige ADMX-Metadaten blockieren ebenfalls keine bereits technisch aufgelösten nativen Werte; die Diagnose bleibt erhalten.
|
||||||
|
|
||||||
|
Dasselbe gilt für explizit bekannte, wertunabhängige Metadatenlücken der Baseline: fehlende Domäne, Backupzeit oder Zeitzone. Diese Angaben belegen keine Gerätezielrolle und verändern den gelesenen Sollwert nicht. Unbekannte Diagnosen, fehlerhafte Metadatenstruktur und Identitätskonflikte werden nicht pauschal freigegeben.
|
||||||
|
|
||||||
|
Mehrere widersprüchliche Einträge derselben Einstellung innerhalb derselben Baseline-GPO sind keine unabhängigen Rollenvarianten. Ohne eindeutig reduzierte Empfehlung bleibt dieser Fall Review required, auch wenn das IST von sämtlichen Einzelwerten abweicht.
|
||||||
|
|
||||||
|
Unterschiedliche Baselineempfehlungen sind kein Kunden-GPO-Konflikt. Die bestehende Kundenquellenprüfung für Conflicts und Duplicates wird durch diese Änderung nicht neu definiert. Ihr Nachweis behauptet ebenfalls keine effektive Anwendung. Review required bleibt in Work unter All sichtbar; das verbindliche GPO-Statusmenü bleibt unverändert bei All, Deviation, Passed, Not configured, Conflicts und Duplicates. Eine Risikoakzeptanz verändert dieses technische Urteil nicht.
|
||||||
|
|
||||||
|
Die spätere Nutzerpräzisierung vom 2026-09-16 ersetzt ausschließlich die frühere Interpretation score-neutraler Conflict-/Duplicate-Container: Der Container ist seit Evaluator 1.7.0 ein Diagnosemerkmal, das Policy-Gesamturteil wird separat aufgerollt. Work speichert das Urteil im Status und Conflict/Duplicate zusätzlich in den eigenen Feldern; die Statusfilter lesen diese Achsen getrennt. Dadurch kann dieselbe Zeile sowohl `Deviation` als auch `Conflicts` beziehungsweise `Duplicates` erfüllen, ohne einen zweiten Scorebeitrag zu erzeugen. Historische Evaluationen werden nicht rückwirkend umgedeutet.
|
||||||
|
|
||||||
|
## Nachweis und Historie
|
||||||
|
|
||||||
|
Praezisierung 2026-09-18 (Work-Zeilen): Mehrere Baselinepakete fuer dieselbe technische Einstellung werden in Work zu genau einer Zeile je Projekt, Snapshotaggregation und Computer-/Benutzerbereich zusammengefasst. Diese Zeile verwendet dieselbe Urteilprioritaet wie die bestehenden Policy-Zaehler (Fail, Review required, Pass, Not configured). Die innerhalb eines Pakets gespeicherte Variantenentscheidung bleibt unveraendert; ein dort gemischter Vergleich bleibt Review required. Der Baselinefilter waehlt Mitgliedschaften dieser gemeinsamen Zeile und berechnet ihr Gesamturteil nicht aus der Filterauswahl neu. Einzelne Paketvergleiche, Settingreferenzen und unterschiedliche Sollwerte bleiben im Reader und in Result DB nachvollziehbar; es entsteht kein synthetischer gemeinsamer Sollwert. Quellen werden anhand ihrer exakten Beobachtungsidentitaet dedupliziert, rote Markierungen muessen gegen alle erhaltenen aktuellen Varianten belegt sein. Historische oder nicht verfuegbare Bewertungen erlauben keine solche Markierung.
|
||||||
|
|
||||||
|
Bestehende lokale Notizen zu frueheren Baselinezeilen bleiben im Reader unter `Previous baseline-specific reviews` lesbar. Uebereinstimmende Notizen werden fuer die gemeinsame Work-Zeile uebernommen; unterschiedliche Notizen werden nicht willkuerlich vereinigt. Risikoakzeptanzen werden niemals automatisch auf die gemeinsame Zeile umgehaengt. Die Projektion benoetigt weder Reimport noch Neuauswertung; gespeicherte Vergleiche und Datenbankschema bleiben unveraendert.
|
||||||
|
|
||||||
|
Jedes neue Ergebnis speichert `baselineVariantAssessment` Version 1 mit Prüfgrenze `backup-configuration`, unveränderten Kunden-/Baselinewerten, Baseline-Settingreferenzen, typisierten Einzelvergleichen, Blockierungsdiagnosen und Gesamtentscheidung. Ausgewählte Settingreferenzen und sämtliche Auswahlkandidaten bleiben getrennt; die vorhandenen Evidenzreferenzen führen zu Analyse, Datei-/Hash-/Zeilenherkunft und Kundenbeobachtungen. Die Schreibvalidierung prüft Referenzabdeckung und Konsistenz von Einzelurteilen, Gesamturteil und Klassifikation.
|
||||||
|
|
||||||
|
Ein einzelner aggregierter Baselinewert wird nur bei tatsächlich gemeinsamem Quellwert ausgegeben. Bei unterschiedlichen Werten zeigen Work, Result DB und der JSONL-Export die Einzelvarianten. Empfehlungen kombinieren niemals die Alternativen; vor einer konkreten Änderung muss die passende Vorlage feststehen.
|
||||||
|
|
||||||
|
Evaluator 1.5.0 ersetzte die pauschale Sperre aus 1.4.0 bei unterschiedlichen Baselinewerten. Evaluator 1.6.0 begrenzt globale Parserdiagnosen auf den tatsächlich betroffenen Bereitstellungsmechanismus. Evaluator 1.7.0 vergleicht die erhaltenen Mitgliedswerte eines Kundenkonflikts einzeln und rollt einen nachgewiesenen Fail auf die technische Policy-Einstellung hoch. Evaluator 1.8.0 hält gewöhnliche fehlende Empfehlungen auch in einem ausgewählten analysierten Backupsatz als `Not configured` filterbar und markiert die begrenzte Aussage mit `policy-evaluation-customer-absence-scope-limited`; eine Domänen- oder Geräteabwesenheit wird nicht behauptet. Evaluator 1.9.0 behandelt die oben vereinbarten Delete-, Delete-all-, Create-key- und typisierten Leerwert-Sollzustände als vergleichbare Baselineevidenz. Alte Läufe werden nicht umgeschrieben. 1.3.3 bis 1.8.0 bleiben historisch lesbar; die aktuelle Version hat Vorrang. Data Hub fordert über den bestehenden Versionsvergleich eine explizite Re-evaluation der erhaltenen Eingaben an. Kein Scanner-Reimport, erneuter Upload oder SQL-Schemawechsel allein wegen dieses Bewertungswechsels; fehlende/unlesbare Ursprungsevidenz bleibt gesondert zu klären.
|
||||||
|
|
||||||
|
## Verifikation und Grenzen
|
||||||
|
|
||||||
|
Synthetische Regressionen prüfen zusätzliche und fehlende Konten, Treffer nur einer Variante, Übereinstimmung aller Varianten, vertauschte Kontenreihenfolge, Typunterschiede, unbekannte/fehlende/unlesbare Varianten, explizite und inventarbasierte Kontexte, fehlende Einstellungen bei vollständiger und partieller Erfassung, die vier Registry-Zustandsregeln einschließlich ihrer Work-Urteile, unveränderte Eingaben, serialisierte Evidenz, Schreibvalidierung, historische Versionsauswahl sowie Work-Filter, Guidance und gerenderte Result-DB-Details. Historische Kundendaten werden nicht automatisch neu bewertet. Die Standardprüfung ersetzt keine freigegebene Integration gegen den kanonischen PostgreSQL-Bestand und keinen RSoP-/GP-Results-Nachweis.
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Protected scanner import contracts
|
||||||
|
|
||||||
|
User decision, 2026-10-09: working scanner imports are ASSESSOR crown jewels. This is a Core compatibility boundary in Phase 13A, not another engine or another worker.
|
||||||
|
|
||||||
|
## Release gate
|
||||||
|
|
||||||
|
Run `npm run test:imports` for every importer, shared helper, dependency or Platform change. `npm test` runs it before the complete suite; `npm run build` runs it through `prebuild` before either production build. Any failure blocks release. Direct build commands are not a substitute for this gate.
|
||||||
|
|
||||||
|
The gate runs Vitest in CI mode, without an update option. Changed and missing snapshots fail. It includes all importer and scoring tests and the Node runtime, worker, base64, batch HTTP and server API tests, plus GPO/GPP server ingestion, Registry.pol, security templates, preferences, advanced audit, ADMX and domain ingestion tests. The existing tests for invalid input, safety limits, native score priority and safe fallback stay required.
|
||||||
|
|
||||||
|
## Registered reference inputs
|
||||||
|
|
||||||
|
`tests/importers/import-contracts.json` is the additive format register. Each entry names a tool, format, exact synthetic fixture, variant, filename/MIME, expected approved Work-record count and SHA-256 of UTF-8 fixture text with CRLF normalized to LF. Excel is generated from the protected CSV using the existing XLSX library; it is checked through both runtime paths.
|
||||||
|
|
||||||
|
Initial coverage: 18 variants across all five registered import profiles, using every existing synthetic scanner fixture plus a synthetic Maester multi-tenant fixture. PingCastle HTML/XML; CIS-CAT HTML details, future control sections and summary/outcomes; Purple Knight HTML, JSON and summary CSV; Maester CSV, Excel, legacy HTML, static object literal, current JSON-script declaration, bare JSON-script data, const/let/var and multi-tenant HTML; generic CSV.
|
||||||
|
|
||||||
|
For each reference the test exercises file selection/import, recognition, non-error preview, explicit confirmation of mappings and canonical batch creation in both browser and Node backend. Both paths must produce identical protected results. Committed snapshots retain finding identity, original and normalized values, source locations, field roles, status/outcome/execution state, severity, score, evidence and its finding association, source tables, scanner report sections and environment observations. The scoring projection is protected too. Generated IDs/links, import timestamps, processing versions and mapping-confidence explanations are not application findings and are excluded where applicable; original source objects are never recursively scrubbed.
|
||||||
|
|
||||||
|
The registry test rejects removal of an initial contract and requires a reference for every registered profile and every `.synthetic.` fixture. Baselines are inspectable output, not hashes of opaque canonical objects. Existing structural and semantic assertion tests remain in the gate to avoid accepting an incorrect initial baseline by snapshot alone.
|
||||||
|
|
||||||
|
## Extending and maintaining contracts
|
||||||
|
|
||||||
|
1. Use only invented Dummy data. Build a fixture for the exact report structure; retain original-value edge cases and provenance. Do not read customer reports or copy customer values into a fixture.
|
||||||
|
2. Add a uniquely named register entry and its normalized fixture checksum. Keep existing entries and fixtures. Add exact assertions for the new behavior, including the scoring declaration required by the project guidance.
|
||||||
|
3. Generate only the new case's initial snapshot using a narrowly selected test, inspect its fields and approved count, and run the complete gate without update mode. Never blanket-update existing snapshots to resolve a regression.
|
||||||
|
4. Run the full tests and both builds, and record coverage/limits in the implementation plan. Do not claim support for a vendor format solely because it is listed by the vendor.
|
||||||
|
|
||||||
|
Changes to established expected behavior, removal of a contract or replacement of a reference require the user's explicit agreement for that exact change and current Core consent for affected areas. A failing test must normally be fixed by restoring compatibility or adding another input variant. Test suppression, hash replacement and baseline regeneration are not repairs.
|
||||||
|
|
||||||
|
## Limits and current gaps
|
||||||
|
|
||||||
|
This protects registered examples and existing behavioral suites, not unknown future vendor formats or all possible report values. An exploratory raw Maester JSON object produced the generic profile rather than Maester recognition; that shape has not been promoted to a supported Maester contract or changed in this protection batch. Additional supported structures should be registered incrementally; unsupported structures need a separate additive implementation and regression tests. No external CI provider is configured in this repository; a future CI pipeline must invoke the same mandatory `npm test`/`npm run build` entry points.
|
||||||
|
|
||||||
|
The protection is executable for normal test/build commands and binding for agents and reviewers through AGENTS.md. It cannot prevent someone with repository write access from intentionally changing the gate itself; such a change is Governance/Platform work and remains subject to Core consent and review.
|
||||||
|
|
@ -50,12 +50,93 @@ Jeder Auftrag erhält einen neuen Worker. Nach Ablauf des Zeitlimits wird dieser
|
||||||
|
|
||||||
## Formatspezifische Härtung
|
## Formatspezifische Härtung
|
||||||
|
|
||||||
- HTML: `script`, `style`, eingebettete Frames/Objekte, Formulare, Medien, SVG und weitere aktive Elemente werden entfernt. Ereignisattribute und navigierende oder nachladende Attribute wie `src`, `href`, `srcdoc` und `action` werden vor der Tabellenauswertung gelöscht. Importierter Code wird nicht ausgeführt. Berichte oberhalb von 4 Mi Zeichen sowie DOM-Stacküberläufe wechseln automatisch in eine begrenzte, nicht rekursive Tabellenextraktion.
|
HTML-Performancefix 2026-09-09: Formatadapter 1.1.1, PingCastle-Extractor
|
||||||
|
2.2.1 und CIS-CAT-Extractor 1.3.1 verwenden einen gemeinsamen linearen
|
||||||
|
Attributleser. Lange Attribute ohne Wert und lange Leerraeume duerfen keine
|
||||||
|
quadratischen Suchwiederholungen verursachen. Interne PingCastle-IDs und
|
||||||
|
Ankernamen werden fuer das jeweilige Markup einmal indiziert; Duplikate,
|
||||||
|
Reihenfolge und Offsets bleiben erhalten. Profile und fachliche Mappingvertraege
|
||||||
|
bleiben unveraendert. Der rein synthetische 3.159.338-Byte-Regressionstest
|
||||||
|
brach im alten Produktionsworker nach 30.011 ms ab und laeuft nach dem Fix
|
||||||
|
mit allen 40 Regeln und 40 verknuepften Tabellen in beobachteten 1.590 ms.
|
||||||
|
Die Messung ist kein universelles Laufzeitversprechen. Wiederholbar nach dem
|
||||||
|
Build mit `node tests/server/html-parser-stress.mjs`; der Test behaelt das
|
||||||
|
30-Sekunden-Budget und die 512/64-MiB-Heapgrenzen bei. Die regulaere Suite
|
||||||
|
prueft zusaetzlich begrenzte Zeichenarbeit, Originale und Browser-/Serverparitaet.
|
||||||
|
|
||||||
|
- HTML: `script`, `style`, eingebettete Frames/Objekte, Formulare, Medien, SVG und weitere aktive Elemente werden entfernt. Ereignisattribute und navigierende oder nachladende Attribute wie `src`, `srcdoc` und `action` werden vor der Tabellenauswertung gelöscht; `href` bleibt nur fuer sichere HTTP(S)-Beleglinks erhalten. Importierter Code wird nicht ausgeführt. Berichte oberhalb von 1 Mi Zeichen sowie DOM-Stacküberläufe wechseln automatisch in eine begrenzte, nicht rekursive Tabellenextraktion.
|
||||||
- XML: `DOCTYPE`, `ENTITY`, `SYSTEM` und `PUBLIC` werden vor dem DOM-Parser abgewiesen. Externe Entitäten und lokale Dateireferenzen werden dadurch nicht aufgelöst.
|
- XML: `DOCTYPE`, `ENTITY`, `SYSTEM` und `PUBLIC` werden vor dem DOM-Parser abgewiesen. Externe Entitäten und lokale Dateireferenzen werden dadurch nicht aufgelöst.
|
||||||
- Excel: Verschlüsselte ZIP-Einträge, nicht zugelassene Kompressionsverfahren, ausbrechende Archivpfade, ZIP64 und eingebettete `vbaProject.bin`-Inhalte werden abgewiesen. SheetJS läuft ohne Formel- oder VBA-Auswertung.
|
- Excel: Verschlüsselte ZIP-Einträge, nicht zugelassene Kompressionsverfahren, ausbrechende Archivpfade, ZIP64 und eingebettete `vbaProject.bin`-Inhalte werden abgewiesen. SheetJS läuft ohne Formel- oder VBA-Auswertung.
|
||||||
- CSV-Export: Intern bleiben Originalwerte unverändert. Spreadsheet-orientierte Downloads, Aktionslisten und kopierte Tabellen neutralisieren führende `=`, `+`, `-` oder `@`, damit ein späteres Öffnen keine Formel aktiviert.
|
- CSV-Export: Intern bleiben Originalwerte unverändert. Spreadsheet-orientierte Downloads, Aktionslisten und kopierte Tabellen neutralisieren führende `=`, `+`, `-` oder `@`, damit ein späteres Öffnen keine Formel aktiviert.
|
||||||
- ZIP: Eintragsanzahl, Einzel- und Gesamtgröße sowie Kompressionsverhältnis werden ausschließlich aus dem Zentralverzeichnis vor dem Entpacken geprüft.
|
- ZIP: Eintragsanzahl, Einzel- und Gesamtgröße sowie Kompressionsverhältnis werden ausschließlich aus dem Zentralverzeichnis vor dem Entpacken geprüft.
|
||||||
|
|
||||||
|
## PurpleKnight-Vollständigkeit
|
||||||
|
|
||||||
|
- Native HTML-Zuweisungen werden über den begrenzten statischen Datenleser
|
||||||
|
verarbeitet, nicht evaluiert: maximal 128 Verschachtelungsebenen,
|
||||||
|
1.000.000 Werte und 16 Mi Zeichen je Datenobjekt, 1.000.000 Zeichen je String
|
||||||
|
sowie 250.000 Indikatoren je Bericht. Globale Datei-/Workergrenzen gelten zusätzlich.
|
||||||
|
- Beschädigte Strukturen und fehlende native IDs scheitern explizit; ein
|
||||||
|
angekündigter Ergebnisbestand darf nicht erfolgreich auf null reduziert werden.
|
||||||
|
Mehrfache Zuweisung derselben Berichtswurzel oder desselben Datenobjekts ist
|
||||||
|
ein Fehler. Wiederholte Regel-IDs erhalten eine Warnung und bleiben als
|
||||||
|
getrennte Vorkommen erhalten.
|
||||||
|
- Deklarierte Summen, Statuszahlen, Kategorieverweise und Detailanzahlen werden
|
||||||
|
gegen die gelesenen Einzeleinträge geprüft. Widersprüche markieren den Import
|
||||||
|
als partiell; Originalzähler und vollständige Einträge bleiben unverändert.
|
||||||
|
- Explizite Null-Details sind bei null Ergebnissen gültig. Zeitstempel werden
|
||||||
|
nicht zur Nachahmung eines Excel-Anzeigeformats auf Minuten gekürzt.
|
||||||
|
- Workbook-Summary, Indikatorentabelle und Ergebnisblätter werden gemeinsam
|
||||||
|
geprüft. CSV-Dateiname allein reicht weiterhin nicht zur Werkzeugfreigabe.
|
||||||
|
|
||||||
|
## PingCastle-Strukturtreue
|
||||||
|
|
||||||
|
Ab Profil 1.5.0 / Extractor 2.2.0 bewahrt der XML-Kontext alle Attribute als
|
||||||
|
dekodierte Originalwerte mit `/@Name`-Quellpfad und ausdrücklich leere Blätter
|
||||||
|
als `empty`. Namespace, Nullwert, `0`, `false` und leere Zeichenkette werden
|
||||||
|
nicht gleichgesetzt. Die vorhandene Attribut-Anzeigezeichenkette bleibt erhalten;
|
||||||
|
ältere Adapter ohne strukturierte Attribute erhalten eine Diagnose statt einer
|
||||||
|
mehrdeutigen Rückkonvertierung. Der originale Dateitext bleibt unverändert.
|
||||||
|
|
||||||
|
HTML-Formatadapter 1.1.1 und XML-Formatadapter 1.1.0 liefern diese Struktur beziehungsweise eine
|
||||||
|
attributgrenzenbewusste HTML-Extraktion. Spitze Klammern in zitierten Attributen
|
||||||
|
werden nur für die inerte lexikalische Auswertung geschützt. Skripte werden
|
||||||
|
nicht ausgewertet. Attributbereinigung arbeitet auf vollständigen Attributen,
|
||||||
|
nicht auf scheinbaren `src`-/`href`-Angaben innerhalb eines Tooltips. Ein
|
||||||
|
abgeschnittener End-Tag in einem begrenzten Textausschnitt wird nicht als
|
||||||
|
Zellinhalt übernommen; ein nicht abgeschlossenes zitiertes Attribut im
|
||||||
|
vollständigen Dokument führt zu einer Diagnose.
|
||||||
|
|
||||||
|
Fehlende native Rule IDs werden abgewiesen, doppelte IDs mit einer Warnung
|
||||||
|
gemeldet. Die bestehende erste Darstellung bleibt das Finding. HTML-Trefferzahlen
|
||||||
|
in den Kategorieüberschriften werden gegen eindeutige Regeln abgeglichen;
|
||||||
|
Kataloggrößen sind keine Ergebniszahlen. Abweichungen oder ungültige Zähler
|
||||||
|
markieren die Verarbeitung als `partial`, ohne gültige Regeln zu entfernen.
|
||||||
|
Angekündigte Treffer ohne lesbare Ergebnisse scheitern; ausdrücklich deklarierte
|
||||||
|
Nulltreffer bleiben zulässig. Originalzähler stehen in einem eigenen
|
||||||
|
Berichtsabschnitt, nicht in der Assetzählung. Fehlende Zähler erlauben keinen
|
||||||
|
belegten Vollständigkeitsanspruch. XML-Semantikmapping und Control-Path-JSON
|
||||||
|
bleiben separate Arbeitspakete. Bestehende Importe werden nicht geändert.
|
||||||
|
|
||||||
|
## Statische Maester-Berichtsdaten
|
||||||
|
|
||||||
|
Der Maester-HTML-Extractor 1.1.0 unterstützt JSON und statische Objektliterale in
|
||||||
|
Script-Zuweisungen sowie JSON-Datenblöcken. Der Locator überspringt Zeichenketten,
|
||||||
|
Kommentare und übliche Regex-Literale; er ist kein allgemeiner JavaScript-Parser.
|
||||||
|
Der anschließende Datenparser erlaubt keine Codeausführung und keine dynamischen
|
||||||
|
Ausdrücke. Insbesondere werden Template-Interpolation, Aufrufe, Getter, Spread,
|
||||||
|
berechnete und doppelte Schlüssel abgewiesen. Unbekannte statische Datenfelder
|
||||||
|
bleiben mit ihrem Objektpfad erhalten.
|
||||||
|
|
||||||
|
Pro Datenobjekt gelten 16 Mi Zeichen, 128 Verschachtelungsebenen, eine Million
|
||||||
|
Werte und eine Million Zeichen pro Zeichenkette. Pro Datei werden höchstens 32
|
||||||
|
potenzielle Berichtsobjekte geprüft und 250.000 Tests akzeptiert. Eine
|
||||||
|
Grenzverletzung oder ein beschädigter Test führt zum Fehler, nicht zur stillen
|
||||||
|
Kürzung. Mehrere konkurrierende Berichtswurzeln werden abgewiesen; ein expliziter
|
||||||
|
`Tenants`-Container erhält seine getrennten Mandantenpfade. Deklarierte Summen
|
||||||
|
werden gegen Einzeltests geprüft. Abweichungen bleiben Warnungen mit beiden
|
||||||
|
Zählwerten, ohne die Quelle oder die Ergebnismenge zu verändern.
|
||||||
|
|
||||||
## Vorschau, Persistenz und Protokollierung
|
## Vorschau, Persistenz und Protokollierung
|
||||||
|
|
||||||
Nicht bestätigte Vorschauen liegen höchstens 15 Minuten im Arbeitsspeicher. Der Cache ist auf 10 Batches und 256 MiB Quelldaten begrenzt. Abgelaufene oder verdrängte Vorschauen können nicht persistiert werden. Beim Abschluss werden IDs, SHA-256, Dateiname, Größe und Änderungszeit gegen die serverseitige Vorschau geprüft.
|
Nicht bestätigte Vorschauen liegen höchstens 15 Minuten im Arbeitsspeicher. Der Cache ist auf 10 Batches und 256 MiB Quelldaten begrenzt. Abgelaufene oder verdrängte Vorschauen können nicht persistiert werden. Beim Abschluss werden IDs, SHA-256, Dateiname, Größe und Änderungszeit gegen die serverseitige Vorschau geprüft.
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,26 @@ Start ab; es findet kein stiller Fallback statt.
|
||||||
Ein vollständiges lokales PostgreSQL-Setup, Schema-, Migrations-, Backup- und
|
Ein vollständiges lokales PostgreSQL-Setup, Schema-, Migrations-, Backup- und
|
||||||
Restore-Regeln stehen in `docs/POSTGRESQL.md`.
|
Restore-Regeln stehen in `docs/POSTGRESQL.md`.
|
||||||
|
|
||||||
|
## Kuratierter GPO-Regelkatalog
|
||||||
|
|
||||||
|
Ohne weitere Konfiguration startet die Policy-Evaluation mit dem freigegebenen,
|
||||||
|
fachlich leeren Katalog. Ein lokal kuratierter JSON-Katalog wird nur geladen,
|
||||||
|
wenn Pfad und erwarteter SHA-256 gemeinsam gesetzt sind:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ASSESSOR_POLICY_RULE_CATALOG_PATH=C:\geschuetzter-pfad\policy-rules.json
|
||||||
|
ASSESSOR_POLICY_RULE_CATALOG_SHA256=<64-stelliger SHA-256-Hexwert>
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Server lädt höchstens 5 MiB aus einer regulären Datei, vergleicht den
|
||||||
|
Inhalt vor dem JSON-Parsing mit dem fest konfigurierten Hash und validiert den
|
||||||
|
vollständigen Katalogvertrag einschließlich Kontextüberschneidungen,
|
||||||
|
Comparatoren, Risiko- und Abhängigkeitsregeln. Fehlende Paarparameter,
|
||||||
|
Hashabweichungen, Symlinks, ungültiges UTF-8/JSON oder fachlich ungültige Regeln
|
||||||
|
verhindern den Serverstart. Die Datei wird weder aus dem Netzwerk bezogen noch
|
||||||
|
durch die Anwendung verändert. Jeder Evaluation Run speichert weiterhin den
|
||||||
|
tatsächlich verwendeten vollständigen Katalog für reproduzierbares Reprocessing.
|
||||||
|
|
||||||
Originaldateien werden aktuell nicht als separate Serverartefakte abgelegt. Die
|
Originaldateien werden aktuell nicht als separate Serverartefakte abgelegt. Die
|
||||||
API verarbeitet die Bytes und speichert die daraus erzeugten kanonischen
|
API verarbeitet die Bytes und speichert die daraus erzeugten kanonischen
|
||||||
Fachobjekte. Eine spätere Aufbewahrung von Originalartefakten bleibt eine eigene
|
Fachobjekte. Eine spätere Aufbewahrung von Originalartefakten bleibt eine eigene
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
# Umsetzungsauftrag 2: Auflösung der Maester-Tag-Liste
|
||||||
|
|
||||||
|
Adressat: Codex. Verbindlich und abschließend formuliert.
|
||||||
|
Wo dieser Auftrag schweigt, gelten `AGENTS.md`, `docs/REIMPORT_POLICY.md`,
|
||||||
|
`docs/TOOL_PROFILES.md` und `docs/REPORT_STRATEGY.md` unverändert weiter.
|
||||||
|
Bei einem Widerspruch: **anhalten und melden, nicht eigenmächtig auflösen.**
|
||||||
|
|
||||||
|
Verhältnis zu Auftrag 1 (`docs/REPORT_IMPLEMENTATION_BRIEF.md`): unabhängig
|
||||||
|
umsetzbar. Nur AP5 dieses Auftrags setzt voraus, dass Auftrag 1 abgeschlossen ist.
|
||||||
|
|
||||||
|
## 0. Befund (verifiziert am Client Dummy)
|
||||||
|
|
||||||
|
Der Maester-Export liefert **zwei** als `category` gemappte Spalten:
|
||||||
|
|
||||||
|
- eine Einzelwert-Spalte — im aktuellen Dummy-Lauf `Maester/Entra` (74),
|
||||||
|
`CISA` (73), `ORCA` (67), `EIDSCA` (44), `CIS` (25), `Exposure Management` (10),
|
||||||
|
`Maester/Exchange` (9), `Maester/Teams` (6);
|
||||||
|
- eine **Tag-Listen-Spalte**, die als **eine** Zeichenkette übernommen wird, zum
|
||||||
|
Beispiel `CIS.M365.1.1.1, L1, CIS E3 Level 1, CIS E3, CIS, Security, CIS M365 v5.0.0`
|
||||||
|
oder `MS.AAD, MS.AAD.3.4, CISA.MS.AAD.3.4, CISA, Security, Entra ID P1`.
|
||||||
|
|
||||||
|
Folgen im Bestand:
|
||||||
|
|
||||||
|
- `assessor_result_categories` enthält **1.236** Maester-Einträge gegenüber 27 für
|
||||||
|
Purple Knight und 17 für PingCastle. Nahezu jeder Test erzeugt eine eigene,
|
||||||
|
unteilbare Kategorie. Als Facette ist das unbrauchbar.
|
||||||
|
- Es existieren **zwei Serialisierungen** derselben Liste: die kommagetrennte
|
||||||
|
Form und eine JSON-Array-Textform (`["CIS.M365.1.1.1","L1",…]`).
|
||||||
|
- `scannerResultCategories` in `src/domain/unified-result.ts` zerlegt bereits
|
||||||
|
echte JSON-Arrays, aber **keine** Zeichenketten.
|
||||||
|
- Die daraus erzeugten Kategorien werden **bei der Ingestion persistiert**
|
||||||
|
(`server/postgres/postgres-assessor-repository.ts:222` → `INSERT INTO
|
||||||
|
assessor_finding_occurrence_categories`). Sie sind keine Lesezeit-Projektion.
|
||||||
|
|
||||||
|
Daraus folgt die Einordnung in Abschnitt 2.
|
||||||
|
|
||||||
|
## 1. Ziel
|
||||||
|
|
||||||
|
Eine Tag-Liste wird in ihre einzelnen Tags zerlegt, **ausschließlich dort, wo das
|
||||||
|
Profil es ausdrücklich erklärt hat**. Damit werden `CISA`-SCuBA-IDs
|
||||||
|
(`MS.AAD.3.4`), CIS-M365-IDs, Benchmark-Versionen und die vorausgesetzte
|
||||||
|
Lizenzstufe (`Entra ID Free` / `P1` / `P2`) als einzeln auswähl- und zählbare
|
||||||
|
Kategorien verfügbar. Es wird nichts geraten und nichts interpretiert.
|
||||||
|
|
||||||
|
## 2. Reimport-Einordnung (bindend)
|
||||||
|
|
||||||
|
Die erzeugten Kategorien sind persistierte Fakten, nicht Darstellung. Diese
|
||||||
|
Änderung fällt damit unter „field-role mapping … or canonical projection" in
|
||||||
|
`docs/REIMPORT_POLICY.md` und ist **reimport-pflichtig**:
|
||||||
|
|
||||||
|
- append-only neuer Eintrag in `PROCESSING_VERSION_MANIFESTS`;
|
||||||
|
- `reimportRequired: true`;
|
||||||
|
- bestehende Maester-Importe werden als reimport-bedürftig markiert;
|
||||||
|
- historische Einträge und bereits persistierte Werte werden **nicht** umgeschrieben.
|
||||||
|
|
||||||
|
Diese Einordnung ist nicht verhandelbar. Sie darf nicht als presentation-only
|
||||||
|
umdeklariert werden, auch nicht, um einen Reimport zu vermeiden.
|
||||||
|
|
||||||
|
## 3. Arbeitspakete
|
||||||
|
|
||||||
|
### AP1 — Deklarative Trennzeichen im Profilvertrag
|
||||||
|
|
||||||
|
**Datei:** `src/domain/tool-profile-definition.ts`
|
||||||
|
|
||||||
|
Optional auf `ToolProfileDefinition`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
categoryLists?: {
|
||||||
|
version: string;
|
||||||
|
/** Exakte Trennzeichen. Leer oder fehlend bedeutet: nicht trennen. */
|
||||||
|
separators: string[];
|
||||||
|
/** Warum diese Quelle Listen liefert und welche Spalte sie trägt. */
|
||||||
|
reason: string;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Bindend:
|
||||||
|
|
||||||
|
- Fehlt das Feld, bleibt das heutige Verhalten **bit-identisch**. Kein Profil
|
||||||
|
wird implizit betroffen.
|
||||||
|
- Nur `src/profiles/maester/maester-definition.ts` deklariert es:
|
||||||
|
`separators: [',', ';', '|']`, mit einer Begründung, die die Tag-Spalte benennt.
|
||||||
|
- Purple Knight, PingCastle, CIS-CAT und Generic deklarieren es **nicht** und
|
||||||
|
erhalten **keinen** Manifest-Eintrag aus diesem Auftrag.
|
||||||
|
- `tests/profiles/declarative-profiles.test.ts` prüft die Deklaration mit:
|
||||||
|
Trennzeichen sind nicht leer, paarweise verschieden, einzelne Zeichen, und
|
||||||
|
`reason` ist nicht leer.
|
||||||
|
|
||||||
|
### AP2 — Zerlegung in `scannerResultCategories`
|
||||||
|
|
||||||
|
**Datei:** `src/domain/unified-result.ts`
|
||||||
|
|
||||||
|
`scannerResultCategories` erhält den Profilvertrag als zusätzliche Eingabe und
|
||||||
|
zerlegt nach dieser Reihenfolge:
|
||||||
|
|
||||||
|
1. Ist `normalizedValue` ein echtes JSON-Array: bestehendes Verhalten von
|
||||||
|
`categoryStrings`, ein Eintrag je Element. **Unverändert.**
|
||||||
|
2. Ist `normalizedValue` eine Zeichenkette, die mit `[` beginnt und über
|
||||||
|
`JSON.parse` zu einem **Array ausschließlich aus Zeichenketten** wird: diese
|
||||||
|
Elemente verwenden. Schlägt das Parsen fehl oder ist das Ergebnis kein solches
|
||||||
|
Array, gilt Schritt 3. Kein `eval`, keine Reparatur, keine Teilauswertung.
|
||||||
|
3. Ist für dieses Profil `categoryLists` deklariert: an den deklarierten
|
||||||
|
Trennzeichen zerlegen. Sonst: Zeichenkette unverändert übernehmen.
|
||||||
|
|
||||||
|
Anschließend für alle Wege gleich: trimmen, leere Werte verwerfen, Duplikate
|
||||||
|
unter Beibehaltung des **ersten** Vorkommens entfernen.
|
||||||
|
|
||||||
|
Reihenfolge und `isPrimary`:
|
||||||
|
|
||||||
|
- Reihenfolge ist: `definition.category`, danach die `category`-Mappings in ihrer
|
||||||
|
bestehenden Reihenfolge, darin die Tags in ihrer Reihenfolge in der Quelle.
|
||||||
|
- `isPrimary` bleibt genau der **erste** Eintrag dieser Gesamtreihenfolge.
|
||||||
|
|
||||||
|
Originalwerte:
|
||||||
|
|
||||||
|
- Die `fieldMappings` der Occurrence bleiben **unverändert**. Die ungeteilte
|
||||||
|
Original-Liste bleibt dort vollständig erhalten und ist weiterhin die
|
||||||
|
Provenienz. Es wird **kein** zusätzlicher Kategorieeintrag angelegt, der die
|
||||||
|
ungeteilte Liste wiederholt.
|
||||||
|
|
||||||
|
Diagnostik:
|
||||||
|
|
||||||
|
- Liefert ein nicht leerer Quellwert nach der Zerlegung null Tags, wird eine
|
||||||
|
Import-Diagnose erzeugt. Der Wert wird nicht still verworfen.
|
||||||
|
|
||||||
|
### AP3 — Processing-Manifest
|
||||||
|
|
||||||
|
**Dateien:** `src/profiles/maester/maester-definition.ts`,
|
||||||
|
`src/domain/processing-compatibility.ts`
|
||||||
|
|
||||||
|
- Maester `version` `1.1.0` → `1.2.0`, `processing.mappingVersion` `1.1.0` → `1.2.0`.
|
||||||
|
- `normalizationVersion` und `presentationVersion` bleiben unverändert.
|
||||||
|
- Neuer, **angehängter** Manifest-Eintrag: `profileId: 'maester'`,
|
||||||
|
`profileVersion: '1.2.0'`, `mappingVersion: '1.2.0'`,
|
||||||
|
`changeClass: 'compatible'`, `reimportRequired: true`,
|
||||||
|
`affectedDimensions: ['mapping', 'presentation']`, `current: true`.
|
||||||
|
Der bisherige `1.1.0`-Eintrag wird auf `current: false` gesetzt und sonst
|
||||||
|
**nicht angefasst**.
|
||||||
|
- `affectedDimensions` enthält **nicht** `environment-context`; die
|
||||||
|
Kontexterfassung ändert sich nicht.
|
||||||
|
- `compatibilityKey` bleibt `maester-canonical-v1`.
|
||||||
|
- `CURRENT_ASSESSOR_PROCESSING_VERSION` wird **nicht** verändert, sofern dafür
|
||||||
|
keine gesonderte Entscheidung vorliegt.
|
||||||
|
|
||||||
|
### AP4 — Konsistenz der bestehenden Ansichten
|
||||||
|
|
||||||
|
Die Zerlegung erhöht die Kategorie-Kardinalität je Datensatz. Sicherzustellen ist:
|
||||||
|
|
||||||
|
- **Work `Category`-Facette:** bleibt funktionsfähig, zählt Tags statt Listen.
|
||||||
|
Die GPO-Kontextregeln aus `AGENTS.md` bleiben unverändert — `Category` bleibt
|
||||||
|
im GPO-Kontext permanent und steht direkt nach `Result Status`.
|
||||||
|
- **Security Domain:** die Zuordnung darf sich für einen unveränderten Datensatz
|
||||||
|
**nicht** ändern. Die Tag-Tokens sind dieselben, nur getrennt abgelegt.
|
||||||
|
Pflichttest mit synthetischem Fixture.
|
||||||
|
- **Pathfinder:** `buildFindingPathItems` in
|
||||||
|
`src/features/mitre-graph/mitre-graph.ts` zerlegt Kategoriefelder heute an
|
||||||
|
`/[;|]+/`, also **nicht** am Komma. Record-Pfad und Unified-Pfad müssen nach
|
||||||
|
dieser Änderung dieselbe Tag-Menge liefern. Abweichungen sind zu beheben,
|
||||||
|
nicht zu tolerieren.
|
||||||
|
- **Performance:** es gelten die Regeln aus `AGENTS.md`, Abschnitt *Navigation and
|
||||||
|
rendering performance*. Facetten werden weiterhin in einem gemeinsamen Durchlauf
|
||||||
|
gezählt; die Prüfung der Kategorie-Eignung bricht beim Überschreiten ihrer
|
||||||
|
Kardinalitätsgrenze ab, exakte Zähler eignungsfähiger Facetten bleiben erhalten.
|
||||||
|
|
||||||
|
### AP5 — Report-Abschnitt „Framework-Abdeckung"
|
||||||
|
|
||||||
|
**Voraussetzung:** Auftrag 1 ist abgeschlossen.
|
||||||
|
|
||||||
|
**Datei:** `src/exports/management-overview-report.ts`,
|
||||||
|
Renderer-Version auf `'1.2.0'`.
|
||||||
|
|
||||||
|
Neuer Abschnitt nach „Maßnahmenbündel", ausschließlich aus den nun einzeln
|
||||||
|
vorliegenden Kategorien:
|
||||||
|
|
||||||
|
- Abdeckung je erkanntem Framework-Präfix (`CIS.M365`, `MS.AAD` bzw. `CISA`),
|
||||||
|
jeweils erfüllt / offen / nicht geprüft, wobei `not_run` **eigene** Zeile ist
|
||||||
|
und niemals als erfüllt gezählt wird;
|
||||||
|
- Abweichungen, die eine Lizenzstufe voraussetzen, gruppiert nach der von der
|
||||||
|
Quelle genannten Stufe (`Entra ID Free`, `P1`, `P2`).
|
||||||
|
|
||||||
|
Bindend:
|
||||||
|
|
||||||
|
- Die Präfixe werden aus den Tags **gelesen**, nicht erfunden. Ein Tag ohne
|
||||||
|
erkanntes Präfix bleibt ungruppiert und wird als solcher ausgewiesen.
|
||||||
|
- Es entsteht **keine** Konformitätsaussage zu NIS2, BSIG oder KRITIS und
|
||||||
|
**keine** Kostenrechnung. Die Lizenzgruppierung ist eine Voraussetzungsangabe
|
||||||
|
der Quelle, keine Empfehlung und keine Beschaffungsaussage.
|
||||||
|
- Unbekanntes ist ein Geviertstrich, niemals `0`.
|
||||||
|
|
||||||
|
### AP6 — Reimport-Anzeige verifizieren
|
||||||
|
|
||||||
|
Ohne Codeänderung, falls die Ableitung greift: Data Hub muss einen Maester-Lauf
|
||||||
|
mit Profilversion `1.1.0` nach dieser Änderung als reimport-bedürftig ausweisen,
|
||||||
|
mit den betroffenen Dimensionen. Greift die Ableitung nicht, ist die Ursache zu
|
||||||
|
beheben — nicht das Manifest anzupassen.
|
||||||
|
|
||||||
|
Beim Reimport gilt der Preservation-Contract aus `docs/REIMPORT_POLICY.md`
|
||||||
|
unverändert: vollständige, kundeneigene Review-Objekte einschließlich
|
||||||
|
Accepted-Risk-Daten überleben über die eindeutige Scanner-/Regel-/Asset-Identität;
|
||||||
|
bei fehlender oder mehrdeutiger Zuordnung bleibt die alte Review am bisherigen
|
||||||
|
Lauf und der Ersatz meldet einen ungelösten Stand.
|
||||||
|
|
||||||
|
## 4. Harte Verbote
|
||||||
|
|
||||||
|
1. Keine Zerlegung ohne ausdrückliche Profil-Deklaration. Kein globales
|
||||||
|
Komma-Splitting, keine Heuristik über Feldnamen, keine Erkennung „sieht nach
|
||||||
|
Liste aus".
|
||||||
|
2. Keine Auswertung von JSON jenseits einer strikten `JSON.parse`-Prüfung auf ein
|
||||||
|
Array von Zeichenketten. Kein `eval`, keine Teilreparatur defekter Werte.
|
||||||
|
3. Keine Änderung, Normalisierung, Groß-/Kleinschreibung-Anpassung oder
|
||||||
|
Übersetzung der einzelnen Tags. Sie werden unverändert übernommen.
|
||||||
|
4. Keine Umdeklaration dieser Änderung als presentation-only und kein Verzicht
|
||||||
|
auf die Reimport-Markierung.
|
||||||
|
5. Kein Umschreiben historischer Manifest-Einträge, Occurrences,
|
||||||
|
`fieldMappings` oder bestehender Kategoriezeilen.
|
||||||
|
6. Keine Ableitung einer Severity, Priorität oder eines Rangs aus einem Tag —
|
||||||
|
auch nicht aus `L1`, `L2` oder einer Lizenzstufe. Die deklarierte
|
||||||
|
`priorityBasis` von Maester bleibt `severity` und unverändert.
|
||||||
|
7. Keine Aussage, dass ein Framework erfüllt sei, solange Tests `not_run` sind.
|
||||||
|
8. Keine echten Kunden-, Mandanten- oder Scan-Inhalte in Tests, Fixtures, Logs
|
||||||
|
oder Commit-Nachrichten. Ausschließlich synthetische Fixtures.
|
||||||
|
|
||||||
|
## 5. Tests (Pflicht, synthetisch)
|
||||||
|
|
||||||
|
| Paket | Testdatei | Muss nachweisen |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| AP1 | Erweiterung `tests/profiles/declarative-profiles.test.ts` | Maester deklariert `categoryLists` gültig; alle übrigen Profile deklarieren es nicht |
|
||||||
|
| AP2 | `tests/domain/unified-result-category-lists.test.ts` | Kommaliste wird zerlegt; JSON-Array-Text wird zerlegt; defekter JSON-Text fällt auf die Zeichenkettenregel zurück; ein Profil ohne Deklaration liefert bit-identisch das bisherige Ergebnis; Trim, Leerwerte, Duplikate, Reihenfolge und `isPrimary`; `fieldMappings` bleiben unverändert; nicht leerer Wert ohne Tags erzeugt eine Diagnose |
|
||||||
|
| AP3 | Erweiterung `tests/domain/processing-compatibility.test.ts` | `1.2.0` ist `current` und `reimportRequired`; `1.1.0` bleibt inhaltlich unverändert und ist nicht mehr `current`; die Liste bleibt append-only |
|
||||||
|
| AP4 | `tests/features/category-facet-consistency.test.ts` | Security-Domain-Zuordnung für ein unverändertes Fixture identisch vor und nach der Zerlegung; Record- und Unified-Pfad liefern dieselbe Tag-Menge; Facettenzählung bleibt in einem Durchlauf und respektiert die Kardinalitätsgrenze |
|
||||||
|
| AP5 | Erweiterung `tests/exports/management-overview-report.test.ts` | Abschnitt vorhanden; `not_run` eigene Zeile und nie als erfüllt; unbekanntes Präfix ausgewiesen; keine Konformitäts- oder Kostenaussage im Output |
|
||||||
|
| AP6 | Verhaltenstest der Readiness-Ableitung | Ein Lauf mit Profilversion `1.1.0` wird als reimport-bedürftig mit den betroffenen Dimensionen gemeldet |
|
||||||
|
|
||||||
|
## 6. Verifikation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Beide müssen grün sein. Zusätzlich ist zu belegen, dass alle bestehenden Profil-,
|
||||||
|
Import- und Result-DB-Tests ohne inhaltliche Änderung weiter bestehen.
|
||||||
|
|
||||||
|
## 7. Nicht Teil dieses Auftrags
|
||||||
|
|
||||||
|
- Zerlegung von Kategorie- oder Tag-Werten anderer Profile. Purple Knight,
|
||||||
|
PingCastle, CIS-CAT und Generic bleiben unangetastet und erhalten keinen
|
||||||
|
Manifest-Eintrag.
|
||||||
|
- Ein kuratiertes NIS2-, BSIG- oder KRITIS-Mapping. Das wäre ein eigener,
|
||||||
|
versionierter Katalog und eine eigene Entscheidung.
|
||||||
|
- Ein quellenübergreifender Framework-Abgleich zwischen CIS-M365 (Maester) und
|
||||||
|
den CIS-Windows-Benchmarks (CIS-CAT). Es sind verschiedene Benchmarks; eine
|
||||||
|
Zusammenführung wäre eine Erfindung.
|
||||||
|
- Änderungen an der Maester-Erkennung, am Tabellen- oder Statusmapping, an der
|
||||||
|
`securityScoring`- oder `timeslotComparison`-Deklaration.
|
||||||
|
|
||||||
|
## 8. Abschluss
|
||||||
|
|
||||||
|
Nach Abschluss ist `docs/REIMPORT_POLICY.md` um eine datierte Klarstellung zu
|
||||||
|
ergänzen, die festhält: Maester `1.1.0` → `1.2.0`, Mapping `1.1.0` → `1.2.0`,
|
||||||
|
Grund die Zerlegung der deklarierten Tag-Liste, Reimport erforderlich, betroffene
|
||||||
|
Dimensionen `mapping` und `presentation`, keine SQL-Migration, keine Änderung an
|
||||||
|
Erkennung, Status, Outcome, Severity oder IOE. `docs/REPORT_STRATEGY.md` erhält
|
||||||
|
den Change-Log-Eintrag für den Abschnitt „Framework-Abdeckung" und die
|
||||||
|
Renderer-Version `1.2.0`. Kein Meilenstein wird als erfüllt markiert, bevor seine
|
||||||
|
Akzeptanzkriterien verifiziert sind.
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Enterprise ATT&CK catalog in ASSESSOR
|
||||||
|
|
||||||
|
Pathfinder uses the compact, versioned default catalog at `src/assets/data/mitre-enterprise-v19.2.json`. It was generated from MITRE's official Enterprise ATT&CK STIX 2.1 bundle:
|
||||||
|
|
||||||
|
- Source: https://github.com/mitre-attack/attack-stix-data/blob/master/enterprise-attack/enterprise-attack-19.2.json
|
||||||
|
- Source SHA-256: `DC1639CAA5501D720E280CF1CBD8FBE009884A0C9B3E6E9ED9D0C25166C3D8F4`
|
||||||
|
- Collection version: 19.2; 15 active tactics, 697 active techniques and subtechniques, 1,057 active groups, campaigns and software entries.
|
||||||
|
|
||||||
|
To refresh the repo asset, download a **version-pinned** official Enterprise STIX bundle and run `node --experimental-strip-types scripts/update-mitre-asset.mts <path-to-bundle>`. Review the generated version, counts and graph tests before changing the default import. The converter follows the STIX matrix tactic order and omits revoked or deprecated objects and relationships.
|
||||||
|
|
||||||
|
The Data Hub has three actions:
|
||||||
|
|
||||||
|
- **Update from MITRE** fetches the official latest Enterprise STIX bundle from MITRE's `attack-stix-data` repository, validates and converts it, and activates it only when its collection version is newer than the active shared catalog. The compact result is saved atomically at `<ASSESSOR_ARTIFACT_ROOT>/reference-data/mitre-enterprise.json`, outside the public web root. The backend serves that catalog after restart, and Pathfinder receives an immediate update event in the current browser. The browser's custom override is cleared after a successful update check.
|
||||||
|
- **Download from MITRE** fetches the original latest Enterprise STIX JSON from that same MITRE source and offers it as `enterprise-attack-v<version>.json`. Downloading does not activate or persist it. The backend proxy keeps the browser same-origin and validates the source before sending it. The source is capped at 80 MiB and the request times out after 120 seconds.
|
||||||
|
- **Import JSON file** is the manual fallback. It accepts an official Enterprise STIX bundle or the compact ASSESSOR JSON format. A custom matrix is stored in browser `localStorage`, overrides the shared catalog only in that browser, and is shown as `Custom v<version>`. **Use active catalog** removes this override. The open Pathfinder graph updates after either action.
|
||||||
|
|
||||||
|
The shared catalog is read through `GET /api/v1/reference-data/mitre-attack`; the guarded `POST /api/v1/reference-data/mitre-attack/refresh` updates it, and `GET /api/v1/reference-data/mitre-attack/source` sends the latest validated original file. No PostgreSQL schema or existing assessment data is changed. MITRE availability is required for update and download; the bundled v19.2 catalog and manual import remain available offline. A shared catalog update does not remove another browser's deliberate local override.
|
||||||
|
|
||||||
|
## Reference recognition (2.8.0)
|
||||||
|
|
||||||
|
ATT&CK updates now also retain the complete versioned recognition catalog, including
|
||||||
|
historical entries, alongside the active graph in the same atomic file. Stored scanner
|
||||||
|
reference runs remain frozen until the explicit client action **Recognize references**.
|
||||||
|
The bundled D3FEND catalog and reference storage are documented in
|
||||||
|
`docs/REFERENCE_RECOGNITION.md`. Migration 023 adds this independent derived storage;
|
||||||
|
the update/download buttons themselves do not rewrite assessment data. Manual browser
|
||||||
|
matrix overrides remain graph fallbacks and do not select backend recognition catalogs.
|
||||||
|
|
@ -77,6 +77,13 @@ ein Rollback ohne Teilimport.
|
||||||
Migration `002_import_fingerprints.sql` ergänzt `assessor_import_fingerprints`. Der SHA-256-Wert ist Primärschlüssel; gespeichert werden außerdem Dateiname, Größe, letzter Dateiänderungszeitpunkt, erster und letzter Importzeitpunkt sowie ein Wiederholungszähler.
|
Migration `002_import_fingerprints.sql` ergänzt `assessor_import_fingerprints`. Der SHA-256-Wert ist Primärschlüssel; gespeichert werden außerdem Dateiname, Größe, letzter Dateiänderungszeitpunkt, erster und letzter Importzeitpunkt sowie ein Wiederholungszähler.
|
||||||
|
|
||||||
Der Abschluss eines bestätigten Batchimports verwendet `commitCanonicalImport`. Der PostgreSQL-Adapter schreibt neues Projekt, Assessment, Scan-Lauf, kanonische Importdaten und Fingerprints auf derselben Poolverbindung innerhalb derselben Transaktion. Die Repository-Revision wird dabei genau einmal erhöht. Ein Validierungs-, Auswahl- oder Datenbankfehler erzeugt ein vollständiges Rollback.
|
Der Abschluss eines bestätigten Batchimports verwendet `commitCanonicalImport`. Der PostgreSQL-Adapter schreibt neues Projekt, Assessment, Scan-Lauf, kanonische Importdaten und Fingerprints auf derselben Poolverbindung innerhalb derselben Transaktion. Die Repository-Revision wird dabei genau einmal erhöht. Ein Validierungs-, Auswahl- oder Datenbankfehler erzeugt ein vollständiges Rollback.
|
||||||
|
## Abgeleitete Result DB
|
||||||
|
|
||||||
|
`assessor_result_db_rows` und `assessor_result_db_state` (Migration 024) sind abgeleitete Daten: Das
|
||||||
|
Backend baut sie unter einer Advisory-Sperre aus den kanonischen Tabellen neu auf, sobald sich die
|
||||||
|
Repository-Revision oder eine Policy-/Waechterversion aendert. Sie werden nie von Hand bearbeitet und
|
||||||
|
zaehlen die Repository-Revision nicht hoch.
|
||||||
|
|
||||||
## Backup
|
## Backup
|
||||||
|
|
||||||
Vor Migrationen, Updates und größeren Löschvorgängen ist ein logisches Backup
|
Vor Migrationen, Updates und größeren Löschvorgängen ist ein logisches Backup
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
# Phase 13D: ATT&CK and D3FEND references
|
||||||
|
|
||||||
|
## Scope and semantics
|
||||||
|
|
||||||
|
The initial implementation recognizes Enterprise ATT&CK technique/sub-technique IDs,
|
||||||
|
tactic IDs and unambiguous exact catalog titles, plus D3FEND defensive-technique IDs
|
||||||
|
and unambiguous exact titles. It processes retained `OriginalField` values using one
|
||||||
|
framework-specific engine, independently of the scanner. A framework-named header
|
||||||
|
declares its context; IDs in other fields are merely **mentioned**. Titles are only
|
||||||
|
matched in declared context. Whitespace and case normalization is allowed; fuzzy
|
||||||
|
matching, translations and LLM classification are not.
|
||||||
|
|
||||||
|
`active`, `deprecated`, `revoked` and `not-in-catalog` describe the lookup result.
|
||||||
|
An unknown ID is not claimed to be invalid. Placeholder text creates no reference.
|
||||||
|
An ambiguous title (for example PowerShell in current and historical ATT&CK entries)
|
||||||
|
does not select either ID. Unmatched original content remains available in the source
|
||||||
|
fields. No ATT&CK-to-D3FEND defensive inference or automatic correlation is performed.
|
||||||
|
|
||||||
|
The derived references never enter `FindingDefinition.frameworkReferences`, the
|
||||||
|
comparison identity engine, source guidance, severity, verdict, score, Security Domain,
|
||||||
|
review state or the external Result DB disclosure. Existing report-correlation semantics
|
||||||
|
remain separate. A shared reference describes a shared topic, not a proven common cause. Explicit user decision 2026-09-30: the context layer (Phase 13E) groups findings whose scanner declares the same active ATT&CK ID and shows exactly this shared declared topic, never a common cause or attack path; mentioned references stay out. Explicit user decision 2026-09-30: declared D3FEND countermeasures are grouped the same way, separately from ATT&CK, as the same countermeasure according to the scanners.
|
||||||
|
|
||||||
|
## Catalogs and reproducibility
|
||||||
|
|
||||||
|
- `attack-reference-v19.2.json`: 873 technique/tactic entries, including historical
|
||||||
|
deprecated and revoked entries. Source: MITRE's version-pinned Enterprise STIX 19.2.
|
||||||
|
- `d3fend-reference-v1.6.0.json`: 272 defensive technique entries (`D3-…`).
|
||||||
|
Source: <https://d3fend.mitre.org/ontologies/d3fend/1.6.0/d3fend.json>.
|
||||||
|
D3A/D3E entities and inferred cross-framework relationships are outside this slice.
|
||||||
|
- Each asset contains its original download URL, source SHA-256 and MITRE copyright/
|
||||||
|
license notice. Snapshot SHA-256 covers the complete canonical JSON catalog used by
|
||||||
|
recognition, including entries, version, provenance and notice.
|
||||||
|
- `scripts/update-reference-assets.mts` regenerates the compact assets from official
|
||||||
|
source files. Recognition and reprocessing require no external network.
|
||||||
|
- The existing **Update from MITRE** downloads ATT&CK and atomically stores both its
|
||||||
|
graph and complete recognition catalog. An equal-version refresh can fill in missing
|
||||||
|
recognition metadata from an older ASSESSOR installation. A central update makes a
|
||||||
|
new catalog available; it does not silently replace previous reference runs.
|
||||||
|
- D3FEND is bundled at 1.6.0 in this initial release; no D3FEND runtime update UI has
|
||||||
|
been added. **Download from MITRE** and manual **Import JSON file** remain ATT&CK
|
||||||
|
matrix actions. The manual browser override affects the displayed matrix and transient
|
||||||
|
previews; stored recognition always uses the backend's versioned catalogs.
|
||||||
|
|
||||||
|
## Storage and execution
|
||||||
|
|
||||||
|
Migration `023_reference_recognition` adds immutable public `assessor_reference_catalogs`,
|
||||||
|
append-only client/assessment/scan-bound `assessor_reference_runs` and their catalog
|
||||||
|
membership table. 021/022 are already reserved in the canonical local datastore by
|
||||||
|
timeslot-comparison and obligation work; those migrations are not modified here.
|
||||||
|
No separate client databases are created in this phase.
|
||||||
|
|
||||||
|
The normal browser batch-import transaction includes its reference run and catalog
|
||||||
|
snapshots. A recognition failure therefore rolls back the batch instead of reporting a
|
||||||
|
partially completed import. The low-level staging API and pre-existing scans can be
|
||||||
|
indexed with the explicit client action below; the UI never silently fabricates a stored
|
||||||
|
run for them.
|
||||||
|
|
||||||
|
**Data Hub → client → Catalog references → Update references** processes all current
|
||||||
|
scanner runs in that client across its timeslots, from their retained fields. The button
|
||||||
|
shows available catalog versions and pending scan count. Its guarded endpoint is
|
||||||
|
`POST /api/v1/projects/:projectId/references/recognize`; status is available through
|
||||||
|
`GET /api/v1/projects/:projectId/references`. Concurrent requests for one client share
|
||||||
|
one job; unchanged input, catalogs and recognizer version are idempotent and do not
|
||||||
|
bump repository revision. Individual scans are committed atomically; after interruption,
|
||||||
|
retry skips scans already completed. This initial synchronous route is not a background
|
||||||
|
job scheduler; very large clients can require a later queued implementation.
|
||||||
|
|
||||||
|
Each result retains occurrence and file identity, source file hash if available, source
|
||||||
|
position, field key/label/path, JSON value pointer, exact matched substring and offsets.
|
||||||
|
Offsets are zero-based UTF-16 indices into the retained `rawValue`, when present, or the
|
||||||
|
individual string leaf of `value`; the end is exclusive. Each run retains recognizer
|
||||||
|
version, timestamp, source-input hash and exact catalog hashes. Older runs remain
|
||||||
|
exportable. Project export includes every referenced compact catalog snapshot, allowing
|
||||||
|
reference history to be read without the central catalog. Original full STIX/JSON-LD
|
||||||
|
downloads are not needed to replay recognition against these exact compact inputs.
|
||||||
|
|
||||||
|
## Presentation and boundaries
|
||||||
|
|
||||||
|
The Data Hub client overview includes reference readiness across every current timeslot.
|
||||||
|
Pending recognition or an outdated recognizer/catalog produces the existing yellow
|
||||||
|
update point and highlighted **Manage**, including before selecting the client. An
|
||||||
|
unavailable status remains a review notice; checking is neutral and never claims readiness.
|
||||||
|
An actual recognition failure is red. Existing scanner/GPO failures retain priority.
|
||||||
|
Status summaries load separately from overview metadata with at most three concurrent
|
||||||
|
requests, refresh on import/data changes and successful catalog updates, and reject stale
|
||||||
|
responses after a newer panel result. There is no added polling or persistent job state.
|
||||||
|
|
||||||
|
**Catalog references** is in the left client navigation immediately above **Timeslots**,
|
||||||
|
including while editing client master data. By explicit user decision on 2026-09-26,
|
||||||
|
**Update references** is red when work or retry is needed and yellow otherwise.
|
||||||
|
The generic explanation is omitted. The first line says **Please update**, **Up to date**
|
||||||
|
with **No action needed.**, or **Update successful** after execution and a fresh status
|
||||||
|
check confirms no remaining work. ATT&CK and D3FEND versions stay visible. Empty clients,
|
||||||
|
loading and failures use explicit separate messages and never claim success.
|
||||||
|
The section starts collapsed for a current client and opens automatically for pending
|
||||||
|
work or a failed status/update. Small green dots precede the heading and confirmed
|
||||||
|
current/success state; required action uses orange and unknown/empty/processing states
|
||||||
|
stay neutral. Catalog versions remain outside the disclosure and visible while collapsed.
|
||||||
|
After updating, the success message remains open; manual and keyboard toggling is retained.
|
||||||
|
Successful recognition updates the client card and its loaded workspace. A failed status
|
||||||
|
request offers **Retry status**. The navigation scrolls as one area below the client header
|
||||||
|
so the added controls do not make timeslots inaccessible on short screens.
|
||||||
|
|
||||||
|
Pathfinder consumes persisted active references and frozen tactic membership, with
|
||||||
|
declared ATT&CK mapped as native and mentioned ATT&CK mapped as derived. Unknown,
|
||||||
|
revoked and deprecated matches are visible in the Finding Reader but do not form active
|
||||||
|
graph relations. Unindexed stored occurrences remain unmapped until explicitly indexed.
|
||||||
|
Transient, unsaved previews use the same pure recognizer with their selected matrix.
|
||||||
|
Original raw references remain readable. The Finding Reader adds a provenance disclosure
|
||||||
|
after explanatory content; its Problem/Next step, Results and work tracking are unchanged.
|
||||||
|
|
||||||
|
Broader 13D work (other frameworks, profile-declared ambiguous contexts, standalone
|
||||||
|
reference search/filtering, technical identity resolution, correlation UI, catalog-version
|
||||||
|
selection and per-client database migration) remains planned separately.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Synthetic domain, full batch HTTP import, client-button/Reader interaction and Pathfinder
|
||||||
|
tests cover declared/mentioned evidence, unknown IDs, exact offsets, nested values,
|
||||||
|
historical/ambiguous entries, client isolation, idempotence, source immutability and
|
||||||
|
catalog updates without retrospective changes. The opt-in PostgreSQL test uses only
|
||||||
|
generated synthetic IDs in the canonical datastore and deletes that exact test project.
|
||||||
|
No tenant report contents are inspected.
|
||||||
|
|
||||||
|
Verified release: **2.8.0**. Both production builds, UI/API version contract, local-only
|
||||||
|
and design checks pass. Synthetic Light/Dark checks include a 480px Reader. The full
|
||||||
|
suite has 1,569 passing and 14 skipped tests, with six pre-existing failures across
|
||||||
|
GPO evaluation visibility (2), Security Domain navigation (1), AppLocker markup (1),
|
||||||
|
Result DB scope source-text expectations (1) and dashboard cache source-text expectations
|
||||||
|
(1). Their root causes are not all resolved by this separate reference implementation.
|
||||||
|
The PostgreSQL integration check additionally verifies source-complete history export
|
||||||
|
after a synthetic scan replacement. No existing customer scans were reprocessed.
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Reimport policy
|
||||||
|
|
||||||
|
ASSESSOR treats a reimport as a new, auditable interpretation of the unchanged original scanner source. The previous scan run remains stored and is linked to its replacement. Scanner-derived records may change; client-owned work must survive.
|
||||||
|
|
||||||
|
## When a reimport is required
|
||||||
|
|
||||||
|
A processing-profile version must be added and existing affected imports must be marked for reimport whenever a change can alter one or more of these persisted facts:
|
||||||
|
|
||||||
|
- scanner or report recognition;
|
||||||
|
- parser or extractor coverage, including newly readable sections, native scores, counters, details, evidence, attributes, or context;
|
||||||
|
- rule identity, finding boundaries, affected-asset identity, or duplicate handling;
|
||||||
|
- field-role mapping, status, outcome, severity, IOE, normalization, or canonical projection;
|
||||||
|
- report completeness diagnostics or security fixes that change which source records are accepted;
|
||||||
|
- scoring inputs that were not retained by the earlier import.
|
||||||
|
|
||||||
|
Presentation-only changes, labels, layout, filtering over already retained fields, and score-formula changes over already retained inputs do not require a source reimport. They must not be disguised as processing changes.
|
||||||
|
|
||||||
|
Clarification 2026-09-22 (Purple Knight for Entra ID): profile 1.4.0 and the summary extractor 1.2.0 recognize `Entra ID` summaries as Purple Knight context, retain the tenant and native Entra posture/counters, and label the scan separately from Purple Knight for Active Directory. Earlier imports stored that summary under the generic profile and therefore contain no canonical Entra observations. They remain unchanged and are marked `Reimport recommended`; the original files are required because scanner source bytes are not retained. Reimport uses the existing auditable replacement path and preserves eligible client-owned review state. No SQL migration is required.
|
||||||
|
|
||||||
|
Every scanner profile, including future profiles, declares its current processing contract. Historical contracts remain append-only. A material import change increments the relevant parser, extractor, profile, mapping, normalization, or presentation version and records `reimportRequired` plus the affected dimensions in `PROCESSING_VERSION_MANIFESTS`. Data Hub derives its warning from that persisted contract.
|
||||||
|
|
||||||
|
Clarification 2026-09-11: Data Hub assesses processing readiness per stored scan run. A generic-only run remains review-required. Generic companion files alongside exactly one recognized scanner profile in the same run do not independently require reimport when all file processing contracts are current or compatible. Every file retains its own profile, provenance and mapping; an inherited display label is not native recognition. A different run cannot supply the missing scanner context. Unknown/incompatible contracts, actual profile updates and unresolved client-review transfers remain actionable. A review-only state does not offer repeated reimport with unchanged processing as a remedy. This corrects status presentation over existing facts and requires no reimport or processing-version change.
|
||||||
|
|
||||||
|
## GPO re-evaluation and presentation
|
||||||
|
|
||||||
|
Clarification 2026-09-17 (missing retained GPO source): A persisted `backend-controlled` storage reference records custody and provenance; it does not prove that the file is currently accessible. The GPO status endpoint checks availability on each read, independently of repository revision, and distinguishes `Upload required` for missing bytes from `Storage unavailable` for an access/configuration failure. Automatic job repair, Retry and Reprocess check availability before enqueueing. A queued job that encounters a missing file stops after its first failed attempt instead of retrying three times. Data Hub and the GPO processing panel offer an artifact-bound original ZIP/folder upload; the project, optional timeslot, exact byte size and SHA-256 must match. Restoration atomically republishes only missing bytes under the original storage key, never overwrites an existing object and preserves original identities, timestamps and retained analysis history. Ordinary byte-identical resubmission repairs missing bytes as well. A missing-source job may resume after restoration; an already current analysis does not need another run just to restore its original file. Evaluation-only updates remain possible from retained inputs, while an analyzer update still requires the source before reprocessing. This is storage recovery, not a changed interpretation: no parser/analyzer/processing-manifest version bump, SQL migration or bulk reprocessing is required.
|
||||||
|
|
||||||
|
Clarification 2026-09-14 (baseline variants): Evaluator 1.5.0 compares each retained candidate independently and requires unanimous supported outcomes for Pass/Fail. It stores the versioned per-variant assessment and original values in the existing result JSON/JSONB. Historical 1.3.3/1.4.0 runs remain unchanged and require explicit re-evaluation through Data Hub; no scanner processing-manifest change or source reimport is required for this evaluation-only change. Inventory context does not establish a GPO target role. See [GPO_BASELINE_EVALUATION.md](GPO_BASELINE_EVALUATION.md) for the decision and evidence contract.
|
||||||
|
|
||||||
|
Clarification 2026-09-15 (mechanism-scoped baseline partiality): Evaluator 1.6.0 no longer lets a parser diagnostic from an unrelated delivery mechanism invalidate a separately resolved comparison. Diagnostics applicable to the candidate mechanism, unexplained partiality, ambiguous values and incomplete customer evidence remain review-only. Historical 1.5.0 results remain unchanged and require explicit re-evaluation in Data Hub; this evaluation-only change does not require a source reimport or processing-manifest update.
|
||||||
|
|
||||||
|
Clarification 2026-09-16 (policy-setting conflict roll-up): Evaluator 1.7.0 compares each retained configured member value of a customer conflict independently. One valid failing member makes the technical policy-setting verdict Fail once, while conflict/duplicate remain separate diagnostic flags; unresolved evidence remains review-only when no independently valid fail exists. Historical results through 1.6.0 remain unchanged and require explicit re-evaluation in Data Hub. The inputs were already retained, so this evaluation/presentation change requires neither a source reimport nor a processing-manifest update.
|
||||||
|
|
||||||
|
Clarification 2026-09-16 (analyzed-scope absence): Evaluator 1.8.0 reports an applicable recommendation with no observed configured value in the analyzed backup set as score-neutral `Not configured`, including for selected-subset imports. It retains a scope-limit diagnostic and does not claim domain-wide or effective-device absence. Historical results through 1.7.0 remain unchanged and require explicit re-evaluation in Data Hub; retained inputs are sufficient, so no source reimport or processing-manifest update is required.
|
||||||
|
|
||||||
|
Clarification 2026-09-16 (Registry.pol state requirements): Evaluator 1.9.0 compares retained `delete-value`, `delete-all-values`, `create-key` and typed empty-string recommendations as explicit backup-configuration states. Historical results through 1.8.0 remain unchanged and require explicit re-evaluation in Data Hub. The required original Registry.pol instructions, types and values are already retained, so this evaluator-only change requires no source reimport, processing-manifest update or SQL migration.
|
||||||
|
|
||||||
|
Clarification 2026-09-17 (baseline analyses of the written form): The Microsoft baseline analyzer consumes the same Security Template parser, so a baseline analysis produced before the parser change still carries the case-folded section. An evaluated setting takes its displayed identity from the baseline side, so the baseline packages must be re-analysed for the written form to reach Work. Append-only analyzer version `microsoft-baseline-gpo-settings` 1.3.0 -> 1.4.0; the accepted version list keeps every earlier value so historical analyses stay readable and are not rewritten. Two further selection rules were corrected in the same step. First, only analyses of the very same baseline package are one lineage: an interrupted supersession chain may leave several of its generations unsuperseded, and the newest analyzer version then wins, while independently analysed packages remain independent benchmarks. Second, the current run per aggregation is chosen by the distinct baselines actually compared rather than by the number of analysis references, so duplicated generations no longer outrank a narrower current run; where retained results name no baseline, the declared references stay the only measure. No source reimport, SQL migration or evaluator change; re-analysing a retained baseline package and re-evaluating is sufficient.
|
||||||
|
|
||||||
|
Clarification 2026-09-17 (written security-template section and key): The Security Template parser folded the section name to lower case and discarded the written form, so `[Privilege Rights]` was retained only as `privilege rights`. The parser now keeps the written section and key; the case-folded forms stay internal to grammar selection, grouping and the identity digest, which keeps existing observation and identity IDs stable. The native target resolver stores the written section and key in the retained target, matching what the registry resolver already did, so Work shows `Privilege Rights - SeCreateTokenPrivilege` and the value stays searchable against Microsoft documentation and external tables. This changes persisted parsing output, so it is an append-only processing-version change, not a presentation change: `gpo-security-template-inf` 1.1.0 -> 1.2.0, `gpo-backup-policy-analysis` 2.2.3 -> 2.2.4 and the GPO job processor 2.2.3 -> 2.2.4. Historical imports keep their retained values unchanged and are marked for reprocessing: Data Hub reports an affected GPO backup as `Reprocessing required` with the analyzer versions and offers the existing reprocess action on the stored source, so no upload is needed. Reprocessing precedes re-evaluation, and prior runs remain retained as history. No SQL migration and no evaluator change.
|
||||||
|
|
||||||
|
Clarification 2026-09-25 (GPO link facts): The GPO metadata parser retains, from each backup's own `GPReport.xml`, the number of `LinksTo` entries and how many are enabled (never the linked OU or domain names). A backup with no enabled link is presented as `Not linked`: the Finding Reader marks its source sections and states `No effect` in the problem summary when every contributing GPO is unlinked, and GPO Processing shows the count. This is a backup-report fact at backup time, not a claim about effective device state, and verdicts and score are unchanged. Because parsing now retains a new fact, it is an append-only processing-version change: `gpo-backup-metadata-xml` 1.2.0 -> 1.4.0 (the Microsoft baseline template variant keeps 1.3.0, so baseline packages need no re-analysis), `gpo-backup-policy-analysis` 2.2.4 -> 2.2.5 and the GPO job processor 2.2.4 -> 2.2.5. Historical imports are marked `Reprocessing required` and the existing reprocess action on the stored source adds the link facts; no upload, SQL migration or evaluator change is required. Until then no link status is shown.
|
||||||
|
|
||||||
|
Clarification 2026-09-17 (Not comparable verdict): A retained customer setting whose delivery mechanism has no comparable technical policy identity - today always a Group Policy Preference - reaches Work under the score-neutral verdict `Not comparable` instead of being dropped. It is never resolved into a policy identity and never compared with a Microsoft baseline. The unified read projection additionally exposes the already retained preference CSE, action and item-level targeting status so that context stays readable in the agreed GPO columns; no new column, facet or persisted field is introduced. Snapshot-aggregation review hints that restate an unresolved identity already reported by their source analysis are no longer emitted a second time. All of this projects facts the importer already retained, so no processing-manifest version, source reimport, evaluator change or SQL migration is required.
|
||||||
|
|
||||||
|
Clarification 2026-09-17 (No baseline rule verdict): Work now projects every retained customer policy state under exactly one verdict. A resolved technical identity with no applicable Microsoft baseline recommendation in the compared baseline set receives the score-neutral verdict `No baseline rule`; a retained comparison that yields no safe verdict stays `Review required` instead of being dropped. `Conflicts`/`Duplicates` remain diagnostic flags on the setting's own row and no longer replace its verdict, so their counts and the Result-DB consistency projection follow the flag rather than the row status. Observations that already evidence an evaluated comparison, including child values of a registry-key requirement, are never reported a second time; unresolved identities keep their separate analysis-gap reporting. This is a read-time projection over already retained observations, aggregations and evaluations: no evaluator version change, source reimport, processing-manifest update or SQL migration is required, and no persisted record is rewritten.
|
||||||
|
|
||||||
|
Clarification 2026-09-17 (current baseline analysis generation): GPO evaluation readiness is no longer derived from the evaluator version alone. A retained evaluation stays pinned to the baseline analysis ids it was created with, so a newer Microsoft baseline analysis generation of the same baseline also marks it as requiring re-evaluation. The Re-evaluate action resolves every retained baseline reference to its current non-superseded analysis and never widens the evaluated baseline set: an unknown reference and a baseline without a newer generation keep their own analysis id. Prior runs remain retained and are not rewritten. Baseline packages, their analyses and the customer inputs are already persisted, so this readiness/selection change requires no source reimport, processing-manifest update or SQL migration.
|
||||||
|
|
||||||
|
Clarification 2026-09-11 (GPO evaluation readiness): A saved baseline comparison from a previous evaluation engine requires re-evaluation of retained inputs, not automatically another upload. Data Hub reports this separately from scanner-profile reimport, scoped through active profile, snapshot, identity-analysis and artifact references. Its explicit Re-evaluate action uses the existing evaluation API; prior runs remain retained. Work labels readable previous-engine comparisons as historical and keeps their original outcomes in a separate Historical result field instead of presenting them as current Pass/Fail. Missing baseline description metadata and failed description loading are separate presentation states. OS grouping and row-bound native counts use already retained environment observations; these display changes introduce no processing manifest or source reimport requirement.
|
||||||
|
|
||||||
|
## Preservation contract
|
||||||
|
|
||||||
|
Reimport replaces scanner-derived data only. The following client-owned values are preserved as one object and transferred to the replacement finding when the identity is unique:
|
||||||
|
|
||||||
|
- workflow status and custom label;
|
||||||
|
- priority and action-plan inclusion;
|
||||||
|
- owner and due date;
|
||||||
|
- comments, prerequisites, required access, next step, and acceptance evidence;
|
||||||
|
- markers, including accepted-risk state;
|
||||||
|
- future additional review fields carried in the same record.
|
||||||
|
|
||||||
|
The match uses scanner identity, native rule ID, and affected asset. It succeeds only for one old and one new occurrence. A stable definition-level review remains attached without copying. ASSESSOR never falls back to title-only or positional matching.
|
||||||
|
|
||||||
|
Structured Accepted Risk decisions retain every original revision and its target occurrence ID. A transferred acceptance displays `Review required` when the new occurrence differs from the approved target. Preservation does not silently renew approval, expiry or scope: recording acceptance for the replacement requires a new decision. See [RISK_ACCEPTANCE.md](RISK_ACCEPTANCE.md).
|
||||||
|
|
||||||
|
When a match is absent or ambiguous, the old review remains on the retained previous scan. The replacement records an unresolved count and Data Hub shows a persistent review-required state. The reimport is still auditable and does not silently discard or guess customer data.
|
||||||
|
|
||||||
|
Both direct reimport and adoption of an already imported byte-identical replacement execute review transfer and scan supersession in the same repository transaction. The exact source SHA-256 multiset, assessment, and scanner must match before replacement is accepted.
|
||||||
|
# Reference derivation (Phase 13D, 2026-09-26)
|
||||||
|
|
||||||
|
ATT&CK/D3FEND recognition is an independent, versioned derivation over retained original
|
||||||
|
fields. It never alters canonical Finding identity, scanner outcome, source mapping or
|
||||||
|
reviews. Catalog or recognizer changes therefore use **Recognize references** in the
|
||||||
|
client's Data Hub and append a new derivation; they do not require reuploading source
|
||||||
|
files. Historical reference runs and their exact catalog snapshots remain retained.
|
||||||
|
If an extractor never retained a required source field, recognition cannot recover it;
|
||||||
|
the existing extractor-version/reimport policy still applies in that case.
|
||||||
|
|
@ -0,0 +1,350 @@
|
||||||
|
# Umsetzungsauftrag: Report A (Management) und Report B (Technical)
|
||||||
|
|
||||||
|
> Hinweis 2026-09-30: Historischer Auftrag. Die hier genannten Report-Dateien liegen seit der Verschiebung ohne Logikaenderung in `src/reporting/` (siehe `docs/REPORT_STRATEGY.md`, Report Engine boundary).
|
||||||
|
|
||||||
|
Adressat: Codex. Dieser Auftrag ist verbindlich und abschließend formuliert.
|
||||||
|
Wo dieser Auftrag schweigt, gelten `AGENTS.md`, `docs/REPORT_STRATEGY.md`,
|
||||||
|
`docs/SCORING_MODEL.md` und `.claude/skills/assessor-ui/SKILL.md` unverändert
|
||||||
|
weiter. Bei einem Widerspruch zwischen diesem Auftrag und einer dieser Quellen:
|
||||||
|
**anhalten und den Widerspruch melden, nicht eigenmächtig auflösen.**
|
||||||
|
|
||||||
|
Plan-Zuordnung: Die Artefakte A–D sind in `docs/REPORT_STRATEGY.md` als
|
||||||
|
Deliverable ohne zugewiesene Plan-Phase festgehalten. Dieser Auftrag setzt A und
|
||||||
|
B um und ändert die Strategie inhaltlich nicht.
|
||||||
|
|
||||||
|
## 0. Ausgangszustand im Code (verifiziert)
|
||||||
|
|
||||||
|
| Baustein | Zustand |
|
||||||
|
| --- | --- |
|
||||||
|
| `src/domain/report-projection.ts` | vorhanden, `REPORT_PROJECTION_VERSION = '1.0.0'`, getestet |
|
||||||
|
| `src/exports/management-overview-report.ts` | vorhanden, `buildManagementOverviewHtml`, Renderer-Version `'1.0.0'`, getestet |
|
||||||
|
| Produktiver Aufrufer von `buildReportProjection` | **existiert nicht** — nur Tests |
|
||||||
|
| Produktiver Aufrufer von `buildManagementOverviewHtml` | **existiert nicht** — nur Tests |
|
||||||
|
| Report B (Technical) | **existiert nicht** |
|
||||||
|
| Security-Domain-Zuordnung über `UnifiedResultProjection` | **existiert nicht** (Katalog arbeitet auf `FindingRecord`) |
|
||||||
|
| Framework-Referenzen (MITRE ATT&CK / D3FEND) in der Unified-Projektion | **nicht enthalten** (nur `targetRole === 'category'` wird übernommen) |
|
||||||
|
|
||||||
|
Die beiden vorhandenen Bausteine sind korrekt und werden **nicht neu geschrieben**,
|
||||||
|
sondern additiv erweitert.
|
||||||
|
|
||||||
|
## 1. Lieferumfang
|
||||||
|
|
||||||
|
Acht Arbeitspakete, in dieser Reihenfolge. Jedes Paket ist einzeln lauffähig,
|
||||||
|
getestet und wird einzeln committet.
|
||||||
|
|
||||||
|
### AP1 — Security-Domain-Zuordnung für `UnifiedResultProjection`
|
||||||
|
|
||||||
|
**Datei:** `src/features/dashboard/security-domains.ts` (erweitern, nicht ersetzen)
|
||||||
|
|
||||||
|
Neue Funktion:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export function securityDomainIdForUnifiedResult(result: UnifiedResultProjection): string
|
||||||
|
```
|
||||||
|
|
||||||
|
Bindende Semantik:
|
||||||
|
|
||||||
|
- Es gilt **derselbe** `securityDomainCatalog` in **derselben** Reihenfolge.
|
||||||
|
Kein zweiter Katalog, keine zweite Regex-Menge, keine report-eigene Zuordnung.
|
||||||
|
- Evidenztext ist: `title`, `description` sowie `categoryLabel` aller Einträge
|
||||||
|
aus `result.categories`.
|
||||||
|
- Die explizite GPO-Erkennung geht wie bisher der Regex-Prüfung vor: erfüllt,
|
||||||
|
wenn `result.sourceKind` einer von `'policy-evaluation' | 'policy-observation'`
|
||||||
|
ist **oder** ein `categoryLabel` exakt `GPO`/`GPOs` lautet (case-insensitiv).
|
||||||
|
- Kein Treffer ⇒ `unclassifiedSecurityDomain.id`.
|
||||||
|
|
||||||
|
**Akzeptanzkriterium (Pflichttest):** Für einen synthetischen Fall, der sowohl
|
||||||
|
als `FindingRecord` als auch als `UnifiedResultProjection` gebaut wird, liefern
|
||||||
|
`securityDomainIdForRecord` und `securityDomainIdForUnifiedResult` **dieselbe**
|
||||||
|
Domain — für alle acht Katalogeinträge, für den GPO-Sonderfall und für
|
||||||
|
`unclassified`. Grund: Client-Info-Drilldown und Reports müssen dieselben
|
||||||
|
Datensatzidentitäten auswählen (`AGENTS.md`, Abschnitt *UI Work filter context*).
|
||||||
|
|
||||||
|
### AP2 — Framework-Referenzen in der Unified-Projektion sichtbar machen
|
||||||
|
|
||||||
|
**Datei:** `src/domain/unified-result.ts`
|
||||||
|
|
||||||
|
Neu:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface UnifiedResultFrameworkReference {
|
||||||
|
namespace: string; // Tool-ID der Quelle
|
||||||
|
value: string; // normalizedValue der Mapping-Eintragung, unverändert
|
||||||
|
sourceFieldKey?: string; // sourceFieldKeys[0], falls vorhanden
|
||||||
|
sourceLabel?: string; // Original-Spaltenüberschrift, falls vorhanden
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
und additiv auf `UnifiedResultProjection`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
frameworkReferences: UnifiedResultFrameworkReference[];
|
||||||
|
```
|
||||||
|
|
||||||
|
Befüllung analog zu `scannerResultCategories`, aber aus den Mappings mit
|
||||||
|
`targetRole === 'framework-reference'`. Leere Werte werden verworfen, alle
|
||||||
|
übrigen **unverändert und unnormalisiert** übernommen.
|
||||||
|
|
||||||
|
Bindende Randbedingungen:
|
||||||
|
|
||||||
|
- Die Ableitung erfolgt **zur Projektionszeit aus bereits persistierten
|
||||||
|
`fieldMappings`**. Es wird **keine** neue Spalte, Tabelle oder Migration
|
||||||
|
angelegt und **kein** Parsing geändert.
|
||||||
|
- Damit ist dies presentation-only über bereits gespeicherte Fakten:
|
||||||
|
**kein** neuer Processing-Manifest-Eintrag, **keine** Reimport-Markierung
|
||||||
|
(`docs/REIMPORT_POLICY.md`).
|
||||||
|
- Sollte sich beim Umsetzen zeigen, dass eine Persistenzänderung nötig wäre:
|
||||||
|
**anhalten und melden**, nicht persistieren.
|
||||||
|
|
||||||
|
**Akzeptanzkriterium:** Synthetische Occurrence mit zwei
|
||||||
|
`framework-reference`-Mappings und einem `category`-Mapping ⇒ zwei
|
||||||
|
`frameworkReferences`, eine `category`, keine Vermischung, Originalwerte identisch.
|
||||||
|
|
||||||
|
### AP3 — Korrelationsmodul (Domain, plattformneutral)
|
||||||
|
|
||||||
|
**Neue Datei:** `src/domain/report-correlation.ts`
|
||||||
|
**Konstante:** `export const REPORT_CORRELATION_VERSION = '1.0.0';`
|
||||||
|
|
||||||
|
Kein Storage, keine Uhr, kein Netzwerk, keine Präsentationstypen. Drei Ergebnisse:
|
||||||
|
|
||||||
|
**(a) Quellenkonvergenz je Security Domain**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface ReportDomainConvergence {
|
||||||
|
domainId: string;
|
||||||
|
sources: Array<{ toolId: string; assessed: number; deviations: number }>;
|
||||||
|
sourceCount: number; // Quellen mit mindestens einer Abweichung
|
||||||
|
evidence: 'shared-framework-reference' | 'domain-only';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`evidence` ist `'shared-framework-reference'` **nur dann**, wenn mindestens zwei
|
||||||
|
Quellen in dieser Domain eine **exakt gleiche, nicht leere**
|
||||||
|
`frameworkReferences`-Zeichenkette tragen. Andernfalls `'domain-only'`.
|
||||||
|
|
||||||
|
**(b) Maßnahmenbündel**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface ReportRemediationBundle {
|
||||||
|
key: string; // die Framework-Referenz, unverändert
|
||||||
|
namespace: string;
|
||||||
|
members: string[]; // resultProjectionIds
|
||||||
|
toolIds: string[];
|
||||||
|
domainIds: string[];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Gebildet über identische `frameworkReferences`-Werte von Abweichungen. Bündel
|
||||||
|
mit weniger als zwei Mitgliedern entfallen. Sortierung: Mitgliederzahl absteigend,
|
||||||
|
danach `key` alphabetisch — stabil, damit Ausgaben reproduzierbar sind.
|
||||||
|
|
||||||
|
**(c) GPO-Einzelquellen-Maßnahmen**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface ReportSingleSourcePolicyAction {
|
||||||
|
policyEntryId: string;
|
||||||
|
backupInstanceId: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Genau die `ReportPolicyEntry` mit `verdict === 'Fail'`, `conflict === false`,
|
||||||
|
`duplicate === false` und **exakt einem** Eintrag in `backupInstanceIds`.
|
||||||
|
|
||||||
|
Bindende Verbote für AP3:
|
||||||
|
|
||||||
|
- **Keine** Titel-, Wort-, Fuzzy- oder Ähnlichkeitsheuristik über Quellen hinweg.
|
||||||
|
- **Kein** quellenübergreifender Score, Rang oder normalisierter Schweregrad.
|
||||||
|
- **Keine** Aussage „gleicher Befund" — die Aussage lautet ausschließlich
|
||||||
|
„dieselbe Domain" bzw. „dieselbe Framework-Referenz".
|
||||||
|
- **Keine** Aufwands-, Kosten- oder Quick-Win-Schätzung. Ein Bündel trägt eine
|
||||||
|
Mitgliederzahl, keine Bewertung.
|
||||||
|
|
||||||
|
### AP4 — Projektion erweitern
|
||||||
|
|
||||||
|
**Datei:** `src/domain/report-projection.ts`
|
||||||
|
**Version:** `REPORT_PROJECTION_VERSION` von `'1.0.0'` auf `'1.1.0'`.
|
||||||
|
|
||||||
|
Additiv auf `ReportProjection`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
convergence: ReportDomainConvergence[];
|
||||||
|
bundles: ReportRemediationBundle[];
|
||||||
|
singleSourcePolicyActions: ReportSingleSourcePolicyAction[];
|
||||||
|
```
|
||||||
|
|
||||||
|
Bestehende Felder, Typen und Semantiken bleiben unverändert. Die bestehenden
|
||||||
|
Zusicherungen in `tests/domain/report-projection.test.ts` müssen ohne Änderung
|
||||||
|
grün bleiben; neue Tests kommen hinzu.
|
||||||
|
|
||||||
|
### AP5 — Report-Eingabe zusammenstellen
|
||||||
|
|
||||||
|
**Neue Datei:** `src/features/reports/report-input-assembler.ts`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export async function assembleReportProjectionInput(
|
||||||
|
repository: AssessorRepository,
|
||||||
|
scope: { projectId: string; assessmentId?: string; generatedAt: string }
|
||||||
|
): Promise<ReportProjectionInput>
|
||||||
|
```
|
||||||
|
|
||||||
|
Quellen, ausschließlich:
|
||||||
|
|
||||||
|
- Ergebnisse: `repository.queryUnifiedResults` über den Scope.
|
||||||
|
- `domainOf`: `securityDomainIdForUnifiedResult` aus AP1.
|
||||||
|
- `priorityBases`: `reportPriorityBases(...)` über die registrierten Profile.
|
||||||
|
- `scoreSources` / `overallScore`: die vorhandene Scoring-Schicht
|
||||||
|
(`src/domain/security-scoring.ts`,
|
||||||
|
`src/features/dashboard/dashboard-security-score.ts`).
|
||||||
|
**Keine eigene Score-Arithmetik.**
|
||||||
|
- `environment`: normalisierte Fakten aus dem Environment-Snapshot. Fehlende
|
||||||
|
Fakten bleiben **absent** und werden nie zu `0`.
|
||||||
|
- `policyEntries` / `policyScopeLimited`: aus der Evaluations- und
|
||||||
|
Aggregationsschicht mit dem **autoritativen, hochgerollten** Verdict.
|
||||||
|
**Kein** Verdict wird hier neu abgeleitet.
|
||||||
|
- `reviews`, `diagnostics`, `provenance`: aus den vorhandenen Repository-Pfaden.
|
||||||
|
- `delta`: nur, wenn ein **gespeicherter** Timeslot-Vergleich existiert. Es wird
|
||||||
|
**keine** Referenzlauf-Auswahl und **keine** Paarung im Export gebildet.
|
||||||
|
|
||||||
|
Die Zusammenstellung läuft **nur beim Export**, nie beim Laden eines Workspace.
|
||||||
|
|
||||||
|
### AP6 — Management-Renderer erweitern
|
||||||
|
|
||||||
|
**Datei:** `src/exports/management-overview-report.ts`
|
||||||
|
**Version:** `MANAGEMENT_OVERVIEW_RENDERER_VERSION` auf `'1.1.0'`.
|
||||||
|
|
||||||
|
Bestehende Abschnitte bleiben unverändert. Zwei neue Abschnitte:
|
||||||
|
|
||||||
|
1. **„Konvergenz der Quellen"** — direkt nach `Security Domains`. Je Domain:
|
||||||
|
Anzahl unabhängiger Quellen mit Abweichungen, die Quellen namentlich, und eine
|
||||||
|
sichtbare Unterscheidung zwischen belegter Konvergenz
|
||||||
|
(`shared-framework-reference`) und thematischer Nähe (`domain-only`). Letztere
|
||||||
|
trägt im Fließtext ausdrücklich den Hinweis auf **Validierungsbedarf**.
|
||||||
|
2. **„Maßnahmenbündel"** — direkt nach `Priority Findings`. Die Bündel aus AP3(b)
|
||||||
|
mit Referenz und Mitgliederzahl sowie die Anzahl der Einzelquellen-GPO-
|
||||||
|
Maßnahmen aus AP3(c). **Keine** Aufwands- oder Nutzenaussage.
|
||||||
|
|
||||||
|
Der Abschnitt „Abdeckung und Lücken" wird **vor** „Priority Findings" gezogen und
|
||||||
|
zeigt zusätzlich je Quelle den Anteil `not_run` an der Gesamtzahl. Begründung:
|
||||||
|
Die Aussagegrenze muss vor der Priorisierung stehen.
|
||||||
|
|
||||||
|
Unverändert bindend: keine Registry-Keys, SIDs, Evidenzpfade, Parser- oder
|
||||||
|
Manifest-Versionen in Report A.
|
||||||
|
|
||||||
|
### AP7 — Report B (Technical)
|
||||||
|
|
||||||
|
**Neue Datei:** `src/exports/technical-report.ts`
|
||||||
|
**Konstante:** `export const TECHNICAL_REPORT_RENDERER_VERSION = '1.0.0';`
|
||||||
|
**Signatur:** `export function buildTechnicalReportHtml(projection: ReportProjection): string`
|
||||||
|
|
||||||
|
Abschnitte in dieser Reihenfolge, gemäß `docs/REPORT_STRATEGY.md` Report B:
|
||||||
|
|
||||||
|
1. Scope und Provenance — Importe, Processing-, Analyzer- und Manifest-Versionen,
|
||||||
|
Reimport-Status, Aggregations- und Evaluations-Run-IDs.
|
||||||
|
2. Ergebnisinventar — Totals je Status, Severity, Domain und Tool, inklusive der
|
||||||
|
score-neutralen Klassen; Passed erscheint als Zahl.
|
||||||
|
3. Findings im Detail — gruppiert je Domain, innerhalb der Gruppe nach Severity.
|
||||||
|
4. GPO-Policy-Register — alle sieben Verdicts vollständig; die score-neutralen
|
||||||
|
sind **am Ort der Anzeige** als score-neutral gekennzeichnet.
|
||||||
|
5. Konvergenz-Nachweis — die Paare aus AP3(a) mit ihrer Begründung, damit die
|
||||||
|
Managementaussage aus AP6 nachprüfbar ist.
|
||||||
|
6. Scope-Grenzen — ausdrücklich: kein RSoP, keine Aussage über wirksame
|
||||||
|
Endpoint-Konfiguration, GPO-Aussagen gelten für das importierte Backup-Set,
|
||||||
|
`not_run` ist kein Pass.
|
||||||
|
7. Diagnostik — jede Beobachtung **einmal**, mit ihrem autoritativen Resolver-Grund.
|
||||||
|
8. Anhang — vollständiger Ergebnisindex.
|
||||||
|
|
||||||
|
### AP8 — Auslösung in der Anwendung
|
||||||
|
|
||||||
|
Zwei Aktionen in der bestehenden Oberfläche, die den Scope des aktuellen Projekts
|
||||||
|
bzw. Assessments verwenden und über `src/exports/download-adapter.ts` bzw.
|
||||||
|
`src/exports/text-download.ts` ausliefern. Keine neue Navigationsebene, keine neue
|
||||||
|
Shell, keine neuen Farb- oder Geometrie-Tokens. Es gilt
|
||||||
|
`.claude/skills/assessor-ui/SKILL.md` und der 20px-Panel-Inset.
|
||||||
|
|
||||||
|
## 2. Gestaltungsvorgaben für beide Dateien
|
||||||
|
|
||||||
|
- Sprache: Deutsch. Etablierte englische Fachbegriffe bleiben englisch
|
||||||
|
(`Deviation`, `Passed`, `Not configured`, `No baseline rule`, `Not comparable`,
|
||||||
|
`Conflicts`, `Duplicates`, `Finding`, `Baseline`, `Score`, `Pass Rate`,
|
||||||
|
Security-Domain-Namen, `Group Policy`, `Registry`, `Domain Controller`).
|
||||||
|
- Zahlen und Daten in deutscher Formatierung. Unbekanntes ist ein Geviertstrich,
|
||||||
|
niemals eine `0`.
|
||||||
|
- Getrennte Dateien. Report A enthält keine technischen Identifikatoren.
|
||||||
|
- Vollständig eigenständig: alle Styles inline, kein CDN, keine externe Font,
|
||||||
|
kein Skript, kein Bild, kein Netzwerkzugriff beim Öffnen. Druckbar nach PDF
|
||||||
|
ohne Layoutverlust.
|
||||||
|
- Typografie-Rollen, Donut-Geometrie, Tile- und Segment-Grammatik,
|
||||||
|
Statusfarbregeln und Panel-Inset wie in der Anwendung. Statusfarbe nur in
|
||||||
|
Akzentlinie, Marker, Arc oder Score-Node — nie als Flächenfüllung.
|
||||||
|
|
||||||
|
## 3. Harte Verbote
|
||||||
|
|
||||||
|
Nicht verhandelbar, auch dann nicht, wenn sie eine Lücke schließen würden:
|
||||||
|
|
||||||
|
1. Kein quellenübergreifender, normalisierter Schweregrad, Rang oder Risiko-Score.
|
||||||
|
2. Keine Aussage über wirksame Endpoint-Konfiguration aus einem Backup-Vergleich.
|
||||||
|
3. Kein GPO-Verdict aus Vergleichsprosa, Historie, Baseline-Text, Guidance oder
|
||||||
|
Evidenz — ausschließlich das autoritative hochgerollte Verdict plus die
|
||||||
|
expliziten Conflict- und Duplicate-Flags.
|
||||||
|
4. Ein selbst gemeldeter Review-Status wird nie zu einem gelösten Finding.
|
||||||
|
5. Kein Delta ohne gespeicherten Vergleich; keine Referenzlauf-Auswahl im Export.
|
||||||
|
6. Keine Score-Arithmetik, kein quellenspezifisches Parsing und kein
|
||||||
|
Datenbankzugriff in einem Renderer.
|
||||||
|
7. `Not configured`, `No baseline rule` und `Not comparable` bleiben score-neutral
|
||||||
|
und erscheinen nie in den Priority Findings.
|
||||||
|
8. Keine Titel- oder Ähnlichkeitsheuristik als Beleg für „gleicher Befund".
|
||||||
|
9. Keine echten Kunden-, Mandanten- oder Scan-Inhalte in Tests, Fixtures, Logs
|
||||||
|
oder Commit-Nachrichten. Ausschließlich synthetische Fixtures.
|
||||||
|
|
||||||
|
## 4. Tests (Pflicht, synthetisch)
|
||||||
|
|
||||||
|
| Paket | Testdatei | Muss nachweisen |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| AP1 | `tests/features/security-domain-unified.test.ts` | Gleichstand beider Klassifizierer über alle acht Domains, GPO-Sonderfall, `unclassified` |
|
||||||
|
| AP2 | `tests/domain/unified-result-framework-references.test.ts` | Trennung `category` / `framework-reference`, Originalwerte unverändert, leere Werte verworfen |
|
||||||
|
| AP3 | `tests/domain/report-correlation.test.ts` | `shared-framework-reference` nur bei exakter Übereinstimmung über mindestens zwei Quellen; Bündel unter zwei Mitgliedern entfallen; stabile Sortierung; GPO-Einzelquelle schließt Conflict, Duplicate und Mehrfach-Backups aus |
|
||||||
|
| AP4 | Erweiterung `tests/domain/report-projection.test.ts` | Neue Felder vorhanden, bestehende Zusicherungen unverändert grün |
|
||||||
|
| AP5 | `tests/features/report-input-assembler.test.ts` | Kein Verdict wird neu abgeleitet; fehlende Fakten bleiben absent; `delta` bleibt ohne gespeicherten Vergleich `undefined`; Zusammenstellung nur beim Export |
|
||||||
|
| AP6 | Erweiterung `tests/exports/management-overview-report.test.ts` | Beide neuen Abschnitte vorhanden; `domain-only` trägt den Validierungshinweis; keine Registry-Keys, SIDs, Pfade oder Versionen im Output; Abdeckung steht vor Priority Findings |
|
||||||
|
| AP7 | `tests/exports/technical-report.test.ts` | Alle acht Abschnitte; score-neutrale Verdicts als solche gekennzeichnet; Diagnostik je Beobachtung genau einmal; Scope-Grenzen-Abschnitt vorhanden |
|
||||||
|
| AP8 | Verhaltenstest der Auslösung | Export läuft nur auf Klick, nicht beim Laden des Workspace |
|
||||||
|
|
||||||
|
Zusätzlich für Report A und B je ein Selbstgenügsamkeits-Test: der erzeugte
|
||||||
|
HTML-String enthält kein `<script`, kein `@import`, keine externe Font-URL und
|
||||||
|
keine `http`- oder `https`-Ressourcenreferenz in `src` oder `href`.
|
||||||
|
|
||||||
|
## 5. Verifikation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Beide müssen grün sein. Zusätzlich ist zu belegen, dass die bestehenden Tests zu
|
||||||
|
`report-projection` und `management-overview-report` ohne inhaltliche Änderung
|
||||||
|
weiter bestehen.
|
||||||
|
|
||||||
|
## 6. Nicht Teil dieses Auftrags
|
||||||
|
|
||||||
|
- **Artefakt C (Arbeitsgrundlage)** und **Artefakt D (Delta Report)**. D ist
|
||||||
|
mangels gespeicherter Vergleiche ohnehin nicht lieferbar.
|
||||||
|
- **Framework-Abdeckung aus Maester-Tags (CISA SCuBA, CIS M365, Lizenzstufen).**
|
||||||
|
Grund: Die Tag-Liste ist heute als **eine** opake Category-Zeichenkette
|
||||||
|
gespeichert, teils doppelt in zwei Serialisierungen. Eine belastbare Tag-Sicht
|
||||||
|
erfordert eine Profil- und Importer-Änderung mit Reimport-Pflicht nach
|
||||||
|
`docs/REIMPORT_POLICY.md` und ist als eigener Auftrag zu beschließen. Bis dahin
|
||||||
|
wird die Zeichenkette **nicht** geraten, gesplittet oder interpretiert.
|
||||||
|
- **Asset- und Angriffspfad-Korrelation.** `assessor_canonical_assets` und
|
||||||
|
`assessor_asset_edges` sind leer, `affectedAsset` ist nur bei einer Quelle und
|
||||||
|
dort nahezu konstant belegt. Ein Pfad-Graph wäre eine Erfindung.
|
||||||
|
- Änderungen an Data Hub, Work, Pathfinder oder Client Info, die über die
|
||||||
|
Auslösung aus AP8 hinausgehen.
|
||||||
|
|
||||||
|
## 7. Abschluss
|
||||||
|
|
||||||
|
Nach Abschluss aller Pakete ist `docs/REPORT_STRATEGY.md` im selben Change um
|
||||||
|
einen Change-Log-Eintrag zu ergänzen, der festhält: Report A auf Renderer-Version
|
||||||
|
`1.1.0` erweitert, Report B in Version `1.0.0` implementiert, Projektion auf
|
||||||
|
`1.1.0`, Korrelationsmodul in `1.0.0` eingeführt, Reimport nicht erforderlich.
|
||||||
|
Kein Meilenstein wird als erfüllt markiert, bevor seine Akzeptanzkriterien
|
||||||
|
verifiziert sind.
|
||||||
|
|
@ -0,0 +1,766 @@
|
||||||
|
# Assessment output strategy
|
||||||
|
|
||||||
|
## Export layout decision 2026-10-02
|
||||||
|
|
||||||
|
User approval 2026-10-02: this implemented report is the binding template for future Management Reports. Its permanent contract lives inside Report Engine at [MANAGEMENT_REPORT_TEMPLATE.md](../src/reporting/MANAGEMENT_REPORT_TEMPLATE.md). Preserve the approved renderer structure and data boundaries unless explicitly revised by the user; historical HTML exports are not overwritten.
|
||||||
|
|
||||||
|
The user-supplied Huntsman report page is the layout/style reference for the Management Report only: white report sheets, ASSESSOR branding, orange headings, dark table headers, two-column category explanations/evidence, factual bar charts and repeated footers. This explicit export-only decision supersedes previous app-style layout guidance for the Management Report; application views remain unchanged. The report embeds the existing ASSESSOR logo and local Inter with no remote dependency. The opening overview and complete Context heatmap lead into one logical report sheet per populated category, followed by scope and method. Logical sheet numbering does not promise one physical PDF page per sheet. Missing time-series/score/endpoint data is never fabricated to imitate the reference. Original technical evidence and recommendations remain in the separate Technical Report.
|
||||||
|
|
||||||
|
## Management Report with published Context results (2026-10-02)
|
||||||
|
|
||||||
|
Explicit user decision supersedes the earlier Core-only Report A content below: the Management Report requires Context results for the same client, assessments, scan runs and committed repository revision. It is generated offline with fixed German text, no LLM and no Internet access. A missing Context producer or mismatched scope blocks the management run; a changing revision retries the read and ultimately fails rather than combining snapshots. Client Info refuses management generation while Context Engine is off and suppresses late output after it is switched off. Technical generation remains available independently and does not compute Context when invoked alone; its archive contains no management HTML rather than a Core-only fallback. Historical frozen projections retain the historical renderer and are not rewritten.
|
||||||
|
|
||||||
|
Context Engine publishes a versioned result object through `context-report-publication.ts`; Report Engine consumes only its declared `ReportContextPublication` contract. The Core backend composes the two at its existing pinned extension point using the canonical read-only port. Neither engine imports the other. The publication preserves catalog/vocabulary/engine/severity versions, deduplicated finding count, per-cluster scanner/GPO counts, assignment confidence, source provenance and technical Result IDs. It uses the existing Context membership and heatmap semantics, without classifying or scoring again.
|
||||||
|
|
||||||
|
The management file contains: Management Summary; all Context categories in the severity-sorted heatmap with the unique overall Finding count; populated problem fields with general catalog descriptions, evidence counts, confidence and scanner sources; fixed review guidance pointing to original recommendations in Report B; documented risk acceptance and selected work-state counts; comparison results only when supplied by the existing shared projection; environment facts, source coverage and collection times; technical Security Domain counts in the appendix; method and source-version notes. General catalog descriptions are explicitly not evidence of a common attack path. Base Severity is category severity, never a new finding verdict, CVSS, priority, exploitation probability or business loss. Unknown/unexecuted checks are not passed; neutral heatmap cells are not secure; GPO backup configuration is not effective host state. Overlapping cluster counts are never added into a total. Work tracking and risk acceptance do not prove remediation; rescans remain required.
|
||||||
|
|
||||||
|
Management disclosure stays separate: no Result IDs, source Rule IDs, evidence paths, registry keys, technical finding fields or original scanner remediation payloads are hidden in Report A. Report B receives the frozen Context counts and the Result IDs proving them alongside its original detailed evidence and recommendations. Archive projection JSON retains the published results for reproducible replay. Static HTML includes the already bundled local Inter font as embedded data and has no script or external resources. The report export is not part of the client load path.
|
||||||
|
|
||||||
|
Current versions: Report Engine 0.3.0, shared projection 1.3.0, Management renderer 3.0.0; product capability release 2.13.0. No database schema, migration or Core result semantics change. A separate historical Delta Report remains its own artifact; the new management cut does not invent comparison evidence when no delta was supplied.
|
||||||
|
|
||||||
|
Decision: 2026-09-18, explicitly approved by the user. The 2026-09-23 user request
|
||||||
|
starts the A/B HTML deliverable through Client Info (Phase 7 UI integration,
|
||||||
|
cross-cutting report projection). Artifact C and D remain separate future work.
|
||||||
|
|
||||||
|
ASSESSOR produces **three** artifacts from one assessment:
|
||||||
|
|
||||||
|
| Artifact | Audience | Nature |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| A — Management Overview (HTML) | customer decision makers | frozen snapshot |
|
||||||
|
| B — Technical Report (HTML) | engineers reproducing a finding | frozen snapshot, complete |
|
||||||
|
| C — Arbeitsgrundlage (XLSX + HTML) | the people doing the work | living document, curated |
|
||||||
|
| D — Delta Report (HTML, management + technical cut) | both, after a rescan | frozen snapshot of the change |
|
||||||
|
|
||||||
|
The dividing principle: **a report states the finding and its proof; the
|
||||||
|
Arbeitsgrundlage states the measure and who owns it.**
|
||||||
|
|
||||||
|
Remediation progress is proven by rescans, never by self-reported status. See
|
||||||
|
*Progress is proven by rescan* below.
|
||||||
|
|
||||||
|
## Approved delivery decisions
|
||||||
|
|
||||||
|
| Decision | Choice |
|
||||||
|
| --- | --- |
|
||||||
|
| Delivery | Separate files per artifact |
|
||||||
|
| Design | ASSESSOR application look |
|
||||||
|
| Technical scope (B) | Deviations, diagnostics and score-neutral classes |
|
||||||
|
| Interaction | Static, print- and PDF-ready, no JavaScript |
|
||||||
|
| Language | German, all artifacts |
|
||||||
|
| Arbeitsgrundlage format | XLSX to work in, HTML as the frozen status view |
|
||||||
|
| Arbeitsgrundlage scope | Curated selection, plus the complete list on a second sheet |
|
||||||
|
| Progress evidence | Rescan only; planning fields stay but are marked unverified |
|
||||||
|
| Delta delivery | Own artifact, management and technical cut |
|
||||||
|
|
||||||
|
Separate files keep the confidentiality boundary real: the Management file can be
|
||||||
|
forwarded without carrying registry keys, SIDs, evidence paths or parser
|
||||||
|
versions in its source. A single file with a hidden section would still contain
|
||||||
|
them.
|
||||||
|
|
||||||
|
## Report language
|
||||||
|
|
||||||
|
Both reports are written in German: headings, prose, labels, table headers,
|
||||||
|
legends and every explanatory sentence.
|
||||||
|
|
||||||
|
This does not change the Client Info dashboard, which remains English-only by its
|
||||||
|
own decision. The reports are separate customer-facing artifacts; the two rules
|
||||||
|
stand side by side and neither overrides the other.
|
||||||
|
|
||||||
|
German is the carrier language. **Established English technical terminology stays
|
||||||
|
English** and is not translated, not hyphenated into a German compound and not
|
||||||
|
replaced by an invented German equivalent. This is the general rule, not a list
|
||||||
|
of exceptions. It covers at least:
|
||||||
|
|
||||||
|
- the agreed GPO verdicts `Deviation`, `Passed`, `Not configured`,
|
||||||
|
`No baseline rule`, `Not comparable`, `Conflicts`, `Duplicates`
|
||||||
|
- the security domain names from `src/features/dashboard/security-domains.ts`
|
||||||
|
- assessment vocabulary such as Finding, Baseline, Score, Scope, Severity,
|
||||||
|
Pass Rate, Review, Owner, Due Date, Remediation, Evidence
|
||||||
|
- platform vocabulary such as Group Policy, Registry, Domain Controller,
|
||||||
|
Privilege, Attack Path, Hardening
|
||||||
|
|
||||||
|
Reason: these are the terms the readers already use, and most of them appear
|
||||||
|
verbatim in the application's Work filters and in the Client Info dashboard. A
|
||||||
|
reader must be able to match a line in the report to the view it came from, and a
|
||||||
|
translated second vocabulary would weaken the documented agreements.
|
||||||
|
|
||||||
|
Where a term is not self-explanatory to a management reader, it is introduced once
|
||||||
|
with a short German explanation and then used in its canonical English form.
|
||||||
|
German prose carries the sentence; the term inside it stays English.
|
||||||
|
|
||||||
|
Original source values, registry keys, value names, principals, evidence paths and
|
||||||
|
native identifiers are never translated or localized. Numbers and dates follow
|
||||||
|
German formatting.
|
||||||
|
|
||||||
|
The shared projection stays language-neutral and carries data, not display
|
||||||
|
strings. All German wording lives in the renderers.
|
||||||
|
|
||||||
|
## Shared report projection
|
||||||
|
|
||||||
|
Implemented in `src/reporting/report-projection.ts` (moved from `src/domain` on 2026-09-30)
|
||||||
|
(`REPORT_PROJECTION_VERSION = '1.1.0'`), covered by
|
||||||
|
`tests/domain/report-projection.test.ts`.
|
||||||
|
|
||||||
|
All artifacts read **one** projection built in the platform-neutral domain layer.
|
||||||
|
The projection is the single source of truth; the renderers only choose depth,
|
||||||
|
never content. Two consequences are binding:
|
||||||
|
|
||||||
|
- A number that appears in both reports is the same number. The Management
|
||||||
|
Overview never rounds, re-derives or re-filters a value that the Technical
|
||||||
|
Report states differently.
|
||||||
|
- Renderers contain no source-specific parsing, no scoring arithmetic and no
|
||||||
|
database access. They receive the finished projection.
|
||||||
|
|
||||||
|
The projection is built on export only. It is not part of the workspace load
|
||||||
|
path and must not derive presentation fields for records no report section
|
||||||
|
consumes.
|
||||||
|
|
||||||
|
## Report A — Management Overview
|
||||||
|
|
||||||
|
**Superseding user decision 2026-09-29 (Phase 7):** The Management Report is
|
||||||
|
generated offline and without an LLM from the current, selected client's
|
||||||
|
existing report projection. It is a factual overview, not a scanner finding
|
||||||
|
list, mixed-method score or speculative risk assessment. The earlier Report A
|
||||||
|
content contract below (overall-score donut, domain spine, priority findings,
|
||||||
|
remediation status and source overview) is retained as historical context but
|
||||||
|
does not govern the current Management export.
|
||||||
|
|
||||||
|
Current Report A shows project name, report date, the captured domain and DC
|
||||||
|
count when present, counts of assessed and deviating results, a heatmap of
|
||||||
|
deviations divided by assessed results for each catalog Security Domain, and a
|
||||||
|
second stacked chart of passed and deviating results with the Pass Rate and
|
||||||
|
underlying counts. A domain with no assessed results has no percentage or heat
|
||||||
|
level. The colors and percentages describe result frequency only: they do not
|
||||||
|
express severity, business impact, compromise, protection or progress.
|
||||||
|
|
||||||
|
The GPO domain chart counts assigned result rows. When the separate policy
|
||||||
|
evaluation exists, its failing comparisons and affected settings are stated
|
||||||
|
separately and never equated to result rows. The export names its statement
|
||||||
|
limits: source observations may come from different times; affected business
|
||||||
|
services, exploitation and current remediation state are not derivable from
|
||||||
|
these facts. It does not extrapolate backup age or add invented risk scenarios,
|
||||||
|
owners, measures or deadlines. Technical results remain in Report B. The
|
||||||
|
document is standalone HTML with inline styles and no external resources.
|
||||||
|
|
||||||
|
**User decision 2026-09-30 (Report Engine v0.2):** Directly below the three key
|
||||||
|
figures, Report A shows a `Kernaussagen` block with three deterministic statements.
|
||||||
|
Each is a fixed German sentence into which only projection values are inserted:
|
||||||
|
|
||||||
|
- **Gesamtbild:** assessed and deviating results with their share.
|
||||||
|
- **Schwerpunkt:** the domain or domains with the most deviations and with the
|
||||||
|
highest deviation share. Ties name every tied domain, and the sentence carries
|
||||||
|
the note that share and frequency say nothing about severity or business risk.
|
||||||
|
- **Aussagegrenze:** not run, not applicable, failed and otherwise unassessed
|
||||||
|
checks, which are neither passed nor deviating and enter no rate.
|
||||||
|
|
||||||
|
A statement without a factual basis is omitted. The statements are
|
||||||
|
language-neutral data in the projection (`statements`, built by
|
||||||
|
`src/reporting/report-narrative.ts`). The wording lives in
|
||||||
|
`src/reporting/report-statement-text.ts`, which both renderers share. The
|
||||||
|
Technical Report proves every statement with the same sentence and the result IDs
|
||||||
|
behind each number (`Belege der Kernaussagen`). Report A contains no IDs.
|
||||||
|
|
||||||
|
### Historical Report A design (superseded 2026-09-29)
|
||||||
|
|
||||||
|
Implemented in `src/reporting/management-overview-report.ts`
|
||||||
|
(`MANAGEMENT_OVERVIEW_RENDERER_VERSION = '1.0.0'`), covered by
|
||||||
|
`tests/exports/management-overview-report.test.ts`.
|
||||||
|
|
||||||
|
Answers: how do we stand, what is most urgent, what is this based on, what do we
|
||||||
|
not know. Its reading order follows the agreed Client Info grammar.
|
||||||
|
|
||||||
|
**Named deviation from the `assessor-ui` skill:** a delivered document uses no
|
||||||
|
application shell — no sidebar, topbar, command band or context strip — and none
|
||||||
|
of the four page archetypes, because it is read and printed outside the
|
||||||
|
application. Typography roles, donut geometry, tile and segment grammar, status
|
||||||
|
colour rules and the panel inset apply unchanged. Light is the document default
|
||||||
|
because a report is printed on white; dark renders from the same semantic roles
|
||||||
|
through `prefers-color-scheme`.
|
||||||
|
|
||||||
|
The bundled Inter is referenced by family name with a system fallback. No font,
|
||||||
|
style, script or image is loaded when the file is opened.
|
||||||
|
|
||||||
|
### 1. Assessment identity
|
||||||
|
Customer, environment (domain, forest, domain controller count from the
|
||||||
|
environment inventory), assessment period, report date, ASSESSOR product version.
|
||||||
|
|
||||||
|
### 2. Overall posture
|
||||||
|
The single score as a donut using the shared donut geometry, with its method
|
||||||
|
label. Binding wording from `SCORING_MODEL.md`:
|
||||||
|
|
||||||
|
- The GPO contribution is *conformance of configured, comparable in-scope
|
||||||
|
settings*. Binding German wording: „Konformität der konfigurierten,
|
||||||
|
vergleichbaren Einstellungen im Scope". It is never labelled baseline
|
||||||
|
compliance, baseline coverage or proof that recommended settings are
|
||||||
|
implemented — in German neither „Baseline-Compliance", „Baseline-Abdeckung"
|
||||||
|
nor any phrasing implying the baseline is fully rolled out.
|
||||||
|
- PingCastle enters as `100 − native risk`; the raw risk stays secondary context.
|
||||||
|
- A mixed-method index is named as such. Security Domain values are pass rates
|
||||||
|
and are not a decomposition of the index.
|
||||||
|
|
||||||
|
### 3. Security Domains
|
||||||
|
The eight domains from `src/features/dashboard/security-domains.ts` as a
|
||||||
|
connected pass-rate spine beside the overall score. Not a table.
|
||||||
|
|
||||||
|
### 4. Priority Findings
|
||||||
|
Grouped by security domain. **There is no global cross-tool ranking.** Inside a
|
||||||
|
domain, findings are ordered by the declared priority basis of their own source,
|
||||||
|
and that basis is named at the point of display.
|
||||||
|
|
||||||
|
Per entry: title, domain, the source's own rank value with its basis, one
|
||||||
|
sentence of impact, plus owner and status when a review exists.
|
||||||
|
|
||||||
|
GPO deviations form their own equal-weight block with their count, the affected
|
||||||
|
policy settings and the scope limit. They are never assigned an invented
|
||||||
|
criticality.
|
||||||
|
|
||||||
|
No registry keys, no SIDs, no file paths, no raw values.
|
||||||
|
|
||||||
|
See *Priority without a common severity* below for why.
|
||||||
|
|
||||||
|
### 5. Remediation status
|
||||||
|
Open, in progress, accepted, overdue, and findings without an owner — derived
|
||||||
|
from the stored reviews including accepted-risk data. If no reviews are
|
||||||
|
maintained, the section is omitted rather than rendered empty.
|
||||||
|
|
||||||
|
### 6. Coverage and gaps
|
||||||
|
What was assessed, what was not, unassessed controls, and every scope limit that
|
||||||
|
bounds a statement. A GPO import covering a subset of the domain is reported as
|
||||||
|
scope-limited and never presented as a domain-wide or effective-device claim.
|
||||||
|
This section is what makes the report defensible.
|
||||||
|
|
||||||
|
### 7. Source overview
|
||||||
|
Per tool: run or file date, score contribution, and the method that produced it.
|
||||||
|
|
||||||
|
### Excluded from Report A
|
||||||
|
Per-record tables, raw values, evidence paths, diagnostics, parser, analyzer and
|
||||||
|
manifest versions.
|
||||||
|
|
||||||
|
## Report B — Technical Report
|
||||||
|
|
||||||
|
### Current form (user decisions 2026-10-08, renderer 4.2.0, model 2.1.0, projection 1.5.0)
|
||||||
|
|
||||||
|
The preview corrections of 2026-10-07/08 supersede the section list further below. The
|
||||||
|
Technical Report is one static DIN A4 document (`@page` A4, 210 mm sheet, no forced page
|
||||||
|
breaks), white, without tiles or grey surfaces, in five chapters:
|
||||||
|
|
||||||
|
1. **Information**: the Client Info overview facts of the same snapshot, about half a page:
|
||||||
|
the six Data facts, Domain Information without the counts Data already shows, and the
|
||||||
|
six environment tiles (Resilience, Inventory, Password policy, Certificates & PKI,
|
||||||
|
Operating systems, Cloud Information) with their compact four-fact overviews. Built by
|
||||||
|
the same React-free functions Client Info renders
|
||||||
|
(`src/features/dashboard/client-info-facts.ts`); the GPO count follows the GPO backup
|
||||||
|
list (instances of the latest processed run per backup source).
|
||||||
|
2. **Lagebild**: one factual sentence; Overall Score and the deviations as the only two
|
||||||
|
donuts (ring sizes 2.5 : 2), the six Security Domains with their Client Info deviation
|
||||||
|
counts and amount bars in the Overall Score tone; problem-field key figures.
|
||||||
|
3. **Beziehungen (Pathfinder)**: one transition sentence (domains, problem fields, largest
|
||||||
|
domain and field) and the Security Domain to problem field diagram. No MITRE section.
|
||||||
|
4. **Problemfelder**: every published problem field in exact Context heatmap order, as an
|
||||||
|
ASSESSOR list row (severity stripe, disclosure arrow, name, German name, Base Severity,
|
||||||
|
bar, count, share; an open field brackets its stripe into its top and bottom separator).
|
||||||
|
An open field shows situation and technical lever side by side, its Security Domains and
|
||||||
|
every Work record behind it as a slim line (Work severity hexagon and title). A finding
|
||||||
|
line opens the head of its Finding Reader card: status, the reader's badge fields,
|
||||||
|
MITRE badges, the Context problem field, Source Rule ID, the first-read Problem rows
|
||||||
|
(Result reduced to its statement without appended lists or URLs), a collapsed
|
||||||
|
Description (flattened Markdown restored to headings, steps, lists and safe https
|
||||||
|
links) and the recognised ATT&CK/D3FEND references. `Ohne Problemkategorie` and the sum
|
||||||
|
close the list; the partition reconciles to the Client Info deviation total.
|
||||||
|
5. **Datenbasis und Datenstand**: collection period, imported reports, record counts, all
|
||||||
|
checks by Client Info result state, and the engine and catalog versions.
|
||||||
|
|
||||||
|
Wording states the actual state only; method explanations are not part of the report.
|
||||||
|
Scan tools are never named. Printing opens the problem fields but keeps finding cards
|
||||||
|
closed. Both reports read the same published Context: the Management Report requires it,
|
||||||
|
the Technical Report renders without problem fields when it is unavailable.
|
||||||
|
|
||||||
|
### Earlier rebuild (2026-10-07, superseded where the form above differs)
|
||||||
|
|
||||||
|
Explicit user decision 2026-10-07 supersedes the content inventory below (sections 1 to 6,
|
||||||
|
including the complete per-finding detail and the complete result index): the Technical
|
||||||
|
Report is rebuilt as a *report*, not a list of all findings. It goes technically deeper than
|
||||||
|
the Management Report, uses only categorisations and clusterings that ASSESSOR already has
|
||||||
|
(Context problem fields, Security Domains, native source areas and severities, GPO verdicts,
|
||||||
|
Pathfinder relations), avoids redundant information and uses graphics in the ASSESSOR
|
||||||
|
application design language. It is generated offline without Internet access and without
|
||||||
|
a language model. Renderer 4.0.0 (`src/reporting/technical-report.ts`) with the model
|
||||||
|
`technical-report-model.ts` 1.0.0 and the chart primitives `report-charts.ts`:
|
||||||
|
|
||||||
|
Follow-up user correction 2026-10-07: ASSESSOR is the only source. Scan tools are neither named
|
||||||
|
nor compared; the Pathfinder view connects Security Domains with problem fields; a problem field
|
||||||
|
shows only its situation, technical lever and Security Domains. Renderer 4.1.0, model 2.0.0:
|
||||||
|
|
||||||
|
1. Lagebild: Overall Score, deviations, passed and assessed as full donuts; key figures.
|
||||||
|
2. Problemfelder: every Client Info deviation exactly once in its primary Context
|
||||||
|
cluster (or `Ohne Problemkategorie`) with Base Severity; per problem field the
|
||||||
|
situation, the technical lever and the Security Domains it falls into.
|
||||||
|
3. Beziehungen (Pathfinder): Security Domain to problem field diagram; ATT&CK tactics and
|
||||||
|
techniques separated into explicitly assigned and only mentioned; assigned D3FEND
|
||||||
|
countermeasures.
|
||||||
|
4. Security Domains: pass/deviation per domain.
|
||||||
|
5. GPO-Baseline-Vergleich: all seven GPO verdicts with score-neutral/flag labels,
|
||||||
|
deviations per policy section, contributing and unlinked backups.
|
||||||
|
6. Abdeckung und Aussagegrenzen: all records by result state and reading notes.
|
||||||
|
7. Umgebung (to move into the report header, see the implementation plan).
|
||||||
|
8. Datenstand und Versionen.
|
||||||
|
|
||||||
|
All figures are aggregated from the same Work records, result states and Security Domain
|
||||||
|
classifier as Client Info (AGENTS.md, Report numbers). Result IDs and every original value
|
||||||
|
remain in the archived projection JSON and in ASSESSOR; the HTML does not list them. The
|
||||||
|
Management Report now points to ASSESSOR for individual evidence and original
|
||||||
|
recommendations. Without a Context result of the same state the problem-field section states
|
||||||
|
its absence and every other section remains complete.
|
||||||
|
|
||||||
|
The following historical inventory is retained for traceability only.
|
||||||
|
|
||||||
|
### Historical inventory (superseded 2026-10-07)
|
||||||
|
|
||||||
|
The initial static renderer is implemented in `src/reporting/technical-report.ts`.
|
||||||
|
It retains the current internal result projection, original Work fields when a
|
||||||
|
canonical occurrence matches, and the retained per-backup GPO instructions.
|
||||||
|
Missing source facts are labelled as unavailable; the renderer never invents
|
||||||
|
actual/expected values or an endpoint-effective state. A dedicated completeness
|
||||||
|
audit of diagnostics, reimport metadata and all source formats remains open and
|
||||||
|
must not be mistaken for a final acceptance of every section below.
|
||||||
|
|
||||||
|
Answers: what exactly is configured, what should it be, where does that stand,
|
||||||
|
how does ASSESSOR know, what do I do.
|
||||||
|
|
||||||
|
### 1. Scope and provenance
|
||||||
|
Imported artifacts, processing, analyzer and manifest versions, reimport status,
|
||||||
|
aggregation and evaluation run identifiers. Enough to reproduce the result.
|
||||||
|
|
||||||
|
### 2. Result inventory
|
||||||
|
Totals per status, severity, domain and tool, including the score-neutral
|
||||||
|
classes. Passed results appear here as a count.
|
||||||
|
|
||||||
|
### 3. Findings in detail
|
||||||
|
Grouped by security domain, ordered by severity inside each group. Per finding:
|
||||||
|
|
||||||
|
- technical identity and scope
|
||||||
|
- actual against expected value, with the field-specific deviation marking used
|
||||||
|
by the Finding Reader: `Operation`, `Data` and `Type code` for registry policy
|
||||||
|
differences, several fields only where each is independently proven
|
||||||
|
- principal sets compared per member: only configured principals absent from
|
||||||
|
every retained baseline variant are marked; a principal also present in the
|
||||||
|
expected baseline stays neutral
|
||||||
|
- GPO source attribution as vertically stacked sections per exact backup
|
||||||
|
identity, each with evidence reference, source-local sequence and every
|
||||||
|
original instruction immediately visible
|
||||||
|
- conflict and duplicate flags, kept separate from the policy verdict
|
||||||
|
- baseline variants where more than one exists
|
||||||
|
- the remediation step from the review
|
||||||
|
|
||||||
|
### 4. GPO policy register
|
||||||
|
All seven verdicts in full: `Deviation`, `Passed`, `Not configured`,
|
||||||
|
`No baseline rule`, `Not comparable`, `Conflicts`, `Duplicates`. The
|
||||||
|
score-neutral verdicts are labelled as score-neutral at the point of display, so
|
||||||
|
that a reader cannot mistake `Not configured` for a remediation obligation.
|
||||||
|
|
||||||
|
### 5. Diagnostics
|
||||||
|
Unresolved identities, parser gaps, scope limits, declared-against-counted
|
||||||
|
mismatches and incomplete backups. Each observation appears once with its
|
||||||
|
authoritative resolver reason and is not restated by snapshot aggregation.
|
||||||
|
|
||||||
|
### 6. Appendix
|
||||||
|
Complete result index.
|
||||||
|
|
||||||
|
## Artifact C — Arbeitsgrundlage
|
||||||
|
|
||||||
|
Decision: 2026-09-18, explicitly approved by the user.
|
||||||
|
|
||||||
|
Answers: what do we do, who owns it, by when, where does it stand.
|
||||||
|
|
||||||
|
The measured volume makes this its own artifact: the stored data holds **1,806
|
||||||
|
actual deviations** (CIS-CAT 969, Maester 350, PingCastle 277, Purple Knight 210)
|
||||||
|
plus the GPO deviations. Rendering each of those with full technical depth inside
|
||||||
|
Report B would produce a document nobody works with.
|
||||||
|
|
||||||
|
Size is not the decisive reason, though. A report is a frozen snapshot that
|
||||||
|
proves what was found on a date and is archived unchanged. An Arbeitsgrundlage is
|
||||||
|
a living document that the customer edits for weeks: owner, due date, status,
|
||||||
|
comment. Forcing both into one file makes both worse.
|
||||||
|
|
||||||
|
### Data model
|
||||||
|
|
||||||
|
The Arbeitsgrundlage is the stored review model in `src/domain/review.ts`:
|
||||||
|
`status`, `priority`, `owner`, `due`, `prerequisites`, `access`, `nextStep`,
|
||||||
|
`evidence`, `comment`, the markers `quickwin`, `blocked`, `external`, `question`
|
||||||
|
and `notRelevant`, plus `riskAcceptance`.
|
||||||
|
|
||||||
|
`include` is the curation switch. The consultant selects what reaches the
|
||||||
|
customer as a work item. Shipping 1,806 unfiltered items produces no action at
|
||||||
|
all.
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
- **Sheet 1 — Maßnahmen.** Only the curated selection, with a stated selection
|
||||||
|
criterion. One row per work item.
|
||||||
|
- **Sheet 2 — Vollständige Abweichungen.** All deviations for reference, so the
|
||||||
|
customer can pull further items in without the working list overflowing.
|
||||||
|
|
||||||
|
The HTML variant renders the same content as a frozen status view for status
|
||||||
|
meetings and filing. It is generated from the same projection, never maintained
|
||||||
|
separately.
|
||||||
|
|
||||||
|
### No duplicated technical depth
|
||||||
|
|
||||||
|
A work item carries its stable finding identifier, the measure, prerequisites,
|
||||||
|
required access and the owner — not the evidence, not the source attribution, not
|
||||||
|
the baseline variants. Those stay in Report B and are reached through the
|
||||||
|
identifier.
|
||||||
|
|
||||||
|
One fact, one place. Repeating the technical detail would leave two diverging
|
||||||
|
versions after the first correction.
|
||||||
|
|
||||||
|
## Progress is proven by rescan
|
||||||
|
|
||||||
|
Decision: 2026-09-18, explicitly approved by the user: *"Den Fortschritt
|
||||||
|
dokumentiere ich durch Rescans — für mich zählt nur der technische Beweis."*
|
||||||
|
|
||||||
|
Remediation progress is established by comparing scan runs, never by a status
|
||||||
|
somebody ticked. This follows `TIMESLOT_COMPARISON_PLAN.md`, which already
|
||||||
|
replaced the conclusion *absent equals remediated*.
|
||||||
|
|
||||||
|
### Binding rules
|
||||||
|
|
||||||
|
- No artifact presents a self-reported status as a resolved finding. A work item
|
||||||
|
marked done in the Arbeitsgrundlage stays an open deviation in every report
|
||||||
|
until a rescan proves otherwise.
|
||||||
|
- A delta uses the lifecycle vocabulary of the timeslot contract — demonstrably
|
||||||
|
remediated, still open, unresolved, not comparable — never a binary
|
||||||
|
done/open. *Unresolved* is a legitimate and frequent outcome and is shown as
|
||||||
|
such.
|
||||||
|
- Absence of a finding is not proof of remediation. Remediation is claimed only
|
||||||
|
where the profile-bound negative-proof contract is satisfied for exactly that
|
||||||
|
rule and object.
|
||||||
|
- Result presence and deviation lifecycle stay separate. A rule can still be
|
||||||
|
reported while its concrete deviation is fixed.
|
||||||
|
- No effective-endpoint claim is derived from a backup-configuration comparison.
|
||||||
|
|
||||||
|
### Why the caveat is not theoretical
|
||||||
|
|
||||||
|
The stored data shows how easily absence would be misread: Maester carries **845
|
||||||
|
`not_run`** and 10 `execution_error`, Purple Knight 34 `not_applicable` and 10
|
||||||
|
`not_run`. A check that did not run would otherwise turn into a success message —
|
||||||
|
the most dangerous false statement a progress report can make, because it errs in
|
||||||
|
the comfortable direction.
|
||||||
|
|
||||||
|
### Artifact D — Delta Report
|
||||||
|
|
||||||
|
An own artifact per rescan, in the same two cuts as the reports. The initial
|
||||||
|
report stays frozen and is never rewritten; the change is a separate document
|
||||||
|
with its own reporting date.
|
||||||
|
|
||||||
|
**The comparison is against a chosen reference run, and that choice is made in
|
||||||
|
ASSESSOR — not at export time.** The export renders a comparison that already
|
||||||
|
exists; it contains no run-selection logic, no fallback to "the previous run" and
|
||||||
|
no implicit pairing of its own. Consequences:
|
||||||
|
|
||||||
|
- The chosen reference run is part of the stored comparison, together with its
|
||||||
|
contract version, engine versions and input fingerprint. The export reads it
|
||||||
|
and never re-derives it.
|
||||||
|
- The document names both compared runs explicitly — reference and current, each
|
||||||
|
with its identity and date — so it stays self-describing once it is out of the
|
||||||
|
application.
|
||||||
|
- Without a stored comparison there is no Delta Report. The export is
|
||||||
|
unavailable rather than guessing a pairing.
|
||||||
|
|
||||||
|
This keeps comparison a domain concern and presentation a separate one: the Delta
|
||||||
|
Report is a renderer, not a second comparison engine.
|
||||||
|
|
||||||
|
- **Management cut:** score movement, what is demonstrably remediated, what
|
||||||
|
persists, what is newly found, and how much of the comparison is unresolved or
|
||||||
|
not comparable. The unresolved share is stated, not hidden.
|
||||||
|
- **Technical cut:** per finding the lifecycle verdict with its evidence — which
|
||||||
|
run, which identity match, which contract qualified it — plus the
|
||||||
|
disqualifying diagnostics where a comparison was not admissible.
|
||||||
|
|
||||||
|
### Planning fields in the Arbeitsgrundlage
|
||||||
|
|
||||||
|
`status`, `due`, `owner` and the markers stay in the XLSX as the customer's own
|
||||||
|
steering aid. They are labelled as unverified self-report at the point of display.
|
||||||
|
ASSESSOR never promotes them into a report as a result.
|
||||||
|
|
||||||
|
The export therefore stays one-way by design, not by limitation: progress does not
|
||||||
|
need a return channel because it comes from the next scan.
|
||||||
|
|
||||||
|
## Priority without a common severity
|
||||||
|
|
||||||
|
Decision: 2026-09-18, explicitly approved by the user.
|
||||||
|
|
||||||
|
Severity is not a property every scanner delivers. Measured against the stored
|
||||||
|
data:
|
||||||
|
|
||||||
|
| Tool | Results | Severity coverage | Native ranking signal |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| CIS-CAT | 1,233 | none, all `unknown` | none |
|
||||||
|
| PingCastle | 277 | derived from Points | Points 0–50+ |
|
||||||
|
| Maester | 1,590 | 99.4 % | native severity |
|
||||||
|
| Purple Knight | 1,309 | 100 %, 187 `critical` | native severity |
|
||||||
|
| GPO / baseline | — | none | none |
|
||||||
|
|
||||||
|
A severity-ranked global list would therefore drop CIS-CAT and GPO out of the
|
||||||
|
Priority Findings entirely, let Purple Knight dominate merely because it is the
|
||||||
|
only source that emits `critical` natively, and keep PingCastle permanently below
|
||||||
|
`critical` because `pingCastleSeverityFromPoints` caps at `high` by construction.
|
||||||
|
That ordering would describe the report formats, not the customer's risk.
|
||||||
|
|
||||||
|
### Binding rules
|
||||||
|
|
||||||
|
- Priority Findings are grouped by security domain. The eight domains are
|
||||||
|
tool-neutral and already exist.
|
||||||
|
- Within a domain, findings are ordered by the declared priority basis of their
|
||||||
|
own source. The basis is visible to the reader.
|
||||||
|
- No ranking is ever produced across sources with incomparable bases. A single
|
||||||
|
ordered list that mixes them is not permitted.
|
||||||
|
- GPO deviations are equal-weight. They get their own block with count, affected
|
||||||
|
policy settings and scope limit, and never an inferred criticality.
|
||||||
|
- A source without any defensible ranking basis lists its findings unranked and
|
||||||
|
says so. Unranked is an acceptable answer; an invented rank is not.
|
||||||
|
|
||||||
|
### Profile obligation
|
||||||
|
|
||||||
|
Implemented as `ToolPriorityBasisDefinition` in
|
||||||
|
`src/domain/tool-profile-definition.ts`, required on every `ToolProfileDefinition`
|
||||||
|
and enforced by `tests/profiles/declarative-profiles.test.ts`.
|
||||||
|
|
||||||
|
Analogous to the existing `securityScoring` obligation, every tool profile
|
||||||
|
declares a **priority basis**: the exact mapped properties, scale, direction and
|
||||||
|
semantics of the value used to order its findings — or an explicit reason why no
|
||||||
|
usable basis exists. Never infer a priority from an arbitrary field named
|
||||||
|
severity, level, score or rank.
|
||||||
|
|
||||||
|
A declaration may also list `excludedProperties`: fields that resemble a basis
|
||||||
|
but are not one, each with its reason. This is what stops a later change from
|
||||||
|
quietly promoting them.
|
||||||
|
|
||||||
|
| Profile | Declared basis |
|
||||||
|
| --- | --- |
|
||||||
|
| Purple Knight | `severity` — native criticality, the only source reaching `critical` without derivation |
|
||||||
|
| Maester | `severity` — native per test |
|
||||||
|
| PingCastle | `numeric` on Points, `0-50+`, higher-is-worse |
|
||||||
|
| CIS-CAT | `unavailable` — benchmarks state no per-recommendation criticality |
|
||||||
|
| Generic | `unavailable` — unrecognized fields establish no scale or direction |
|
||||||
|
|
||||||
|
PingCastle is declared on Points rather than severity on purpose: its canonical
|
||||||
|
severity is *derived* from Points by `pingcastle-severity-from-points` and is
|
||||||
|
capped at `high` by construction. Points keeps the granularity the capped
|
||||||
|
severity loses.
|
||||||
|
|
||||||
|
`reportPriorityBasisFrom` turns an `unavailable` declaration into an explicit
|
||||||
|
`equal-weight` basis carrying the profile's own reason, so a report states why a
|
||||||
|
source is unranked instead of implying the declaration is missing.
|
||||||
|
|
||||||
|
A derived, normalized cross-tool ASSESSOR priority is **not** approved. It would
|
||||||
|
require its own versioned rule and an explicit decision of its own.
|
||||||
|
|
||||||
|
### Guarded mis-mapping risk
|
||||||
|
|
||||||
|
`src/profiles/cis-cat/cis-cat-definition.ts` declares
|
||||||
|
`severityHeaders: /^(?:severity|level)$/i`. CIS `Level 1` / `Level 2` is a
|
||||||
|
hardening profile tier, not a criticality. It falls through to `unknown`, which
|
||||||
|
is harmless, and it is now recorded as an excluded property with its reason and
|
||||||
|
covered by a test. It must not be "corrected" into a severity, because that would
|
||||||
|
manufacture criticalities the benchmark never stated.
|
||||||
|
|
||||||
|
## Report Engine boundary
|
||||||
|
|
||||||
|
Decision: 2026-09-30, explicitly approved by the user. Implementation plan: Phase 7,
|
||||||
|
*Report Engine als eigenstaendiger Nur-Lese-Baustein*.
|
||||||
|
|
||||||
|
The Report Engine is a self-contained, read-only building block inside ASSESSOR.
|
||||||
|
It produces the reports offline, without an LLM, and never changes ASSESSOR
|
||||||
|
logic or data.
|
||||||
|
|
||||||
|
- **Location.** The projection, correlation, input assembler,
|
||||||
|
`client-report-projection` and both renderers were moved unchanged to
|
||||||
|
`src/reporting/` on 2026-09-30. The shared security-domain catalog, scoring and review
|
||||||
|
hydration stay ASSESSOR components and are only imported, so that a report
|
||||||
|
number always equals the Client Info and Work number. An architecture test
|
||||||
|
enforces the boundary.
|
||||||
|
- **Read-only access.** The engine reads through a read-only repository port and
|
||||||
|
a dedicated read-only database pool. It detects a repository revision change
|
||||||
|
during assembly and repeats the read instead of mixing states.
|
||||||
|
- **Server rendering.** Since v0.1 the server renders both files once per run and
|
||||||
|
archives them. Client Info downloads exactly the archived file through
|
||||||
|
`POST /api/v1/projects/:id/report-runs`.
|
||||||
|
- **Frozen state.** Every report run is archived server-side in its own folder
|
||||||
|
and never modified afterwards. The folder holds `projection.json`, both HTML
|
||||||
|
files and a manifest with file hashes, engine versions, repository revision,
|
||||||
|
scope and time. The archive lives outside the repository and outside synced
|
||||||
|
folders (`ASSESSOR_REPORT_ARCHIVE_DIR`) and is deleted together with its
|
||||||
|
client. `projection.json` and the manifest are internal. They are never
|
||||||
|
delivered with Report A, which keeps the separate-files confidentiality
|
||||||
|
boundary.
|
||||||
|
- **Boundaries.**
|
||||||
|
- Only the shared security-domain catalog.
|
||||||
|
- Severity only per source, never summed across scanners.
|
||||||
|
- Review state only as unverified self-report, and never in Report A.
|
||||||
|
- Scenarios and cross-finding relationships only from the context engine
|
||||||
|
(Phase 13E), never from report-local rules.
|
||||||
|
|
||||||
|
## Guardrails for both reports
|
||||||
|
|
||||||
|
- Score-neutral classes never appear in Priority Findings and never enter the
|
||||||
|
score. They remain fully inspectable in Report B.
|
||||||
|
- A GPO verdict is taken only from the authoritative rolled-up policy verdict and
|
||||||
|
the explicit conflict and duplicate flags. Never from comparison prose,
|
||||||
|
historical results, baseline text, guidance or evidence.
|
||||||
|
- Unknown or unavailable facts render as an em dash, never as an inferred zero.
|
||||||
|
- Both files are self-contained: inline styles, no CDN, no external fonts,
|
||||||
|
scripts or images, no network access when opened. A report leaves the building
|
||||||
|
and must not load anything.
|
||||||
|
- The bundled Inter variable font is embedded or a local system stack is used;
|
||||||
|
no font is downloaded at view time.
|
||||||
|
- Shared typography roles, donut geometry, marking semantics and panel inset
|
||||||
|
apply as in the application. Status colour stays in accent lines, markers,
|
||||||
|
arcs and score nodes, never as a full status fill.
|
||||||
|
- Static output: no JavaScript, printable to PDF without layout loss.
|
||||||
|
|
||||||
|
## Relationship to the existing export
|
||||||
|
|
||||||
|
### Current-view export — explicit user extension, 2026-09-18
|
||||||
|
|
||||||
|
The Work toolbar's CSV/MD/HTML buttons now export the current filtered and sorted
|
||||||
|
view, including Results, with exactly two content columns: `Soll-Zustand` and
|
||||||
|
`Abweichungen`. This supersedes their old marked-only selection. The user can
|
||||||
|
still restrict the view with `My Workspace`; that restriction combines with the
|
||||||
|
other active filters. A view export is a frozen, scoped inspection snapshot,
|
||||||
|
not the curated Arbeitsgrundlage C, a replacement for reports A/B, or a delta.
|
||||||
|
|
||||||
|
`src/exports/work-view-export.ts` supplies one click-time presentation projection
|
||||||
|
for these three formats, consuming existing Work records and the shared Results
|
||||||
|
resolver without ranking, scoring, re-evaluation or database queries. The shared
|
||||||
|
`report-projection.ts` contract for artifacts A–D remains unchanged. Native
|
||||||
|
remediation, baseline variants, exact source attribution and verification gaps
|
||||||
|
stay visible. Review completion never becomes a technical resolution. Long CSV
|
||||||
|
cells are preserved in labelled continuation rows (same finding ID), keeping two
|
||||||
|
columns and avoiding Excel's cell-size truncation. HTML is self-contained,
|
||||||
|
script-free, with locally bundled Inter embedded and responsive wrapping.
|
||||||
|
|
||||||
|
The following paragraph records the earlier strategy baseline; its description
|
||||||
|
of the toolbar's marked-only behaviour has been superseded by this extension.
|
||||||
|
|
||||||
|
`src/exports/dashboard-export.ts` and `ActionlistControls` already export the
|
||||||
|
review model as CSV, Markdown and a minimal HTML page. That HTML knows nothing of
|
||||||
|
the score, the security domains, GPO verdicts or provenance and uses Segoe UI
|
||||||
|
with fixed colours.
|
||||||
|
|
||||||
|
The existing action-list export is the seed of artifact C, not of the reports.
|
||||||
|
The reports are new; the action-list HTML is replaced by the artifact C renderer,
|
||||||
|
and the CSV path stays useful for ticket-system import.
|
||||||
|
|
||||||
|
## Open decisions
|
||||||
|
|
||||||
|
- How many findings each domain block shows before it is truncated, and how a tie
|
||||||
|
inside one source's ranking basis is broken.
|
||||||
|
- Whether the Microsoft baseline packages carry any per-setting classification
|
||||||
|
that could later give GPO deviations a defensible basis. Until proven, GPO
|
||||||
|
stays equal-weight.
|
||||||
|
- The selection criterion wording for the curated sheet, and whether it is fixed
|
||||||
|
or written per engagement.
|
||||||
|
- Which profiles can satisfy a negative-proof contract at all. Profiles that
|
||||||
|
cannot will never produce a *demonstrably remediated* verdict, and the Delta
|
||||||
|
Report must say so per source rather than leave the reader guessing.
|
||||||
|
- File naming convention across all artifacts, including how a delta names its
|
||||||
|
two compared runs.
|
||||||
|
|
||||||
|
## Dependency
|
||||||
|
|
||||||
|
Artifact D depends on the timeslot comparison actually producing stored results.
|
||||||
|
`assessor_timeslot_evidence_comparisons` exists with its contract version, engine
|
||||||
|
versions and input fingerprint, but currently holds no rows. Until the comparison
|
||||||
|
is implemented per `TIMESLOT_COMPARISON_PLAN.md`, artifacts A, B and C are
|
||||||
|
deliverable and D is not.
|
||||||
|
|
||||||
|
## Change log
|
||||||
|
|
||||||
|
- 2026-09-18: Strategy established. Delivery, design, technical scope and
|
||||||
|
interaction decided by the user; content inventory per report fixed.
|
||||||
|
- 2026-09-18: Report language decided by the user — German for both reports. The
|
||||||
|
agreed GPO verdict vocabulary and the security domain names stay canonical
|
||||||
|
English inside the German text; source values are never localized. The Client
|
||||||
|
Info English-only rule is unaffected.
|
||||||
|
- 2026-09-18: User correction — established English technical terminology stays
|
||||||
|
English as a general rule, not as an enumerated exception. German carries the
|
||||||
|
prose; technical terms are never translated or turned into German compounds.
|
||||||
|
- 2026-09-18: Priority model decided by the user after the user raised that not
|
||||||
|
every tool provides severity. Verified against stored data: CIS-CAT and GPO
|
||||||
|
have no severity at all, PingCastle cannot reach `critical` by construction.
|
||||||
|
Priority Findings are grouped by security domain with a per-source, declared
|
||||||
|
and visible ranking basis; no cross-tool ranking; GPO equal-weight in its own
|
||||||
|
block. Profiles must declare a priority basis. A normalized cross-tool
|
||||||
|
priority is explicitly not approved.
|
||||||
|
- 2026-09-18: Third artifact added on the user's proposal. The per-finding
|
||||||
|
working basis becomes its own deliverable — XLSX to work in plus an HTML status
|
||||||
|
view, curated through `include` with the complete deviation list on a second
|
||||||
|
sheet. Decisive reason is the differing lifecycle, not only the measured volume
|
||||||
|
of 1,806 deviations. Work items reference findings by identifier and never
|
||||||
|
repeat the technical depth of Report B.
|
||||||
|
- 2026-09-18: Progress model fixed by the user — progress is documented by
|
||||||
|
rescans, only technical proof counts. A fourth artifact is added: the Delta
|
||||||
|
Report, in a management and a technical cut, using the timeslot lifecycle
|
||||||
|
vocabulary. Self-reported status never becomes a reported result; planning
|
||||||
|
fields remain in the Arbeitsgrundlage marked as unverified. The earlier open
|
||||||
|
question about re-importing a maintained worklist is closed: the export is
|
||||||
|
one-way by design.
|
||||||
|
- 2026-09-18: Reference run decided by the user — a delta compares against a
|
||||||
|
chosen reference run, and the choice is made in ASSESSOR. The export renders a
|
||||||
|
stored comparison and carries no run-selection logic, no previous-run fallback
|
||||||
|
and no implicit pairing. Both runs are named in the document.
|
||||||
|
- 2026-09-18: Shared projection implemented as `src/domain/report-projection.ts`.
|
||||||
|
The security-domain assignment is injected by the caller so the domain layer
|
||||||
|
never points at the feature layer. Profiles do not yet declare a priority
|
||||||
|
basis, so the projection accepts one per source and falls back to `undeclared`
|
||||||
|
with its own diagnostic rather than guessing. Declaring the basis inside
|
||||||
|
`ToolProfileDefinition` is the follow-up step.
|
||||||
|
- 2026-09-18: Profile obligation implemented. `ToolPriorityBasisDefinition` is
|
||||||
|
required on every registered profile and enforced by a contract test. Purple
|
||||||
|
Knight and Maester declare native severity, PingCastle declares Points because
|
||||||
|
its severity is derived and capped, CIS-CAT and Generic declare unavailable
|
||||||
|
with a reason. CIS `Level` is recorded as an excluded property so it cannot be
|
||||||
|
promoted into a criticality later.
|
||||||
|
- 2026-09-18: Management Overview renderer implemented as
|
||||||
|
`src/exports/management-overview-report.ts`. Static, self-contained German
|
||||||
|
HTML with no script, inline handler, external URL or `url()`. Deviation from
|
||||||
|
the `assessor-ui` shell and page archetypes named above; all token, typography,
|
||||||
|
donut and status rules kept. An out-of-range score is clamped and a missing one
|
||||||
|
draws no arc at all.
|
||||||
|
- 2026-09-30: Report Engine boundary decided by the user. The report modules move
|
||||||
|
unchanged to `src/reporting/` and access is read-only. Every run is archived
|
||||||
|
immutably server-side, outside the repository and synced folders. Domain,
|
||||||
|
severity, review-state and scenario boundaries are confirmed, with scenarios
|
||||||
|
only from the Phase 13E context engine. Documentation only; no
|
||||||
|
implementation yet.
|
||||||
|
- 2026-09-30: Report modules moved unchanged to `src/reporting/`, with their
|
||||||
|
tests in `tests/reporting/`. `tests/reporting/reporting-boundary.test.ts`
|
||||||
|
enforces the importer allow-list and the absence of writing repository calls.
|
||||||
|
Output and numbers are unchanged.
|
||||||
|
- 2026-09-30: Report Engine v0.1 (product 2.10.0). A read-only port, a PostgreSQL
|
||||||
|
reader with read-only sessions and a revision check before and after the read
|
||||||
|
(repeat, otherwise refuse) ensure a consistent read. Every run is archived
|
||||||
|
write-once on the server. The Client Info buttons now download the
|
||||||
|
server-rendered, archived file, and the browser no longer renders reports.
|
||||||
|
Report content is unchanged.
|
||||||
|
- 2026-09-30: Report Engine v0.2. The user chose the statements Gesamtbild,
|
||||||
|
Schwerpunkt and Aussagegrenze, placed below the key figures, with evidence in the
|
||||||
|
Technical Report and the archive. Projection 1.2.0 adds ID lists per domain and
|
||||||
|
per coverage group plus the statements. The narrative module is 1.0.0, the
|
||||||
|
Management renderer 2.1.0 and the Technical renderer 1.1.0, whose result index
|
||||||
|
now shows the result ID.
|
||||||
|
- 2026-10-07: Technical Report rebuilt on explicit user request as a report instead of
|
||||||
|
a finding list (renderer 4.0.0, model 1.0.0, projection 1.4.0 with a lean Work cut 2.0.0
|
||||||
|
carrying native area, rule ID and Pathfinder ATT&CK/D3FEND relations). The complete
|
||||||
|
result index and per-finding detail are dropped from the HTML; Result IDs stay in the
|
||||||
|
archive projection. Management renderer 3.3.1 adjusts its pointer to the Technical Report.
|
||||||
|
- 2026-10-07: User correction to the rebuilt Technical Report: single source ASSESSOR, no
|
||||||
|
scan-tool names or per-tool comparison (score composition, source tiles, source chips,
|
||||||
|
multi-source corroboration, lead checks, imported-report and diagnostic tables removed),
|
||||||
|
Security Domain to problem field diagram, lean problem-field cards. Renderer 4.1.0, model 2.0.0.
|
||||||
|
- 2026-10-08: Technical Report finalised from the user-reviewed Dummy preview and moved into
|
||||||
|
production (renderer 4.2.0, model 2.1.0, projection 1.5.0, Work cut 2.1.0 with the Finding
|
||||||
|
Reader card head per deviation): DIN A4, chapter order Information, Lagebild, Beziehungen,
|
||||||
|
Problemfelder, Datenbasis; heatmap order; MITRE section removed; problem-field rows with
|
||||||
|
finding lines and cards; Client Info facts in Information; the technical run now reads the
|
||||||
|
published Context. ASSESSOR 2.18.0.
|
||||||
|
|
@ -104,7 +104,7 @@ Jede Ableitung trägt `ioe_rule_id`, `ioe_rule_version`, `ioe_derivation_method`
|
||||||
| CIS-CAT | XCCDF informational | compliance_check | xccdf_result = informational | informational | false |
|
| CIS-CAT | XCCDF informational | compliance_check | xccdf_result = informational | informational | false |
|
||||||
| GPO vs. Baseline | Abweichung | policy_deviation | verdict = deviation | fail | false |
|
| GPO vs. Baseline | Abweichung | policy_deviation | verdict = deviation | fail | false |
|
||||||
| GPO vs. Baseline | Uebereinstimmung | policy_deviation | verdict = match | pass | false |
|
| GPO vs. Baseline | Uebereinstimmung | policy_deviation | verdict = match | pass | false |
|
||||||
| GPO vs. Baseline | Setting fehlt beim Subjekt | policy_deviation | verdict = missing_on_subject | fail | false |
|
| GPO vs. Baseline | Setting fehlt beim Subjekt | policy_deviation | verdict = missing_on_subject | informational | false |
|
||||||
| GPO vs. Baseline | Setting nur beim Subjekt | policy_deviation | verdict = extra_on_subject | warning | false |
|
| GPO vs. Baseline | Setting nur beim Subjekt | policy_deviation | verdict = extra_on_subject | warning | false |
|
||||||
| GPO vs. Baseline | nicht vergleichbar | policy_deviation | verdict = not_comparable | unknown | false |
|
| GPO vs. Baseline | nicht vergleichbar | policy_deviation | verdict = not_comparable | unknown | false |
|
||||||
|
|
||||||
|
|
@ -404,9 +404,144 @@ Vertrags wird bei Bedarf daraus abgeleitet, nicht umgekehrt gespeichert.
|
||||||
`ioe_derivation_method` sowie `exposure_state` sind additive Spalten auf der
|
`ioe_derivation_method` sowie `exposure_state` sind additive Spalten auf der
|
||||||
bestehenden Occurrence-Tabelle (`assessor_finding_occurrences`,
|
bestehenden Occurrence-Tabelle (`assessor_finding_occurrences`,
|
||||||
Migration `008_result_contract_facets.sql`), nicht Spalten einer neuen
|
Migration `008_result_contract_facets.sql`), nicht Spalten einer neuen
|
||||||
Haupttabelle. `result_compliance` und `result_deviation` als eigene Blöcke
|
Haupttabelle. `result_compliance` und `result_deviation` wurden nicht als neue
|
||||||
wurden nicht angelegt, da für beide noch kein Producer existiert
|
Haupttabellen angelegt. GPO-/Baseline-Vergleiche stammen aus den versionierten
|
||||||
(CIS-CAT-Formatentscheidung offen, GPO-/Baseline-Submodell deferiert).
|
Policy-Evaluation-Entitäten und werden nur im Read-Modell vereinheitlicht.
|
||||||
|
|
||||||
|
### Datenschutzgrenze der Result DB
|
||||||
|
|
||||||
|
Die Result DB ist eine fail-closed, LLM-faehige Offenlegungsprojektion und kein
|
||||||
|
Rohdaten- oder Kundenkontextspeicher. Seit Disclosure Policy 2.1.0 duerfen
|
||||||
|
regulaere kanonische Findings und GPO-Vergleiche keinen personen- oder
|
||||||
|
kundenidentifizierenden Inhalt transportieren. Bei den explizit bekannten
|
||||||
|
Scannerprofilen CIS-CAT, Maester, PingCastle und Purple Knight bleiben jedoch
|
||||||
|
regelbezogene Titel, Beschreibungen und Empfehlungen erhalten, damit die
|
||||||
|
Result DB fachlich und fuer ein LLM nutzbar bleibt. Vor der Ausgabe ersetzt die
|
||||||
|
Projektion exakte private Begleitwerte und feste Identifierklassen durch
|
||||||
|
typisierte Platzhalter und wandelt HTML-Regelprosa in Klartext um. Assets,
|
||||||
|
Personen-/Reviewfelder, Tenant-/Host-/GPO-/Backupidentitaeten, Locators,
|
||||||
|
Quellzeitpunkte und interne Artefaktprovenienz bleiben ausschliesslich im
|
||||||
|
internen Analysepfad. Unbekannte Werkzeuge und generische Quellen fallen
|
||||||
|
weiterhin auf kontrollierte Bezeichnungen ohne Quellprosa zurueck.
|
||||||
|
|
||||||
|
Disclosure Policy 2.5.0 erweitert die freigegebenen PingCastle-Rule-IDs auf den
|
||||||
|
oeffentlich dokumentierten Regelkatalog. Die Validierung ist
|
||||||
|
gross-/kleinschreibungsunabhaengig, die Ausgabe behaelt die native Schreibweise;
|
||||||
|
unbekannte IDs bleiben aus der LLM-faehigen Result DB ausgeschlossen.
|
||||||
|
|
||||||
|
Disclosure Policy 2.5.0 zieht fuer GPO-Vergleiche die verbindliche Grenze
|
||||||
|
zwischen technischer Abweichung und Quellenidentitaet. Titel, technische
|
||||||
|
Setting-ID, Policy-Pfad, Value Name, Operation, Registry-Typ, konfigurierter
|
||||||
|
Wert, erwarteter Wert, Comparator-Relation, Status, oeffentliche
|
||||||
|
Baselinebezeichnung/-version, oeffentliche Beschreibung/Empfehlung, belegte
|
||||||
|
Diagnosen, Verarbeitungsstaende und die Aussagegrenze des Backupvergleichs
|
||||||
|
duerfen vollstaendig erscheinen. Kunden-/Projektname, Domainlabel,
|
||||||
|
kundenspezifischer GPO-Name, GPO-/Backup-GUID, Datei-/Sharepfad, interne
|
||||||
|
Projekt-/Assessment-/Run-/Import-/Artefaktidentitaet, Source Locator,
|
||||||
|
Zeitpunkte und Review-/Risikoakzeptanzdaten bleiben ausgeschlossen.
|
||||||
|
`policyIdentityKey` und interne Baseline-IDs werden nicht ausgegeben; die
|
||||||
|
technische Setting-ID steht ausschliesslich als kontrollierte `Source Rule ID`.
|
||||||
|
Automatisch erzeugte `gpo-policy-identity-*`-Hashes sind niemals oeffentliche
|
||||||
|
Regelidentitaeten.
|
||||||
|
|
||||||
|
Wohlbekannte oeffentliche Principals wie `S-1-5-18`, `S-1-5-32-544` und
|
||||||
|
`BUILTIN\\Administrators` bleiben technisch lesbar. Domain-SIDs verlieren ihre
|
||||||
|
eindeutigen Domainkomponenten (`S-1-5-21-[DOMAIN]-<RID>`), bekannte
|
||||||
|
Domainrollen werden als `[DOMAIN]\\<Rolle>` dargestellt und sonstige konkrete
|
||||||
|
Benutzer/Gruppen erhalten stabile, nur innerhalb des einzelnen Resultats
|
||||||
|
gueltige Platzhalter `[DOMAIN_PRINCIPAL_n]`. Dieselbe Reduktion gilt rekursiv
|
||||||
|
fuer Baselinevarianten. Die Originalzuordnung bleibt ausschliesslich in Work
|
||||||
|
und GPO Analysis erhalten.
|
||||||
|
|
||||||
|
Die fachlichen Bewertungsachsen bleiben erhalten: normalisierte Severity und
|
||||||
|
Outcome, Result-Typ, Abweichung/IOE, kontrollierte GPO-Klassifikation,
|
||||||
|
Comparator-Relation sowie nicht identifizierende Aggregate und technische
|
||||||
|
Versionsmetadaten. Damit bleibt die Sicherheitsbewertung fuer ein LLM nutzbar,
|
||||||
|
ohne Kunden- oder konkrete GPO-Quellenidentitaet weiterzugeben. Die Aussage bezieht sich auf
|
||||||
|
Identifizierbarkeit; ein Finding beschreibt weiterhin den anonymisierten
|
||||||
|
Sicherheitszustand des aktiv ausgewaehlten Scopes.
|
||||||
|
|
||||||
|
Ergaenzende Scannerbeobachtungen durchlaufen vor der Unified-Result-Projektion
|
||||||
|
die versionierte, fail-closed Disclosure Policy
|
||||||
|
`result-db-supplemental-disclosure` 1.0.0. Eine Beobachtung wird nur ausgegeben,
|
||||||
|
wenn Werkzeug, Property und ein enger Datentyp-/Wertebereich ausdruecklich
|
||||||
|
freigegeben sind. Titel, Subject-Klasse und Property werden aus der kontrollierten
|
||||||
|
Regel neu aufgebaut. Subject-Identitaet, Originalwert, Attribute, betroffener
|
||||||
|
Assetname, Abschnitt und Source Locator werden niemals aus der Beobachtung in
|
||||||
|
die Result DB uebernommen. Unbekannte Werkzeuge und neue Zusatzfelder bleiben
|
||||||
|
vollstaendig in der internen Persistenz, sind aber bis zu einer bewussten
|
||||||
|
Klassifikation nicht Teil der Result DB. Das betrifft nicht ihre regulaeren
|
||||||
|
kanonischen Findings.
|
||||||
|
|
||||||
|
Rohe GPO-Policy-Beobachtungen sind ebenfalls kein Result-DB-Inhalt. Sie bleiben
|
||||||
|
im expliziten `internal-analysis`-Lesemodus fuer Work und GPO Analysis erhalten;
|
||||||
|
versioniert evaluierte Soll-/Ist-Vergleiche erreichen die normale Result DB als
|
||||||
|
`policy_deviation`. Ohne freigegebene Risikoregel bleibt die Severity `unknown`;
|
||||||
|
die Finding Factory darf nur belegte Ergebnisse anreichern und erzeugt keine
|
||||||
|
doppelte Result-Zeile. Der JSONL-Export verwendet Schema 2.0.0 und Disclosure
|
||||||
|
Policy 2.5.0. Er wendet die allgemeine Projektion nochmals an, verlangt fuer
|
||||||
|
jede Environment-Zeile weiterhin eine Disclosure-Klassifikation und entfernt
|
||||||
|
zusaetzlich interne Result-, Projekt-, Assessment-, Run-, Snapshot-, Evaluation-
|
||||||
|
und Artefaktidentitaeten sowie Zeitpunkte. Manifest-Scope und Filter werden nur
|
||||||
|
als Boolesche beziehungsweise Mengenangaben ausgegeben; Suchtext, Assetfilter
|
||||||
|
und konkrete Scope-IDs werden nie serialisiert.
|
||||||
|
|
||||||
|
Disclosure Policy 2.6.0 (2026-09-28) schliesst eine Luecke zur obigen Grenze:
|
||||||
|
`reviewStatus`, `reviewDisposition` (einschliesslich akzeptierter Risiken) und
|
||||||
|
`reviewPriority` gelangten zuvor in Result DB API und JSONL. Die Projektion gibt
|
||||||
|
jetzt nur noch Felder aus, die in `RESULT_DB_FIELD_DISCLOSURE` ausdruecklich als
|
||||||
|
`disclose` klassifiziert sind; ein neues Projektionsfeld verhindert die
|
||||||
|
Kompilierung, bis es klassifiziert ist, und zur Laufzeit hinzugefuegte Felder
|
||||||
|
fallen weg. Review-Status und -Disposition sind deshalb keine Result-DB-Filter
|
||||||
|
mehr; Work und die interne Analyse behalten den Review unveraendert. JSONL-Schema
|
||||||
|
2.1.0 ergaenzt im Manifest `handling` mit der Klassifikation
|
||||||
|
`confidential-security-posture`: Die Result DB ist anonymisiert, beschreibt aber
|
||||||
|
weiterhin ausnutzbare Schwaechen der bewerteten Umgebung und ist bei Weitergabe
|
||||||
|
an externe LLM-Anbieter oder Dritte entsprechend zu behandeln.
|
||||||
|
|
||||||
|
Disclosure Policy 3.0.0 und JSONL-Schema 3.0.0 (2026-09-28, verbindliche
|
||||||
|
Nutzeranforderung, ersetzt die Entscheidung vom 2026-09-25 zum vollstaendigen
|
||||||
|
Ist-/Sollwert): Die Result DB ist datenschutzkonform und keine Angriffsanleitung.
|
||||||
|
Sie enthaelt keine Werte, die die bewertete Umgebung wiedererkennbar machen oder
|
||||||
|
den Angriffsaufwand kalibrieren. Der GPO-Istwert samt Principals, die Anzahl der
|
||||||
|
GPO-Quellen und konkrete Baseline-Releases entfallen; es bleiben technische
|
||||||
|
Setting-ID, Sollwert, Comparator-Relation und die Baseline-Familie. Identische
|
||||||
|
Baseline-Erwartungen werden zusammengefasst. In Scannerprosa ausser CIS-CAT
|
||||||
|
werden exakte Anzahlen, Datums-, Zeit- und Dauerangaben zu `[count]`, `[date]`,
|
||||||
|
`[time]` und `[duration]`; oeffentliche technische Kennungen (Versionen, CVE/KB,
|
||||||
|
Regel- und ATT&CK-IDs, Produktnamen, Ports, Event-IDs, oeffentliche SIDs) bleiben.
|
||||||
|
CIS-CAT-Prosa ist Benchmarktext und behaelt ihre oeffentlichen Empfehlungswerte.
|
||||||
|
Die Zusatzbeobachtungs-Policy 2.0.0 laesst nur noch binaere Konfigurationszustaende
|
||||||
|
zu; Anzahlen, Scores, Funktionsebenen und Versionen bleiben intern. Das JSONL-Manifest
|
||||||
|
enthaelt statt `generatedAt` nur `generatedOn` (Kalendertag), der Dateiname nur das
|
||||||
|
Datum. Work und interne Analyse behalten alle Originalwerte.
|
||||||
|
|
||||||
|
Disclosure Policy 3.1.0 (2026-09-28, Nutzerkorrektur nach Pruefung des Dummy-Exports)
|
||||||
|
begrenzt die Schwaerzung auf echte Umgebungswerte: Nummerierte Schritte bleiben; nur das
|
||||||
|
Empfehlungsfeld behaelt Hersteller-Schwellenwerte (`at least 8 characters`, `more than 180 days`,
|
||||||
|
`15 or more`), Titel und Beschreibung bleiben vollstaendig geschwaerzt. Gruppenrichtlinien- und
|
||||||
|
Registry-Pfade sowie dokumentierte Windows-Autoritaeten (`NT SERVICE`, `RESTRICTED SERVICES` u. a.)
|
||||||
|
gelten nicht als Konten. `[ip]` betrifft nur alleinstehende IPv4-Adressen, nie CIS-Regelnummern,
|
||||||
|
`Rule`-Verweise oder dokumentierte Bereichsgrenzen. Der Handling-Hinweis nennt jetzt ausdruecklich,
|
||||||
|
dass oeffentliche Baseline-Erwartungen, Herstellerhinweise und Schwellenwerte erhalten bleiben.
|
||||||
|
|
||||||
|
Disclosure Policy 3.2.0 (2026-09-28, Nutzerkorrektur nach Pruefung des gefilterten Dummy-Exports)
|
||||||
|
erhaelt Inline-Aufzaehlungen (Folge ab 1 mit mindestens zwei Punkten, eine wiederholte oder
|
||||||
|
uebersprungene Quellnummer toleriert), dokumentierte Attributflags (`adminCount=1`) und im
|
||||||
|
Empfehlungsfeld Zielwerte (`set to 1`). Links und Hostnamen von Hersteller- und Standard-
|
||||||
|
dokumentation bleiben, solange sie weder `@`, Tenant-Merkmale (`onmicrosoft`, `sharepoint`),
|
||||||
|
private Literale noch weitere nicht oeffentliche Hosts enthalten. Tenant-Hosts und alle
|
||||||
|
uebrigen Hosts bleiben geschwaerzt. Dateinamen, ScubaGear-Kennungen, Graph-Berechtigungen,
|
||||||
|
Code-Namespaces und -Bezeichner sowie Satzverbindungen ohne Leerzeichen gelten nicht als Domain.
|
||||||
|
|
||||||
|
Gespeicherte Result DB (2026-09-28, verbindliche Nutzeranforderung, Migration 024): Die Zeilen werden beim
|
||||||
|
Erzeugen offengelegt, geprueft und in `assessor_result_db_rows` gespeichert, mit Statussatz
|
||||||
|
`assessor_result_db_state` (Revision, Policy- und Waechterversion, Zeilen, Ersetzungen, gesperrte Zeilen).
|
||||||
|
Der Aufbau laeuft nach dem Start und nach jeder gespeicherten Aenderung; Lesezugriffe warten auf einen
|
||||||
|
aktuellen Stand. Jede Zeile traegt `disclosureTrace` (Feld, Regel, Anzahl, zurueckgehaltene Felder) ohne
|
||||||
|
Originalwerte. Der Identitaetswaechter 1.3.0 entfernt die eigenen Namen des Clients aus allen Zeilen und
|
||||||
|
sperrt ein Feld, wenn ein Name in anderer Schreibweise ueberlebt. `GET /api/v1/results/status` liefert den
|
||||||
|
Pruefstand; die Oberflaeche zeigt ihn und im Zeilendetail das Protokoll mit `Open original in Work`.
|
||||||
|
|
||||||
### Asset-/Kantenschicht: Struktur ohne Producer
|
### Asset-/Kantenschicht: Struktur ohne Producer
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Accepted Risk: decision record and storage contract
|
||||||
|
|
||||||
|
Status: implemented local documentation workflow; authenticated organizational approval remains a server-readiness requirement.
|
||||||
|
|
||||||
|
## Purpose and basis
|
||||||
|
|
||||||
|
Accepted Risk records an explicit, bounded business decision about a finding. It does not turn a failed scanner result into Passed, remove the finding, improve its technical score or waive a legal, contractual or policy obligation.
|
||||||
|
|
||||||
|
The workflow follows the separation of assessment evidence, accountable risk decisions, authorization conditions and monitoring described in [NIST SP 800-37 Rev. 2](https://csrc.nist.gov/pubs/sp/800/37/r2/final) and the [NIST RMF Authorize step](https://csrc.nist.gov/Projects/risk-management/about-rmf/authorize-step). These are design references, not a claim of certification or a universally binding approval policy. No form can guarantee that a decision is legally or organizationally valid in every context.
|
||||||
|
|
||||||
|
User decision, 2026-09-10: no existing organizational approval rules; provide a best-practice starting point. The application therefore requires a named risk owner and a different named approving authority with separate contacts, explicit approval evidence, a bounded validity period and a future review date. It does not invent a universal maximum acceptance duration or assume that a particular job title is authorized. Review frequency and acceptance duration must follow the documented exposure and business context.
|
||||||
|
|
||||||
|
## Recorded information
|
||||||
|
|
||||||
|
- Exact scope: affected assets/systems and exclusions, with the canonical finding occurrence linked automatically.
|
||||||
|
- Risk scenario, likelihood, business impact, initial risk, residual risk and justification for the remaining exposure.
|
||||||
|
- Business rationale, alternatives considered, and risk-register/ticket reference.
|
||||||
|
- Compensating controls, accountable control owner and effectiveness evidence; explicitly justify absent controls.
|
||||||
|
- Risk owner, role/team and contact; independent approving authority, role/decision authority and contact.
|
||||||
|
- External approval date and an attributable approval evidence reference covering scope, residual risk and validity.
|
||||||
|
- Validity start, expiry, next review, monitoring arrangements and review owner, escalation/reassessment triggers, and exit/remediation milestones.
|
||||||
|
- Applicable policy, regulatory and contractual considerations and any consulted specialists or restrictions.
|
||||||
|
- Additional evidence references, recorder name, revision reason, server recording timestamp and complete prior decision snapshots.
|
||||||
|
|
||||||
|
Evidence references are stored as inert text. The form neither uploads documents nor verifies their contents, signatures or availability. Source finding values and scanner provenance remain in the existing canonical model.
|
||||||
|
|
||||||
|
## States and rules
|
||||||
|
|
||||||
|
`draft`, `accept` and `revoke` are persisted revision actions. A partial draft requires a recorder and revision reason. Recording acceptance requires all substantive fields except additional evidence references, distinct owner/approver identities as declared, valid dates, approval on or before the start, a non-retroactive start, future expiry, and a future review date within the validity period. Revocation requires a recorder, reason and a previous acceptance; it retains the accepted decision's original details.
|
||||||
|
|
||||||
|
Displayed states are derived at read/render time: Not recorded, Draft, Scheduled, Accepted, Review due, Expired, Revoked and Review required. Expiry dates are inclusive UTC calendar dates; a review becomes due on its specified date. No scheduler or notification service is implied. A new draft becomes the current decision state; earlier approvals remain in history. Renewal or changed scope requires a new recorded acceptance, never editing an earlier snapshot.
|
||||||
|
|
||||||
|
The existing review workflow (including the historical `Actionable` → `accepted` mapping) is separate and is not repurposed for risk acceptance. Ordinary review edits preserve the structured record. Accepted Risk does not automatically alter bookmarks, remediation status or technical dashboard numbers.
|
||||||
|
|
||||||
|
## Implemented persistence
|
||||||
|
|
||||||
|
The authoritative record is `FindingReview.riskAcceptance`, an additive version-1 JSON object stored in the existing `assessor_reviews.data` JSONB column. It contains an append-only `revisions` array. Each revision contains action, complete details, sequential revision number, recorder, reason, original target occurrence ID, server-generated timestamp and `approvalIdentityVerified: false`.
|
||||||
|
|
||||||
|
No SQL migration is needed for this implementation. The canonical local datastore remains Docker project `assessor-2_0`, container `assessor-2_0-postgres-1`, database `assessor`, volume `assessor-2_0_assessor-postgres-data`, `127.0.0.1:5432`. The browser never accesses PostgreSQL directly.
|
||||||
|
|
||||||
|
Writes use `POST /api/v1/projects/:projectId/risk-acceptances` with occurrence ID, command and expected review revision. The backend verifies project membership and validates the decision. PostgreSQL locks the occurrence and current review within the repository transaction, appends the snapshot and increments the review/repository revisions atomically. The local reference repository implements the same contract. General `/reviews` writes cannot introduce, replace, remove or retarget a risk acceptance history. Conflicts fail without overwriting the previous record.
|
||||||
|
|
||||||
|
The UI reports successful storage only after the backend acknowledges the write. Disconnected/unsaved findings cannot record acceptance. Failed writes retain the dialog entries. Existing browser review caches and project snapshots can retain a copy; they are not the authority for acceptance. Exports include the complete structured review record. Existing project/scan deletion policies still apply: this is not an independent legal-hold archive.
|
||||||
|
|
||||||
|
## Reimport and retention
|
||||||
|
|
||||||
|
The existing stable-identity review transfer preserves the complete object and all original revision targets. It never matches by title or row position. If a replacement occurrence differs from the recorded target, the preserved acceptance displays Review required. A new decision can explicitly cover the replacement after reassessment. Failed or ambiguous matches retain the original review according to the reimport policy.
|
||||||
|
|
||||||
|
This refines preservation in `docs/REIMPORT_POLICY.md`: preserving a prior decision is not proof that a changed finding or new assessment is within its approved scope. Previous approvals, expiry dates and decision evidence must never be silently renewed.
|
||||||
|
|
||||||
|
## Security limits and planned server extension
|
||||||
|
|
||||||
|
The current local-only application has no authenticated approver identity, organizational role registry, digital signing, independent evidence verification, trusted immutable storage or legal-hold enforcement. Distinct names/contact entries and an approval reference document an external decision; they do not prove authorization or enforce separation of duties between authenticated users. Same-origin protections and revision checks protect the application workflow, not against a database administrator. These limits remain visible in the dialog and prevent any claim of a tamper-proof or universally compliant approval.
|
||||||
|
|
||||||
|
Before organization-wide approval in the browser/server target, add:
|
||||||
|
|
||||||
|
1. SSO-bound actors and client-scoped authorization; configurable risk-owner/approver roles, thresholds, separation of duties and delegation rules.
|
||||||
|
2. Dedicated `risk_acceptances`, `risk_acceptance_revisions`, `risk_acceptance_approvals` and `risk_acceptance_evidence` tables with immutable event IDs, project/occurrence foreign keys, actor IDs and server timestamps. Migrate current JSON history without replacing historical declared identities with verified identities.
|
||||||
|
3. Evidence references with content hashes, access controls, immutable versions and an organization-approved retention/legal-hold policy. Do not add external storage or uploads without a security-model update.
|
||||||
|
4. Transactional approval/revocation endpoints, tamper-evident audit retention, server-side due/expiry queries and an explicitly configured notification/monitoring workflow.
|
||||||
|
5. Tests against a dedicated approved integration environment for concurrent approvals, permissions, clock boundaries, evidence retention and migration round-trips. An approved persistence migration requires a verified backup of the canonical datastore first.
|
||||||
|
|
||||||
|
These are acceptance criteria for the future governance extension in phase 14, not completed features. Phase 7 covers the dialog, phases 9–11 cover its storage/API contract, and phase 13 covers security validation. Existing phases are not reopened or marked complete by this scoped implementation.
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
# ASSESSOR scoring rule 1.0.0
|
||||||
|
|
||||||
|
Decision: 2026-09-10, explicitly approved by the user. Implementation belongs to
|
||||||
|
Phase 7/13C (Client Info and scanner context), with score-comparison safeguards
|
||||||
|
under Phase 8. This decision supersedes the display-only native-score policy.
|
||||||
|
|
||||||
|
## Comparison qualification, 2026-09-11
|
||||||
|
|
||||||
|
The numeric contribution policy below remains unchanged. Its comparison safeguards are not a completed acceptance of object-level remediation or an explicitly selected A/B report. Code review identified an evaluation-population key without asset identity, weaker pending-revalidation checks than score pairing, and last-known-source replacement that can omit unresolved obligations after partial intermediate scans. Original stored findings are not deleted. The current historical baseline is not necessarily the user's selected A.
|
||||||
|
|
||||||
|
[TIMESLOT_COMPARISON_PLAN.md](TIMESLOT_COMPARISON_PLAN.md) defines the open Phase-8 follow-up: verified object/scope populations, an explicit A/B basis, obligation-level history and separate eligibility rules for score deltas and remediation evidence. Matching `assessed` and `eligible` counts alone does not prove coverage of absent objects. Native totals must not be recomputed for an arbitrary smaller scope without separately retained contributions. Missing historical risks stay visible separately; no historical score is relabelled as a current measurement and no new penalty formula is introduced.
|
||||||
|
|
||||||
|
This is a planning clarification, not a runtime fix, processing-manifest change or completed verification. The earlier implementation/test notes below remain historical evidence for their stated scope; they do not satisfy the new follow-up's acceptance matrix.
|
||||||
|
|
||||||
|
## GPO scoring scope, decision 2026-09-16
|
||||||
|
|
||||||
|
The GPO contribution measures conformance of customer settings that are actually configured, independently comparable and inside an explicitly applicable Microsoft-baseline scope. It is not a measurement of complete Microsoft-baseline adoption. Recommended settings that are absent from the customer GPO set remain visible as `Not configured`, but they do not create operational findings, remediation obligations or score penalties in the normal assessment. Full baseline deployment is a separate explicitly commissioned hardening scope.
|
||||||
|
|
||||||
|
Clarification 2026-09-16: absence is reported within the analyzed backup scope. An applicable recommendation with no observed configured value in that imported set remains filterable as `Not configured`, including for a selected subset. This does not assert domain-wide absence or effective device state; the limited scope remains diagnostic evidence. The verdict remains excluded from the score.
|
||||||
|
|
||||||
|
Clarification later on 2026-09-16: `Not configured` is a score-neutral policy verdict. `Duplicates` and `Conflicts` are distinct diagnostic categories on a technical policy setting, not alternative verdicts. Roll up member comparisons once per technical setting: one independently comparable, in-scope configured member value with a valid `Fail` makes the complete policy-setting verdict `Fail`; retain the conflict/duplicate flags beside it. All applicable members passing yields `Passed`. Unresolved applicability, precedence or comparison yields `Review required`. Duplicate source rows, alternative presentations and diagnostic flags never add score weight.
|
||||||
|
|
||||||
|
This clarification supersedes only the earlier same-day interpretation that a conflict/duplicate container could remain score-neutral despite a proven member fail. It is binding for new GPO scoring work. Current runtime categories remain disjoint, so the rolled-up verdict plus diagnostic-flag model requires a dedicated result-population identity, synthetic conflict/duplicate fixtures and explicit verification before it may be claimed as implemented. If implementing it changes persisted evaluation semantics or score inputs, version the affected evaluator/scoring contract and follow the reimport/re-evaluation policy; do not reinterpret historical runs silently.
|
||||||
|
|
||||||
|
## Scan contribution
|
||||||
|
|
||||||
|
Use a valid native score from the exact import file and scan run. Otherwise use
|
||||||
|
`100 * passed / (passed + deviations)` when the result population supports a
|
||||||
|
pass rate. Never combine the native score and the pass rate of the same scan.
|
||||||
|
|
||||||
|
| Source | Native contribution | Fallback |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Purple Knight for Active Directory | Native AD posture, 0–100 | Pass rate |
|
||||||
|
| Purple Knight for Entra ID | Native Entra posture, 0–100 | Pass rate |
|
||||||
|
| CIS-CAT | Native benchmark score, 0–100 | Pass rate |
|
||||||
|
| PingCastle Healthcheck | `100 - native risk`, bounded 0–100 | No pass rate from findings-only reports |
|
||||||
|
| Maester | No native numeric score assumed | Pass rate |
|
||||||
|
| GPO Analysis | None | Pass rate of configured, independently comparable in-scope settings |
|
||||||
|
| Additional registered scanners | Native overall score declared by their profile | Declared result-population fallback |
|
||||||
|
| Unrecognized tabular sources | No native score inferred | Provisional pass rate only with evidence of passed results |
|
||||||
|
|
||||||
|
Purple Knight AD and Purple Knight Entra are separate scan scopes. Their native
|
||||||
|
posture values are bound respectively to the imported forest or Entra tenant
|
||||||
|
and must never be substituted for one another. A mixed report with conflicting
|
||||||
|
native scopes remains unavailable rather than being collapsed into one score.
|
||||||
|
A missing native score on an old import requires the relevant summary context
|
||||||
|
to be reimported; no score or tenant is fabricated.
|
||||||
|
|
||||||
|
Null, blank, boolean, nonnumeric, out-of-range and conflicting native values
|
||||||
|
remain unavailable. A valid zero is a real score. Invalid native data and source
|
||||||
|
integrity diagnostics are visible in the calculation details.
|
||||||
|
|
||||||
|
Passed and deviations use normalized outcomes. Not applicable is excluded.
|
||||||
|
Execution errors, not run and unknown results do not pass and do not add a
|
||||||
|
security failure. They affect known-check coverage. GPO context states retain
|
||||||
|
their existing authoritative disposition. `Not configured` does not enter the
|
||||||
|
GPO denominator. Conflict/duplicate flags do not enter independently; the
|
||||||
|
rolled-up policy verdict enters once and is `Fail` when any independently
|
||||||
|
comparable, in-scope configured member fails. No
|
||||||
|
severity or missing-setting penalty is invented. Findings-only input cannot
|
||||||
|
establish a pass denominator.
|
||||||
|
|
||||||
|
## Future scanner support
|
||||||
|
|
||||||
|
Every new or extended scanner profile must first examine the supported report
|
||||||
|
formats for a usable native overall score. `ToolProfileDefinition.securityScoring`
|
||||||
|
is mandatory: declare exact mapped observation properties, overall scope,
|
||||||
|
0–100 scale, direction, incompatible scopes and fallback population, or record
|
||||||
|
an explicit reason why native scoring is unavailable. A new profile registered
|
||||||
|
in the profile catalog automatically uses the shared native-first calculation;
|
||||||
|
there is no separate hardcoded scanner list in that calculation.
|
||||||
|
|
||||||
|
The extractor must retain the original score and its exact source path as
|
||||||
|
file/run-bound report context. Different native scales or ambiguous scope need
|
||||||
|
an explicitly documented and tested mapping before they are usable. Arbitrary
|
||||||
|
fields named score, category scores and finding weights do not qualify. Until
|
||||||
|
an unfamiliar format has such a profile, its native semantics remain unknown.
|
||||||
|
|
||||||
|
A valid native score, including zero, takes priority even for findings-only
|
||||||
|
reports. Missing, invalid, conflicting or unrelated native observations lead
|
||||||
|
to the declared fallback; without a defensible result population the contribution
|
||||||
|
remains unavailable. Scoring-definition versions participate in comparison
|
||||||
|
compatibility. Extend synthetic format fixtures and verify native priority,
|
||||||
|
provenance, invalid values and fallback whenever adding scanner support.
|
||||||
|
|
||||||
|
This extension moves existing declarations into their profiles without changing
|
||||||
|
their numeric methods or import mapping versions. No historic imports are
|
||||||
|
rewritten. New extraction or semantic changes still require explicit versioning
|
||||||
|
and a reimport recommendation for older data lacking the required context.
|
||||||
|
|
||||||
|
## Source weighting and provenance
|
||||||
|
|
||||||
|
Each source tool has one fixed weight, initially 1. Distinct scan scopes are
|
||||||
|
averaged equally inside that weight; the overall value is the weighted mean of
|
||||||
|
scoreable source contributions. File count and check count do not increase a
|
||||||
|
tool's overall weight. Contributions with no score remain explicitly visible.
|
||||||
|
Incomplete contributions make the displayed index provisional.
|
||||||
|
|
||||||
|
Same-run representations are combined only with a verified tool, assessed
|
||||||
|
target, benchmark scope, exact reported timestamp and processing identity, or
|
||||||
|
an identical SHA-256 and processing identity. Preserve the contributing file,
|
||||||
|
run and occurrence references. Conflicting native scores or result populations
|
||||||
|
make the combined contribution unscoreable. Source rule/outcome signatures
|
||||||
|
also detect different results with coincidentally equal totals.
|
||||||
|
|
||||||
|
When a scanner exports one native score-only summary beside its finding file,
|
||||||
|
the shared scan-run and processing identity may join those complementary files.
|
||||||
|
The native score remains the contribution and the finding file supplies the
|
||||||
|
passed/deviation population. This association requires exactly one native
|
||||||
|
carrier and one unambiguous result population; ambiguous result files remain
|
||||||
|
separate instead of borrowing the native value.
|
||||||
|
|
||||||
|
Within a known target and benchmark, use the latest reported scan time in the
|
||||||
|
assessment. Never use import time or a filename to infer scan identity. Unknown
|
||||||
|
identity/time remains disclosed. Original values and timestamps are unchanged.
|
||||||
|
|
||||||
|
The `Assessment.scoringPolicy` stores rule version, tool weights and default
|
||||||
|
weight through the existing JSON/JSONB assessment contract. New scanner import
|
||||||
|
assessments and new Data Hub timeslots initialize this field. Missing policies
|
||||||
|
on historical assessments permanently mean the version 1.0.0 default, not an
|
||||||
|
alias to a future current policy. Existing records are not migrated or rewritten.
|
||||||
|
Unsupported policy versions do not produce a score. Round only at display time.
|
||||||
|
|
||||||
|
## Presentation and comparisons
|
||||||
|
|
||||||
|
Overall Score is a mixed-method assessment index, not a percentage of security.
|
||||||
|
Native risk models and pass rates are not methodologically equivalent or
|
||||||
|
empirically calibrated. Security Domain values are explicitly labelled pass
|
||||||
|
rates and are not presented as a numeric decomposition of this index.
|
||||||
|
|
||||||
|
Source Overview shows the actual contribution and its method. The Scoring
|
||||||
|
methodology dialog shows per-scan contribution, native input, source weight,
|
||||||
|
evaluated-check basis, reported scan time and any qualifications. Critical
|
||||||
|
findings and check coverage remain separate from the index.
|
||||||
|
|
||||||
|
Measured score progress uses only the one-to-one intersection of identical
|
||||||
|
assessed targets/benchmarks with unchanged rule/evaluation population, processing, engine, method and
|
||||||
|
weight, a newer source observation, and unqualified evidence. Both values are
|
||||||
|
recomputed on this shared basis; omitting a weak source cannot become measured
|
||||||
|
improvement. Changed scope/method is visibly labelled on the current index.
|
||||||
|
|
||||||
|
The workspace projects the latest known prior evidence per source scope from
|
||||||
|
persisted historical assessments. Missing-source open obligations retain their
|
||||||
|
original occurrence, run, assessment and observation-time references across
|
||||||
|
successive absent follow-ups. Historical scores are never copied into current
|
||||||
|
scan measurements. Unavailable history blocks a verified-progress statement
|
||||||
|
and is visible without preventing access to the current workspace.
|
||||||
|
|
||||||
|
This is a read-only score/provenance projection. The broader Phase-8 workflow
|
||||||
|
for persisted carry-forward lifecycle states, operational backlog integration
|
||||||
|
and audited source retirement remains a separate unfinished work package.
|
||||||
|
No new source retirement is inferred from a selection/filter operation.
|
||||||
|
|
||||||
|
## Manufacturer references
|
||||||
|
|
||||||
|
- [Semperis scoring methodology](https://www.semperis.com/blog/pk-scoring-improves-understanding-of-identity-system-security-vulnerabilities/): native posture and category scores have independent calculations.
|
||||||
|
- [PingCastle Healthcheck](https://www.pingcastle.com/documentation/healthcheck/): overall risk uses the maximum of four capped category values. Inversion is an explicit ASSESSOR index convention.
|
||||||
|
- [CIS-CAT report documentation](https://ciscat-assessor.docs.cisecurity.org/en/latest/ReportHTML/): scores are recommendation-based; scored recommendations currently have equal weight.
|
||||||
|
|
||||||
|
## Remaining limitations
|
||||||
|
|
||||||
|
- No new environment-area weighting or automatic mapping of native totals to
|
||||||
|
the eight regex-assigned Security Domains.
|
||||||
|
- Unknown source identity or engine/processing changes limit time comparisons.
|
||||||
|
- Missing-result diagnostics qualify native scores rather than silently
|
||||||
|
replacing source data. A source's scoring conventions can differ from check
|
||||||
|
coverage conventions, especially for execution errors.
|
||||||
|
- GPO-only history remains under the existing policy-comparison workflow.
|
||||||
|
- This change does not reimport, migrate or inspect real assessment contents.
|
||||||
|
|
||||||
|
## Verification, 2026-09-10
|
||||||
|
|
||||||
|
`npm test`: 1,188 passed, eight optional PostgreSQL integration tests skipped;
|
||||||
|
local-only security and design checks passed. Final focused run: 55 passed,
|
||||||
|
including unchanged rule-population comparisons and persisted GPO weights.
|
||||||
|
Final `npm run build`: client and server successful; existing bundle-size
|
||||||
|
advisory remains. All content-level checks used synthetic fixtures. No real
|
||||||
|
database changes, live content inspection, reimport or backend restart.
|
||||||
|
|
||||||
|
Follow-up for future scanner profiles: ten additional synthetic regressions
|
||||||
|
cover the mandatory profile contract, native priority for a new scanner through
|
||||||
|
dataset projection, score direction and zero, invalid/conflicting/foreign
|
||||||
|
observations, unknown semantics, findings-only fallback and scoring versions.
|
||||||
|
`npm test`: 1,198 passed, eight optional PostgreSQL tests skipped; local-only
|
||||||
|
and design checks passed. `npm run build`: client and server successful.
|
||||||
|
|
||||||
|
Purple Knight companion-file follow-up: the score-only assessment summary is
|
||||||
|
now associated with one unambiguous findings population from the exact same
|
||||||
|
scan run and processing contract. The regression with 65 native posture,
|
||||||
|
96 passed and 19 deviations yields 65 as the dashboard contribution while
|
||||||
|
retaining all 19 deviations. `npm test`: 1,199 passed, eight optional
|
||||||
|
PostgreSQL tests skipped; security/design checks and both builds passed.
|
||||||
|
|
||||||
|
Purple Knight Entra follow-up 2026-09-22: profile 1.4.0 recognizes the native
|
||||||
|
`Entra ID` assessment summary, persists its tenant, posture, counters and
|
||||||
|
category scores, and labels the scan independently from Purple Knight for
|
||||||
|
Active Directory. The same native-first rule applies within the Entra scope;
|
||||||
|
the tenant name is its target identity. Historical 1.3.0 imports do not gain
|
||||||
|
these facts at read time and require reimport of the original files.
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Timeslot comparison capability audit
|
||||||
|
|
||||||
|
Stand: 2026-09-11. Phase 8, Paket P0/P1. Code- und synthetisches Fixture-Audit; ergänzend wurde der vom Nutzer ausdrücklich vollständig freigegebene Mandant `Dummy` ausschließlich lesend geprüft. Es gab keine Datenmutation.
|
||||||
|
|
||||||
|
## Ergebnis
|
||||||
|
|
||||||
|
Die aktuelle kanonische Persistenz bewahrt Tool-/Regel-ID, optional ein generisch gemapptes `affectedAsset`, Originalfelder und Provenienz. Sie bewahrt außerdem scannergebundene Ziel-/Benchmarkbeobachtungen für die Scoreprojektion. Diese Angaben reichen nicht pauschal für eine objektgenaue Gegenprüfung: Der Assetwert besitzt keinen deklarierten Typ oder Namensraum, Score-Scope und Finding-Vorkommen sind nicht allgemein über einen versionierten Vergleichsvertrag verbunden, und kein Profil deklariert derzeit eine vollständige Negativnachweissemantik je Regel/Objekt.
|
||||||
|
|
||||||
|
| Quelle | Unterstützte Formate/Pfade | Erhaltener übergeordneter Scope | Ergebnispopulation | Objektgenaue Behebung aus heutigem Bestand |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Purple Knight | CSV/Excel/HTML über generische Tabellen plus native Summary-/HTML-Extraktion | `forest.name`; AD-native Scorequelle | `complete` für Scoreberechnung | Nicht allgemein belegt. `asset` ist heuristisch gemappt; Typ/Namensraum und vollständige Objektpopulation fehlen |
|
||||||
|
| PingCastle | XML/HTML über Healthcheck-Extractor | `domain.fqdn` | `findings-only` | Abwesenheit beweist keine Behebung. Native Regel-ID ist erforderlich, aber Ergebnislisten enthalten nur ausgelöste Regeln |
|
||||||
|
| CIS-CAT | HTML sowie generische Tabellen/Objektpfade | `host.name` und `benchmark.name` | `complete` für Scoreberechnung | Scopevergleich grundsätzlich möglich; regel-/objektbezogener Negativnachweis und Namespaceregel noch nicht deklariert |
|
||||||
|
| Maester | Excel/CSV/HTML über generische Tabellen bzw. HTML-Extractor | `entra.tenant_id` | `complete` für Scoreberechnung | Tenant-/Regelvergleich grundsätzlich möglich; `asset` ist keine verpflichtende, typisierte Zielidentität und vollständige Negativsemantik fehlt |
|
||||||
|
| Generisch | CSV/Excel/JSON/XML/HTML über registrierte Formatadapter und generischen Extractor | kein verifizierter Zielscope | `unknown` | Nicht automatisch vergleichbar; Titel, Position oder untypisierter Assettext dürfen keine Behebung beweisen |
|
||||||
|
| GPO/Baseline | Eigener versionierter Ingestion-, Identity-, Snapshot-, Evaluation- und Finding-Pfad | Projekt, Environment Profile, Kontext/Baseline/Setting und Backupreferenzen | explizite Complete/Partial/Blocked- und Evidencezustände | Fachlich reichere Identitäten vorhanden, aber ohne RSoP/GP Results keine allgemeine Aussage über effektive Anwendung; eigener Policyvertrag bleibt maßgeblich |
|
||||||
|
|
||||||
|
Die älteren `comparisonKeys` bleiben vorerst als Legacydeklaration vorhanden. Zusätzlich muss jetzt jedes Scannerprofil einen versionierten `timeslotComparison`-Vertrag besitzen. Purple Knight, PingCastle, CIS-CAT und Maester sind zunächst ausschließlich scopeweit freigegeben; das generische Profil ist nicht automatisch vergleichbar. Kein aktuelles Profil erlaubt Behebung durch bloße Abwesenheit. Die neue sichere Engine konsumiert diese Verträge; die bisherige Produktprojektion konsumiert sie noch nicht.
|
||||||
|
|
||||||
|
## Ergänzende Bestandsvalidierung mit Dummy
|
||||||
|
|
||||||
|
- Purple Knight besitzt im aktiven Bestand Regel-IDs und für jedes Ergebnis einen `affectedAsset`-Wert. Die Werte sind jedoch nur die Ergebnisbereiche `AD` beziehungsweise `Hybrid (AD)`, keine stabilen Host-/Objektidentitäten. Der Vertrag bleibt deshalb korrekt auf `forest.name` und Scopegranularität begrenzt.
|
||||||
|
- CIS-CAT besitzt `host.name`, `benchmark.name` und `report.generated_at`; PingCastle besitzt `domain.fqdn` und `report.generated_at`; Purple Knight besitzt `forest.name` und `report.generated_at`. Der aktive Maester-Excel-Lauf besitzt keinen gespeicherten `entra.tenant_id`- oder Reportzeitnachweis und ist nach dem neuen Vertrag aktuell nicht vergleichbar.
|
||||||
|
- Dummy enthält mehrere supersedierte Reimporte derselben Scannerquellen. Die Läufe besitzen weder `startedAt` noch `completedAt`; ihre Importzeiten unterscheiden sich. Diese Importzeiten dürfen und werden nicht als neue Sicherheitsbeobachtung verwendet.
|
||||||
|
- Die derzeit aktiven Dummy-Quellen liegen nicht als zwei vollständig vergleichbare A/B-Timeslots desselben Scanners vor. Dummy kann daher aktuell keinen realen Fortschrittswert abnehmen; er bestätigt stattdessen die Fail-closed-Grenzen für Scope, Reportzeit und Reimport.
|
||||||
|
|
||||||
|
## Versionierungsentscheidung für den ersten Schnitt
|
||||||
|
|
||||||
|
Der neue Domänenvertrag in `src/domain/timeslot-comparison.ts` ist additiv und wird noch nicht von Import, Persistenz, bestehender Vergleichsengine oder UI verwendet. Er verändert keine gespeicherten Fakten und benötigt deshalb für diesen ersten Schnitt weder Processing-Manifest noch Reimport.
|
||||||
|
|
||||||
|
Sobald ein Profil Objekt-/Scopefelder neu extrahiert, anders mappt oder eine neue Vollständigkeits-/Negativnachweisaussage aus dem Quellformat ableitet, gilt `REIMPORT_POLICY.md`: append-only Parser-/Extractor-/Profil-/Mappingversion und Reimportmarkierung nur für die betroffenen historischen Verträge. Bereits vollständig gespeicherte, verifizierbare Fakten dürfen später über eine getrennt versionierte Vergleichsprojektion neu ausgewertet werden; fehlende Evidenz darf nicht rekonstruiert werden.
|
||||||
|
|
||||||
|
## P1-Profilstand und offene Entscheidungen
|
||||||
|
|
||||||
|
- Scopegranularität, notwendige Scopeeigenschaften, Reportzeitquellen, Ergebnisart und Diagnosepolitik sind für alle registrierten Profile deklariert und synthetisch validiert. Objektgranularität bleibt für alle aktuellen Scannerprofile gesperrt.
|
||||||
|
- `complete` trägt bei keinem aktuellen Profil automatisch eine negative Regel-/Objektaussage. Abwesenheit bleibt `unverified-absent`, bis ein formatspezifischer vollständiger Negativnachweis synthetisch belegt und versioniert ist.
|
||||||
|
- Scannerdiagnosen nach Einfluss auf Identität, Einzelresultat, Abdeckung und Gesamtscore klassifizieren.
|
||||||
|
- GPO-Vergleichsvertrag getrennt auf vorhandene Policy-Identitäten und Wirksamkeitsgrenzen abbilden; keine Scanner-Sonderlogik in React.
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
# Belastbarer Timeslotvergleich: Umsetzungs- und Abnahmeplan
|
||||||
|
|
||||||
|
Stand: 2026-09-11. Zugehörigkeit: Phase 8, mit UI aus Phase 7, Persistenz/API aus Phasen 9-11 und erforderlichen Profilverträgen aus Phasen 6/13A/13C; GPO-Anbindung über Phase 13B.
|
||||||
|
|
||||||
|
Status: **in Arbeit, nicht fachlich abgenommen**. P0 ist abgeschlossen. P1 besitzt den additiven Domänenvertrag und verpflichtende profilgebundene Scope-/Zeit-/Evidenzdeklarationen; Persistenz/API und Übergangsvertrag bleiben offen. P2 ist begonnen: `compareProjectTimeslotEvidence` paart echte Scannerläufe je Werkzeug und bewertet Processing, Scope, Reportzeit, Diagnosen sowie Fail-zu-Pass konservativ. Die bisherige Engine und Oberfläche verwenden diesen Pfad noch nicht. P3-P6 sind offen. Der aktuelle Implementierungsschnitt führt keine Migration, Reimporte, Datenmutation oder Aktivierung einer Vergleichsoberfläche aus. Der historische Phase-8-Abschluss vom 2026-07-17 bleibt dokumentiert, ist aber keine Abnahme dieses Arbeitspakets.
|
||||||
|
|
||||||
|
Dieser Plan präzisiert das Arbeitspaket „faire Vergleiche bei fehlenden Folgequellen“ in [IMPLEMENTATION_PLAN.md](IMPLEMENTATION_PLAN.md). Für die künftige Umsetzung ersetzt er ausdrücklich die ältere Schlussfolgerung „abwesend = behoben“ und die pauschale Ablösung offener Verpflichtungen durch einen neuen Lauf derselben Quelle. Historische Ergebnisse und Originaldaten werden dadurch nicht rückwirkend geändert.
|
||||||
|
|
||||||
|
## 1. Ziel und Grenzen
|
||||||
|
|
||||||
|
Zwei ausdrücklich gewählte Timeslots A und B desselben Kunden liefern drei getrennte Aussagen:
|
||||||
|
|
||||||
|
1. **Nachweislich behoben:** zuvor bestätigte Abweichungen mit nachvollziehbarer aktueller Gegenprüfung.
|
||||||
|
2. **Weiterhin offen oder ungeklärt:** aktuell bestätigte sowie historische, noch nicht ausreichend erneut geprüfte Verpflichtungen; neue und wieder aufgetretene Abweichungen getrennt ausweisen.
|
||||||
|
3. **Vergleichbare Scoreveränderung:** A und B werden auf derselben belegten Prüfgrundlage berechnet; ausgeschlossene Bereiche, Evidenzabdeckung und Aktualität bleiben sichtbar.
|
||||||
|
|
||||||
|
Der Overall Score bleibt der methodisch gemischte Index aus [SCORING_MODEL.md](SCORING_MODEL.md), kein Prozentsatz tatsächlicher Sicherheit. Keine neuen Risikogewichte, Scannerformeln oder automatische Übertragung nativer Gesamtwerte auf Security Domains. Fehlende Daten sind weder null noch bestanden. Unbekannte Prüfgrundlagen führen zu einer begrenzten oder nicht verfügbaren Aussage.
|
||||||
|
|
||||||
|
## 2. Ausgangsbefund und Reparaturbedarf
|
||||||
|
|
||||||
|
Codeprüfung vom 2026-09-11, keine neue Laufzeitabnahme:
|
||||||
|
|
||||||
|
- `compareProjectTimeslots` in [datahub-scan-analysis.ts](../src/features/project-import/datahub-scan-analysis.ts) besitzt derzeit keinen Produktaufrufer. Mehrfachauswahl/Öffnen ist kein expliziter A/B-Bericht.
|
||||||
|
- Die Timeslot-Projektion ersetzt beide Laufwerkzeuge durch `multiple`, lässt `importFiles` aber an Original-Lauf-IDs. Dadurch kann `sameTool` fälschlich gelten und die dateigebundene Kompatibilitätsprüfung ausfallen.
|
||||||
|
- Die automatischen Matchingstufen der [comparison-engine.ts](../src/comparison/comparison-engine.ts) erzwingen keine objektgenaue Identität. Eine gemeinsame Regeldefinition ist kein Nachweis desselben betroffenen Objekts. Die Zwei-Lauf-Projektion entfernt außerdem frühere Beobachtungen, die zur Verifikation eines Wiederauftretens gebraucht werden.
|
||||||
|
- Abwesende Vorkommen können `resolved` werden, während zugeordnete Fail-zu-Pass-Ergebnisse `persistent` mit Statusänderung bleiben. Die lokale `isOpen`-Prüfung verwechselt fachliche Abweichungen mit technischen beziehungsweise ungeprüften Zuständen.
|
||||||
|
- Die Score-Paarbildung prüft viele Kompatibilitätsbedingungen. Ihr `evaluationScopeKey` enthält jedoch keine Asset-Identität; eine unveränderte Regelanzahl kann einen Objektwechsel verdecken.
|
||||||
|
- `awaitingRevalidation` verwendet schwächere Bedingungen als die Score-Paarbildung. Zusätzlich überschreibt `lastKnown` im [Workspace-Lader](../src/features/project-import/load-canonical-project-workspace.ts) ältere Quellstände, ohne jede offene Verpflichtung zu erhalten. Das kann Verpflichtungen aus der Anzeige verdrängen, löscht aber keine gespeicherten Originalfindings.
|
||||||
|
- Die derzeitige Score-Baseline ist ein historischer letzter Stand je Quellscope vor der geöffneten Auswahl, nicht zwingend der ausdrücklich gewünschte Timeslot A.
|
||||||
|
|
||||||
|
## 3. Fachliche Verträge
|
||||||
|
|
||||||
|
### 3.1 Regel, Vergleichsidentität und Beobachtung
|
||||||
|
|
||||||
|
- `FindingDefinition` bleibt eine Regeldefinition. Kein pauschaler Ersatz ihres `tool:rule`-Schlüssels durch `tool:rule:objekt`.
|
||||||
|
- Die projektgebundene Vergleichsidentität eines Vorkommens umfasst verifizierte Quelle, native Regel, fachlichen Scope und gegebenenfalls die stabile Identität des betroffenen Objekts. Zeitpunkt und Importlauf gehören zur Beobachtung und Provenienz, nicht in den dauerhaften Vergleichsschlüssel.
|
||||||
|
- Objektart, Namensraum und Geltungsbereich gehören zum Identitätsvertrag. Fehlende Identität ist nicht die leere Identität eines vermeintlich gleichen Objekts. Anzeigenamen, Positionen und Titelähnlichkeit reichen nicht aus; Umbenennungen oder Aliasbeziehungen dürfen nicht geraten werden.
|
||||||
|
- Objektbezogene, scopeweite und aggregierte Regeln erhalten deklarierte Vergleichsgranularitäten. Für einen domainweiten Befund wird kein Host erfunden. Eine Regel mit einer Liste betroffener Objekte ist ohne geeignete strukturierte Evidenz nicht automatisch eine Menge eigenständiger Instanzfindings.
|
||||||
|
- Automatische Zuordnung muss beidseitig eindeutig auf der deklarierten Granularität sein. Unsichere Zuordnungen bleiben Vorschläge; sie dürfen weder Behebung noch neue Abweichungen allein aus unklarer Restmengenbildung beweisen. Lokale bestätigte Regeln dürfen diese Scope-/Objektgrenzen nicht umgehen.
|
||||||
|
- Der bereits vorhandene objektbezogene Reimport-Reviewtransfer ist eine wiederzuverwendende Grundlage, aber kein ungeprüfter universeller Identitätsvertrag. Definitionsreviews, Vorkommensreviews und Accepted Risk behalten ihren jeweiligen Geltungsbereich.
|
||||||
|
|
||||||
|
### 3.2 Gegenprüfung und Lebenszyklus
|
||||||
|
|
||||||
|
Die Ergebnispräsenz und der Lebenszyklus einer Abweichung werden getrennt betrachtet. Eine Regel kann weiterhin als Ergebnis vorhanden sein, obwohl die konkrete Abweichung behoben ist.
|
||||||
|
|
||||||
|
| Evidenz im Ziel B gegenüber einer bestätigten Abweichung | Fachliche Aussage |
|
||||||
|
|---|---|
|
||||||
|
| Eindeutig dasselbe Objekt/derselbe Scope, kompatible Prüfung, neueres belastbares Pass-Ergebnis | nachweislich behoben |
|
||||||
|
| Dieselbe Vergleichsidentität mit bestätigter aktueller Abweichung | weiterhin offen |
|
||||||
|
| Quelle, Objekt oder Ergebnis fehlt; Prüfung abgebrochen, unbekannt oder mehrdeutig | ungeklärt, Verpflichtung bleibt bestehen; `unverified-absent` für ungeprüfte Abwesenheit |
|
||||||
|
| Finding fehlt in einem deklarativ vollständig belegten Negativbericht für genau diese Regel und dieses Objekt | Behebung nur bei erfülltem profilgebundenem Negativnachweisvertrag |
|
||||||
|
| Nicht anwendbar oder aus dem Scope genommen | keine Behebung; eigene belegte Anwendbarkeits-/Scopeentscheidung |
|
||||||
|
| Manuell als erledigt markiert oder Risiko akzeptiert | Workflow-/Risikoentscheidung, kein technischer Behebungsnachweis |
|
||||||
|
|
||||||
|
Neue und wieder aufgetretene Abweichungen verlangen ebenfalls ausreichende Historie und Vergleichbarkeit. Eine bloße Lücke im direkten Vorgänger ist kein Nachweis einer zwischenzeitlichen Behebung. Ein späterer Fail nach belegtem Pass derselben Identität ist ein Wiederauftreten; nach einer ungeprüften Lücke bleibt eine alte Verpflichtung offen.
|
||||||
|
|
||||||
|
Ein Gegenprüfungsvertrag je Profil/Format beschreibt Regelpopulation, Zielobjekte, erfolgreichen Ausführungsumfang, positive/negative Berichtsemantik, Zeitbezug, notwendige Provenienz und disqualifizierende Diagnosearten. GPOs verwenden ihre vorhandenen Settings-, Scope-, Baseline- und Evaluationsverträge; ohne Wirksamkeitsevidenz wird keine effektive Anwendung behauptet.
|
||||||
|
|
||||||
|
`assessed` und `eligible` sind unterstützende Signale, kein Beweis vollständig untersuchter Objekte. Ein im Report vollständig fehlendes Objekt kann beide Zähler unverändert lassen. Fehler werden nach ihrem Einfluss auf Identität, einzelne Prüfergebnisse, Abdeckung oder den Gesamtscore klassifiziert. Eine belastbare Einzelprüfung kann trotz fachlich unabhängiger Fehler verwendet werden; unbekannte Fehlerauswirkung verhindert automatische Behebung.
|
||||||
|
|
||||||
|
### 3.3 Dauerhafte offene Verpflichtungen
|
||||||
|
|
||||||
|
- Jede offene Verpflichtung behält Ursprungs-Timeslot, Originalvorkommen, Quelle, Objekt/Scope, letzten bestätigten Zustand und Beobachtungszeitpunkt. Über A, einen unvollständigen Zwischenstand und B darf sie nicht durch „letzter Scan je Quelle“ verschwinden.
|
||||||
|
- Fortschreibung kopiert keine Rohbeobachtung als aktuelle Evidenz. Sie erhält eine Referenz mit Zustand, Grund und Alter; neue Beobachtungen werden dazu verknüpft.
|
||||||
|
- Ablösung erfolgt je Verpflichtung durch aktuelle Bestätigung, belegte Behebung oder eine ausdrücklich auditierte Scopeentscheidung. Eine spätere Dateizeit, ein nichtleerer Scan oder `assessed > 0` genügt nicht.
|
||||||
|
- Die erwartete Quelle kann aktuelle und ungeklärte Teilbereiche zugleich besitzen. Die bisherigen Quellzustände `observed-current`, `carried-forward-awaiting-revalidation` und `retired-out-of-scope` dürfen diesen Mischfall nicht zu „alles aktuell“ verdichten.
|
||||||
|
- Backlog und Evidenzabdeckung sind getrennt vom gemessenen Score. Historische Risiken verschwinden nicht bei Quellenauswahl; ein alter Score wird nicht als Messwert von B eingesetzt. Die frühere Formulierung „scorewirksam“ autorisiert keinen erfundenen numerischen Malus. Ein zusätzlicher risikogewichteter Expositionsindex benötigt eine eigene Methodikentscheidung.
|
||||||
|
- Risikoakzeptanz bleibt separat sichtbar und beendet keine technische Abweichung. Reviewhistorie einschließlich zukünftiger Felder wird vollständig erhalten; ein Reimport verlängert oder erneuert keine Akzeptanz.
|
||||||
|
|
||||||
|
### 3.4 Vergleichspaar und Score
|
||||||
|
|
||||||
|
- Der Vergleich bindet genau A und B samt Projekt-, Assessment-, aktiven Run-, Datei- und Processingreferenzen. A muss fachlich vor B liegen; ungültige, identische oder nicht eindeutig geordnete Auswahlen liefern keinen verifizierten Fortschritt. Scanzeit wird nicht durch Importzeit ersetzt.
|
||||||
|
- Mehrteilige Dateien werden je originalem Scannerlauf geprüft. Mehrere Quellen dürfen nicht zu einem gemeinsamen `multiple`-Vertrag verschmolzen werden. Fehlende oder mehrdeutige Zuordnung sperrt die betroffene Aussage; übrige vergleichbare Bereiche bleiben mit ausgewiesenem Umfang nutzbar.
|
||||||
|
- Historie vor A darf offene Verpflichtungen und Wiederauftreten erklären, aber weder den Messwert von A ersetzen noch ungefragt die A/B-Scorebasis verändern.
|
||||||
|
- Der Scorevergleich verlangt eine gemeinsame belegte Prüfpopulation einschließlich der erforderlichen Objekt-/Scopeidentitäten, eindeutige Paarung, kompatible Verarbeitung, neuere Beobachtung sowie gleiche Scoremethodik und Gewichtung. Fehlende, hinzugekommene, entfernte oder ausgetauschte Objekte werden als Scopeänderung erkennbar, auch bei gleicher Anzahl.
|
||||||
|
- Native Gesamtwerte dürfen nicht auf eine kleinere Objektteilmenge heruntergerechnet werden, wenn deren Beitrag nicht separat belegt ist. Ohne vergleichbare native Grundgesamtheit ist für diesen Beitrag kein Delta verfügbar. Pass-rate-Teilmengen sind nur mit ausgewiesenen, vollständig identifizierten Prüfungen zulässig.
|
||||||
|
- Score- und Behebungsfreigabe sind getrennte Prädikate. Eine geänderte Gewichtung oder ein unbrauchbarer nativer Score kann das Delta verhindern, ohne eine belegte Einzelbehebung zu entwerten. Umgekehrt ist ein vergleichbarer Gesamtscore kein Behebungsnachweis.
|
||||||
|
- Die bestehenden Regeln für aktuelle Abweichungen werden gemeinsam verwendet (`isDeviation`, für GPOs `policyComparisonDisposition`). Fehler/Not run/Unknown bleiben Evidenzlücken. Historisch ungeklärte Verpflichtungen bleiben zusätzlich offen, auch wenn B keine aktuelle Abweichung dazu enthält.
|
||||||
|
|
||||||
|
Behebungsquoten beziehen sich auf eine explizite, deduplizierte Menge bestätigter offener Vergleichsinstanzen aus A. Der Zähler enthält nur nachgewiesene Behebungen dieser Menge; ungeklärte, nicht vergleichbare und aus dem Scope genommene Instanzen werden separat ausgewiesen und nicht still aus dem Nenner entfernt. Ein unbekannter oder leerer Nenner liefert keine behauptete Erfolgsquote. Neue Findings aus B und zusätzliche Verpflichtungen vor A gehören in den getrennten Bestandsbericht. Die reine Differenz offener Anzahlen ist ausdrücklich keine Behebungsquote; jede Kennzahl muss auf ihre zugrunde liegenden Identitäten zurückführbar sein.
|
||||||
|
|
||||||
|
## 4. Arbeitspakete und Reihenfolge
|
||||||
|
|
||||||
|
Die Paketstände beziehen sich ausschließlich auf nachgewiesene Ergebnisse dieses Arbeitspakets; ein begonnener Vertrag ist noch keine Produktintegration.
|
||||||
|
|
||||||
|
| Paket | Inhalt und betroffene Grenzen | Voraussetzung / Nachweis |
|
||||||
|
|---|---|---|
|
||||||
|
| P0 · abgeschlossen | Synthetisches Bestandsaudit je Scanner/Format/GPO: Regelgranularität, bereits erhaltene Objekt-/Scopefelder, Quellenpfade, Vollständigkeitssignale und fehlende Informationen dokumentieren; notwendige Vertragsänderungen und Reimportbetroffenheit bestimmen | [TIMESLOT_COMPARISON_AUDIT.md](TIMESLOT_COMPARISON_AUDIT.md); keine echten Kundendaten; Fähigkeiten und Grenzen je unterstütztem Pfad dokumentiert |
|
||||||
|
| P1 · in Arbeit | Versionierte Vergleichsidentität, Gegenprüfungs-/Diagnosevertrag und getrennte Ergebnis-/Verpflichtungszustände in der Domäne spezifizieren; Persistenz-/API-Entwurf und Übergangsregeln festlegen | Domänen- und Profilverträge sowie synthetische Scope-/Zeitfälle umgesetzt; Persistenz-/API- und Übergangsentwurf bleiben offen |
|
||||||
|
| P2 · in Arbeit | Scanner-/GPO-Paarung anhand echter Quellreferenzen; vollständige Kompatibilitätsprüfungen; objektgenauer Lebenszyklus, Fail-zu-Pass, konservative Abwesenheit und einheitliche aktuelle Abweichungsmetriken | Sicherer Scannerpfad mit echter Tool-/Run-/File-Paarung, Processing-, Scope-, Reportzeit- und Diagnosegrenzen umgesetzt; GPO, Historie, Produktintegration und vollständige Matrix bleiben offen |
|
||||||
|
| P3 | Verpflichtungsbezogene Fortschreibung über alle Zwischenstände, getrennt von der Score-Baseline; atomare/idempotente Ablage und API gemäß P1, Wiederladen und auditierter Scope-Ausstieg | P1/P2; nichts verschwindet bei Teilimport, Reload, Reimport oder mehrdeutiger Zuordnung |
|
||||||
|
| P4 | Explizite A/B-Scorebasis, objekt-/scopebewusste Population und getrennte Freigaben für Delta/Gegenprüfung; native Scoregrenzen und Coverageausgabe | P1/P2/P3; keine Verbesserung durch Quellenauslassung, Objektwechsel oder historische Baselinevermischung |
|
||||||
|
| P5 | A/B-Auswahl und dreiteiliger Bericht in der vorhandenen Oberfläche; Gründe, Messbasis, Quellen/Versionen und Belege pro Aussage; aktueller Workspace bleibt unverändert nutzbar | P2-P4 fachlich verifiziert; UI ist Konsument der Domänenlogik, kein eigener Matcher |
|
||||||
|
| P6 | Gesamtabnahme, Dokumentationsabgleich und kontrollierte Aktivierung | Alle unten genannten Fälle; keine Freigabe allein wegen vorhandener Funktion oder grüner Buildausgabe |
|
||||||
|
|
||||||
|
Keine frühe Verdrahtung der bisherigen `compareProjectTimeslots`-Ergebnisse als belastbarer Fortschrittsbericht. Nach Implementierungsbeginn und jedem verifizierten Teilpaket werden Status, offene Risiken und tatsächliche Nachweise im Hauptplan fortgeschrieben.
|
||||||
|
|
||||||
|
## 5. Versionierung, Bestand und Betrieb
|
||||||
|
|
||||||
|
- [REIMPORT_POLICY.md](REIMPORT_POLICY.md) bleibt verbindlich. Änderungen an importierter Identität, Findinggrenzen, Assetmapping, Normalisierung, Extraktion, Vollständigkeitsdiagnosen oder neu benötigten Scoreeingaben erhalten append-only Processing-Manifeste und Reimportmarkierungen für betroffene historische Verträge.
|
||||||
|
- Eine Änderung allein an der Vergleichsauswertung unveränderter, bereits ausreichend gespeicherter Fakten wird getrennt versioniert und benötigt eine nachvollziehbare Neuberechnung der Vergleichsergebnisse. Sie darf keine versteckte Änderung importierter Identität oder eine Ergänzung fehlender Evidenz sein. P0/P1 müssen diese Grenze pro Änderung dokumentieren; kein pauschaler Bestandsreimport und keine pauschale Reimportfreiheit.
|
||||||
|
- Neue Vergleichsergebnisse erhalten eine eigene Algorithmus-/Vertragsversion, genaue Eingabereferenzen und Freigabegründe. Historische Ergebnisse behalten ihre Bedeutung und werden als Legacy/nicht nach neuem Vertrag verifiziert gekennzeichnet; alte `resolved`-Werte werden nicht rückwirkend zu Beweisen erklärt.
|
||||||
|
- Der konkrete Speichervertrag für Verpflichtungen und Scopeentscheidungen wird vor Datenbankänderungen festgelegt. Persistenz/Backend bleiben außerhalb von React; Projektgrenzen, optimistische Revisionen, atomare Updates und Wiederholbarkeit sind Pflicht.
|
||||||
|
- Vor einer gesondert freigegebenen Migration: Backup erstellen und prüfen, Migration/Rückfallplan synthetisch verifizieren. Nur den kanonischen lokalen PostgreSQL-Datastore verwenden; keine Ersatzdatenbank, keine neuen Ports oder Volumes. Der Planauftrag führt weder Migration noch Reimport aus.
|
||||||
|
- Kundenreviews und Accepted-Risk-Historien dürfen weder verloren gehen noch auf andere Objekte geraten werden. Nicht eindeutig übertragbare Daten bleiben am Ursprung mit dauerhaft sichtbarem Klärungsbedarf.
|
||||||
|
|
||||||
|
## 6. Verbindliche synthetische Abnahmematrix
|
||||||
|
|
||||||
|
Die Matrix bleibt bis auf ausdrücklich im Paketstatus genannte Teilnachweise geplant. Domain-, Import-/Projektions-, Repository-/API- und UI-Ebene werden entsprechend ihrer Verantwortung geprüft.
|
||||||
|
|
||||||
|
| Fall | Erwarteter Nachweis |
|
||||||
|
|---|---|
|
||||||
|
| Dieselbe Regel auf Server A in A und Server B in B | Kein objektgenaues Match und keine Behebung für Server A |
|
||||||
|
| Fünf betroffene Objekte in A, drei erneut vorhanden in B | Drei nur bei Identitätsbeweis zugeordnet; zwei ungeklärt, niemals allein aus der Differenz behoben |
|
||||||
|
| Gleiche Anzahl und Regeln, aber ausgetauschte Zielobjekte | Scopeänderung; kein ungeprüftes vergleichbares Score-Delta |
|
||||||
|
| Dasselbe Objekt: Fail zu Pass / Fail zu Fail | Belegt behoben / weiterhin offen; Ergebnispräsenz ersetzt keinen Lebenszyklus |
|
||||||
|
| Kein oder doppelter Objektschlüssel, gleiche Titel, Umordnung | Kein geratenes Match; deterministische Diagnose und vollständige Erhaltung aller Vorkommen |
|
||||||
|
| Domainweite oder aggregierte Regel ohne Host-ID | Deklarierte Scopegranularität statt erfundener Objektinstanzen |
|
||||||
|
| Gesamte Scannerquelle fehlt; generische Begleitdateien bleiben | Offene Verpflichtungen bleiben, fehlende Quelle zählt nicht als Behebung |
|
||||||
|
| Unvollständiger Folgescan mit `assessed > 0` und Score-Issues | Kein pauschales Entfernen historischer Verpflichtungen; einzelne belegte Prüfungen getrennt behandelbar |
|
||||||
|
| A offen, Zwischenstand teilweise/leer/fehlerhaft, B ohne Quelle | Ursprüngliche Verpflichtung mit Originalherkunft bleibt sichtbar, auch nach Reload |
|
||||||
|
| `assessed === eligible`, aber ein erwartetes Objekt fehlt | Zählergleichheit allein erteilt keine Vollständigkeits- oder Behebungsfreigabe |
|
||||||
|
| Findings-only-Bericht enthält keinen Treffer | Behebung nur mit explizit erfülltem vollständigem Negativnachweisvertrag |
|
||||||
|
| Neuerer Scanzeitpunkt fehlt oder ist unverändert; bloßer Reimport | Kein gemessener Fortschritt aus Importzeit oder identischen Beobachtungen |
|
||||||
|
| Geänderte/ unbekannte Processingverträge, gemischte Scanner und Mehrdateiläufe | Schutz greift auch im Timeslotvergleich; keine Umgehung durch künstliche Lauf-IDs |
|
||||||
|
| Gewichtung/Scoremethode geändert, Einzelprüfung dennoch belastbar | Delta gesperrt/begrenzt, gültige Behebung unabhängig davon bewertbar |
|
||||||
|
| Neuere Evidenz wieder Fail nach belegtem Pass / nach ungeprüfter Lücke | Wiederaufgetreten / alte Verpflichtung weiter offen |
|
||||||
|
| Ältere Historie H vor A und B | A/B-Delta unverändert durch H; historische Verpflichtungen separat und nachvollziehbar |
|
||||||
|
| Keine vergleichbare Quelle oder fehlende Historie | Kein Null-Delta als Erfolg; nicht verfügbare Aussage mit Grund |
|
||||||
|
| Not applicable, Not run, Execution error, Unknown und Accepted Risk | Keine künstlichen Behebungen; aktuelle Abweichung, Evidenzlücke und Risikoentscheidung getrennt |
|
||||||
|
| GPO-Teilabdeckung, andere Baseline/Scope, fehlende Wirksamkeitsevidenz | Konservative getrennte Aussage gemäß Policyvertrag, keine erfundene effektive Behebung |
|
||||||
|
| Scope-Ausstieg mit/ohne ausdrückliche Begründung und Akteur | Nur auditierte Entscheidung wirksam; nicht als Behebung gezählt; Historie bleibt erhalten |
|
||||||
|
| Reimport, doppelte Anforderung, Konflikt oder fehlgeschlagene Transaktion | Keine verlorenen/doppelten Verpflichtungen; vollständige Reviews und Accepted Risk erhalten |
|
||||||
|
| Mandantenwechsel, manipulierte A/B-IDs, supersedierte Läufe | Projektgrenze und aktive Auswahl durch Domäne/API abgesichert |
|
||||||
|
| Auswahlwechsel, Filter, wiederholtes Öffnen, schmale Ansicht | Kein veraltetes Delta, vollständige Belege, klare A/B-Beschriftung, begrenzte Renderarbeit |
|
||||||
|
|
||||||
|
Für relevante Codeänderungen sind `npm test` und `npm run build` die Basis; zusätzlich gezielte Vergleichs-/Score-, Historien-, Importprojektions-, Persistenz-/API- und synthetische UI-Tests. Optionale PostgreSQL-Tests werden nur im freigegebenen Rahmen ausgeführt; übersprungene Tests ersetzen keine Persistenzabnahme. Laufzeitbeobachtungen sind keine fragilen Millisekunden-CI-Grenzen. Keine echten Reportinhalte, Screenshots gefüllter Kundenansichten, Telemetrie oder externen Uploads für die Verifikation.
|
||||||
|
|
||||||
|
## 7. Offene Detailentscheidungen und Abschlusskriterium
|
||||||
|
|
||||||
|
- P0/P1: exakte Identitätsfelder, Alias-/Umbenennungsbelege, Granularitäten und Negativnachweisfähigkeiten je unterstütztem Profil/Format; fehlende Fähigkeiten bleiben ausdrücklich nicht verifizierbar.
|
||||||
|
- P1/P3: additive Datensätze/Versionen für Verpflichtungen, Vergleichsergebnisse und Scopeentscheidungen; Rückfall-/Neuberechnungspfad, Projektberechtigungen und Grenzen der vorhandenen Akteursnachweise. Keine Behauptung einer authentifizierten Signatur ohne entsprechende Infrastruktur.
|
||||||
|
- P4: welche separat belegten Teilpopulationen pro Scoreverfahren tatsächlich berechenbar sind. Keine frei geschätzte Zerlegung nativer Gesamtscores; kein zusätzlicher Expositionsindex ohne separate Entscheidung.
|
||||||
|
- P5: konkrete Bedienposition und Belegnavigation im bestehenden Designsystem; keine neue Oberfläche oder neue Exportfunktion außerhalb des Vergleichsauftrags.
|
||||||
|
|
||||||
|
Das Paket ist erst abgeschlossen, wenn die Matrix auf den relevanten Schichten bestanden ist, dokumentierte Profilgrenzen in der Oberfläche sichtbar bleiben, A/B-Auswahl und Herkunft reproduzierbar sind und weder fehlende Evidenz noch Objekt-/Methodenwechsel als Verbesserung erscheinen. Plan, Vergleichsdokumentation und Scoringdokumentation müssen denselben verifizierten Stand beschreiben. Bis dahin lautet der Status weiterhin offen beziehungsweise nach Beginn in Arbeit.
|
||||||
|
|
||||||
|
## Verifikation der bisherigen Schnitte, 2026-09-11
|
||||||
|
|
||||||
|
- Sechs synthetische Domänentests bestätigen getrennte Regel-/Objektidentität, fehlende Typ-/Namespacefreigabe, Fail-zu-Pass nur bei gleicher Identität, konservative Abwesenheit, Diagnose-/Zeitgrenzen sowie Herkunftserhalt und auditierten Scope-Ausstieg.
|
||||||
|
- Client- und Serverbuild, Local-only-Sicherheitsprüfung und Designprüfung sind erfolgreich.
|
||||||
|
- Gesamtlauf: 1.224 Tests bestanden, acht optionale PostgreSQL-Tests übersprungen. Ein fachfremder kombinierter Node-Importtest überschritt unter Gesamtlast einmal sein unverändertes Fünf-Sekunden-Limit und bestand isoliert mit 5/5 Tests in 1.193 ms.
|
||||||
|
- Der erste Schnitt enthielt keine reale Dateninspektion. Über alle bisherigen Schnitte gab es keine Persistenzmutation, Migration, Reimport- oder UI-Aktivierung; P1/P2 bleiben ausdrücklich unvollständig.
|
||||||
|
- Nach ausdrücklicher Nutzerfreigabe wurde ausschließlich `Dummy` lesend ergänzt geprüft. Der Bestand bestätigt, dass Purple-Knight-`affectedAsset` nur Bereiche statt Objekte enthält und dass wiederholte Reimporte ohne neue Reportzeit keinen Fortschritt belegen. Keine Daten wurden verändert.
|
||||||
|
- Vier Profile besitzen nun scopegebundene Vergleichsverträge; Generic bleibt nicht automatisch vergleichbar. Die sichere Scannerpaarung zählt nur ein eindeutiges Fail-zu-Pass derselben Identität bei kompatibler Verarbeitung und neuerer Reportzeit als behoben. Fehlende Quellen, Abwesenheit und gleiche Reportzeit bleiben ungeklärt oder nicht vergleichbar.
|
||||||
|
- 23 fokussierte synthetische Tests in drei Dateien sowie Client- und Serverbuild sind erfolgreich. Persistenz/API, GPO, Verpflichtungshistorie, Score-A/B-Basis und UI bleiben offen.
|
||||||
|
- Projektweiter Lauf: 1.240 Tests bestanden, acht optionale PostgreSQL-Tests übersprungen. Derselbe fachfremde kombinierte Node-Importtest überschritt unter Gesamtlast einmal sein unverändertes Fünf-Sekunden-Limit und bestand isoliert mit 5/5 Tests in 3.661 ms. Local-only- und Designsprache-Prüfung sind erfolgreich; das Zeitlimit wurde nicht erhöht.
|
||||||
|
|
@ -7,7 +7,7 @@ Status: Policy-Finding- und Read-Vertikalschnitt umgesetzt; Gesamtmodell in Arbe
|
||||||
## Zweck
|
## Zweck
|
||||||
|
|
||||||
Das gemeinsame Result-Read-Model stellt Scanner-Finding-Vorkommen und
|
Das gemeinsame Result-Read-Model stellt Scanner-Finding-Vorkommen und
|
||||||
nachvollziehbar erzeugte bestätigte Policy-Abweichungen über eine einheitliche,
|
nachvollziehbar erzeugte Policy-Baseline-Vergleiche über eine einheitliche,
|
||||||
nicht schreibbare Abfragegrenze bereit. Es ersetzt weder das kanonische
|
nicht schreibbare Abfragegrenze bereit. Es ersetzt weder das kanonische
|
||||||
Finding-Modell noch die GPO-, Baseline- oder Evaluationsentitäten.
|
Finding-Modell noch die GPO-, Baseline- oder Evaluationsentitäten.
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ Finding-Modell noch die GPO-, Baseline- oder Evaluationsentitäten.
|
||||||
| Scannerpipeline | erfüllt | Formatadapter, Source Extractor, Candidate-Mapping und kanonische Persistenz sind getrennt | keine werkzeugspezifische Resultpipeline ergänzen |
|
| Scannerpipeline | erfüllt | Formatadapter, Source Extractor, Candidate-Mapping und kanonische Persistenz sind getrennt | keine werkzeugspezifische Resultpipeline ergänzen |
|
||||||
| GPO-Ingestion | teilweise | technische Identität, Baselineanalyse, Snapshotaggregation, Evaluation und Finding Factory sind vorhanden | freigegebene Inbox-ADMX-/Regelkataloge und UI fehlen |
|
| GPO-Ingestion | teilweise | technische Identität, Baselineanalyse, Snapshotaggregation, Evaluation und Finding Factory sind vorhanden | freigegebene Inbox-ADMX-/Regelkataloge und UI fehlen |
|
||||||
| PostgreSQL | erfüllt für den Read-Pfad | Migrationen 001–017 persistieren Scanner-, Policy-Evaluation- und Policy-Finding-Provenienz | vollständig SQL-seitige Facetten bleiben optional |
|
| PostgreSQL | erfüllt für den Read-Pfad | Migrationen 001–017 persistieren Scanner-, Policy-Evaluation- und Policy-Finding-Provenienz | vollständig SQL-seitige Facetten bleiben optional |
|
||||||
| Repository/API | erfüllt | gemeinsame Result-, Facetten-, Detail- und Kategorienqueries lesen Scanner- und bestätigte Policy-Findings in Local, PostgreSQL, HTTP und Client | UI-Anbindung bleibt offen |
|
| Repository/API | erfüllt | gemeinsame Result-, Facetten-, Detail- und Kategorienqueries lesen Scanner-Findings, neutrale Policy-Vergleiche und angereicherte Policy-Findings in Local, PostgreSQL, HTTP und Client | UI-Anbindung bleibt offen |
|
||||||
| UI-Filter | teilweise | generische dynamische Dataset-Facetten sind vorhanden | serverseitige gemeinsame Resultfacetten sind noch nicht angebunden |
|
| UI-Filter | teilweise | generische dynamische Dataset-Facetten sind vorhanden | serverseitige gemeinsame Resultfacetten sind noch nicht angebunden |
|
||||||
|
|
||||||
Gefährliche Doppelimplementierungen wären insbesondere eine zweite Scanner-
|
Gefährliche Doppelimplementierungen wären insbesondere eine zweite Scanner-
|
||||||
|
|
@ -74,13 +74,19 @@ Quelle -> Formatadapter -> DecodedSourceDocument -> Source Extractor
|
||||||
```text
|
```text
|
||||||
GPO-Backup -> GPOBackupInstance -> PolicyObservation -> PolicyIdentity
|
GPO-Backup -> GPOBackupInstance -> PolicyObservation -> PolicyIdentity
|
||||||
-> EvaluationRun -> PolicyEvaluationResult
|
-> EvaluationRun -> PolicyEvaluationResult
|
||||||
-> Policy Finding Factory -> Policy Finding Occurrence
|
|
||||||
-> Unified Result Projection
|
-> Unified Result Projection
|
||||||
|
-> optional: Policy Finding Factory -> angereicherte Projektion
|
||||||
```
|
```
|
||||||
|
|
||||||
Die Policyseite projiziert ausschließlich vollständig belegte, bereits als
|
Die Evaluation vergleicht eine technisch eindeutig aufgelöste Kundeneinstellung
|
||||||
`eligible` bestätigte konfigurierte Abweichungen. Review- und Lückenfälle
|
neutral direkt mit dem Wert der importierten, eindeutig passenden Baseline. Fehlt
|
||||||
bleiben außerhalb der Finding-Projektion.
|
eine zusätzliche Katalogregel vollständig, ist ein typgetreuer Equality-Vergleich
|
||||||
|
zulässig; er erzeugt Matches oder konfigurierte technische Abweichungen, aber
|
||||||
|
keine Severity und keine Finding-Freigabe. Jeder deterministische Vergleich wird
|
||||||
|
direkt als `policy_deviation` unter dem Source Tool `GPOs` projiziert. Die
|
||||||
|
Policy-Finding-Projektion ersetzt die neutrale Zeile nur bei vollständig belegten,
|
||||||
|
durch eine freigegebene Risikoregel als `eligible` bestätigten Abweichungen und
|
||||||
|
ergänzt dann Severity sowie Risikokontext.
|
||||||
|
|
||||||
## Statusdimensionen
|
## Statusdimensionen
|
||||||
|
|
||||||
|
|
@ -120,8 +126,10 @@ Die API stellt Results, Facetten, Kategorien und Details unter `/api/v1/results`
|
||||||
- Die PostgreSQL-Read-Projektion leitet Kategorien derzeit weiterhin aus den
|
- Die PostgreSQL-Read-Projektion leitet Kategorien derzeit weiterhin aus den
|
||||||
versionierten Domainobjekten ab; die relationale Struktur ist für den
|
versionierten Domainobjekten ab; die relationale Struktur ist für den
|
||||||
folgenden vollständig SQL-seitigen Filterpfad vorbereitet.
|
folgenden vollständig SQL-seitigen Filterpfad vorbereitet.
|
||||||
- Der produktive Policy-Regelkatalog ist bis zur fachlichen Kuratierung leer;
|
- Der produktive Policy-Regelkatalog ist bis zur fachlichen Kuratierung leer.
|
||||||
deshalb entstehen aus dem realen Bestand weiterhin keine Policy-Findings.
|
Neutrale Baseline-/GPO-Vergleiche funktionieren ohne ihn; Policy-Findings,
|
||||||
|
Severity, stärker/schwächer und Abhängigkeitsauswertung entstehen weiterhin
|
||||||
|
erst nach fachlicher Freigabe entsprechender Regeln.
|
||||||
- Konflikte, Katalog-/Parserlücken, partielle Analysen und unaufgelöste
|
- Konflikte, Katalog-/Parserlücken, partielle Analysen und unaufgelöste
|
||||||
Identitäten benötigen noch eine gemeinsame UI-Darstellung.
|
Identitäten benötigen noch eine gemeinsame UI-Darstellung.
|
||||||
- Lebenszykluswerte aus Vergleichen werden im ersten Schnitt nicht erfunden.
|
- Lebenszykluswerte aus Vergleichen werden im ersten Schnitt nicht erfunden.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
# Context: fehlende Pass-Regeln (Client Dummy)
|
||||||
|
|
||||||
|
Stand: 2026-10-04 · Context Engine 1.10.0 · Katalog 1.9.1 · Scope: alle drei Dummy-Timeslots
|
||||||
|
|
||||||
|
Analyse-Liste, keine Katalogänderung. Aufgeführt ist jede Regel, deren Pass-Ergebnis laut Scan-Bericht in keiner Context-Bedingung vorkommt. Abweichungen sind vollständig katalogisiert; diese Lücke betrifft nur Pass-Belege. PingCastle fehlt, weil der Bericht keine Pass-Ergebnisse enthält.
|
||||||
|
|
||||||
|
Eine Regel gehört erst nach geprüfter Zuordnung zu einem Cluster in den Katalog: exakte Regel-ID mit Polarität `fail-means-met`, bei CIS-CAT der Berichtsabschnitt. Keine Heuristik.
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
| Quelle | Pass-Regeln gesamt | davon im Context | fehlend |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Purple Knight | 96 | 20 | 76 |
|
||||||
|
| Maester | 74 | 8 | 66 |
|
||||||
|
| CIS-CAT | 87 | 54 | 33 |
|
||||||
|
| GPO (Einstellungen) | 25 | 7 | 18 |
|
||||||
|
|
||||||
|
## Purple Knight
|
||||||
|
|
||||||
|
| Regel-ID | Titel | Severity (Quelle) | Kategorie (Quelle) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `SI000013` | Evidence of Mimikatz DCShadow attack | Critical | AD Infrastructure Security |
|
||||||
|
| `SI000014` | Users and computers with non-default Primary Group IDs | Low | Account Security |
|
||||||
|
| `SI000015` | Reversible passwords found in GPOs | Critical | Group Policy Security |
|
||||||
|
| `SI000017` | Kerberos KRBTGT account with old password | Medium | Kerberos Security |
|
||||||
|
| `SI000018` | Non default value on ms-Mcs-AdmPwd SearchFlags | High | AD Delegation |
|
||||||
|
| `SI000021` | Non-default principals with DC Sync rights on the domain | Critical | AD Delegation |
|
||||||
|
| `SI000022` | Risky RODC credential caching | Medium | AD Infrastructure Security |
|
||||||
|
| `SI000023` | Well-known privileged SIDs in SIDHistory | Critical | AD Infrastructure Security |
|
||||||
|
| `SI000025` | Privileged objects with unprivileged owners | High | AD Delegation |
|
||||||
|
| `SI000027` | Users with Kerberos pre-authentication disabled | Medium | Account Security |
|
||||||
|
| `SI000032` | GPO linking delegation at the domain level | High | Group Policy Security |
|
||||||
|
| `SI000033` | Recent privileged account creation activity | Low | Account Security |
|
||||||
|
| `SI000037` | Users with old passwords | Low | Account Security |
|
||||||
|
| `SI000038` | Enabled admin accounts that are inactive | Medium | Account Security |
|
||||||
|
| `SI000039` | Privileged users that are disabled | Low | Account Security |
|
||||||
|
| `SI000040` | GPO linking delegation at the domain controller OU level | High | Group Policy Security |
|
||||||
|
| `SI000041` | GPO linking delegation at the AD Site level | High | Group Policy Security |
|
||||||
|
| `SI000042` | Users with SPN defined | Low | Kerberos Security |
|
||||||
|
| `SI000043` | Changes to privileged group membership in the last 7 days | Medium | Account Security |
|
||||||
|
| `SI000044` | AD objects created within the last 10 days | Informational | Account Security |
|
||||||
|
| `SI000046` | Objects in privileged groups without adminCount=1 (SDProp) | Medium | AD Delegation |
|
||||||
|
| `SI000047` | Changes to default security descriptor schema in the last 90 days | High | AD Delegation |
|
||||||
|
| `SI000048` | Changes to Default Domain Policy or Default Domain Controllers Policy in the last 7 days | Medium | Group Policy Security |
|
||||||
|
| `SI000049` | Built-in domain Administrator account with old password (180 days) | Medium | Account Security |
|
||||||
|
| `SI000050` | Domain Controller owner is not an administrator | High | AD Delegation |
|
||||||
|
| `SI000051` | Domains with obsolete functional levels | Low | AD Infrastructure Security |
|
||||||
|
| `SI000053` | Operator groups no longer protected by AdminSDHolder and SDProp | Medium | AD Infrastructure Security |
|
||||||
|
| `SI000054` | Computers with older OS versions | Medium | AD Infrastructure Security |
|
||||||
|
| `SI000056` | Enterprise Key Admins with full access to domain | High | AD Delegation |
|
||||||
|
| `SI000057` | Objects with constrained delegation configured | Medium | Kerberos Security |
|
||||||
|
| `SI000058` | Kerberos protocol transition delegation configured | High | Kerberos Security |
|
||||||
|
| `SI000059` | Computers with password last set over 90 days ago | High | AD Infrastructure Security |
|
||||||
|
| `SI000060` | Recent SIDHistory changes on objects | Medium | Account Security |
|
||||||
|
| `SI000061` | Computer Accounts in Privileged Groups | High | Account Security |
|
||||||
|
| `SI000062` | Admins with old passwords | Low | Account Security |
|
||||||
|
| `SI000063` | Domain Controllers with old passwords | Low | AD Infrastructure Security |
|
||||||
|
| `SI000064` | Forest contains more than 50 privileged accounts | Medium | Account Security |
|
||||||
|
| `SI000068` | Trust accounts with old passwords | Low | Account Security |
|
||||||
|
| `SI000069` | Domain Controllers in inconsistent state | Low | AD Infrastructure Security |
|
||||||
|
| `SI000072` | gMSA objects with old passwords | High | AD Infrastructure Security |
|
||||||
|
| `SI000073` | Domain Controllers that have not authenticated to the domain for more than 45 days | Medium | AD Infrastructure Security |
|
||||||
|
| `SI000074` | User accounts with password not required | High | Account Security |
|
||||||
|
| `SI000075` | User accounts that use DES encryption | Medium | Account Security |
|
||||||
|
| `SI000076` | User accounts that store passwords with reversible encryption | Medium | Account Security |
|
||||||
|
| `SI000078` | Users and computers without readable PGID | Medium | Account Security |
|
||||||
|
| `SI000081` | Non-standard schema permissions | High | AD Infrastructure Security |
|
||||||
|
| `SI000082` | Changes to AD Display Specifiers in the past 90 days | Low | AD Delegation |
|
||||||
|
| `SI000083` | Non-privileged users with access to gMSA passwords | High | AD Delegation |
|
||||||
|
| `SI000086` | Weak certificate cipher | Critical | AD Infrastructure Security |
|
||||||
|
| `SI000087` | Domain trust to a third-party domain without quarantine | Medium | AD Infrastructure Security |
|
||||||
|
| `SI000088` | Outbound forest trust with SID History enabled | High | AD Infrastructure Security |
|
||||||
|
| `SI000089` | Users with permissions to set Server Trust Account | Critical | AD Delegation |
|
||||||
|
| `SI000092` | Non-default access to DPAPI key | High | AD Delegation |
|
||||||
|
| `SI000097` | NTFRS SYSVOL Replication | High | AD Infrastructure Security |
|
||||||
|
| `SI000098` | Abnormal Password Refresh | Medium | Account Security |
|
||||||
|
| `SI000100` | Accounts with Constrained Delegation configured to ghost SPN | High | Kerberos Security |
|
||||||
|
| `SI000101` | SYSVOL Executable Changes | Low | Group Policy Security |
|
||||||
|
| `SI000102` | Accounts with altSecurityIdentities configured | High | Kerberos Security |
|
||||||
|
| `SI000103` | FGPP not applied to Global group | Medium | Account Security |
|
||||||
|
| `SI000106` | Inheritance enabled on AdminSDHolder object | Critical | AD Delegation |
|
||||||
|
| `SI000108` | Dangerous Trust Attribute Set | High | AD Infrastructure Security |
|
||||||
|
| `SI000110` | Ephemeral Admins | Low | Account Security |
|
||||||
|
| `SI000124` | Foreign Security Principals in Privileged Group | Medium | AD Delegation |
|
||||||
|
| `SI000138` | Privileged user credentials cached on RODC | Medium | AD Infrastructure Security |
|
||||||
|
| `SI000150` | Operators Groups that are not empty | High | Account Security |
|
||||||
|
| `SI000185` | Objects with Reanimate-Tombstones extended right | Medium | AD Delegation |
|
||||||
|
| `SI000188` | GPO Weak LM Hash storage enabled | High | Group Policy Security |
|
||||||
|
| `SI000189` | Writable shortcuts found in GPO | High | Group Policy Security |
|
||||||
|
| `SI000193` | OU permissions enabling BadSuccessor dMSA escalation | High | Account Security |
|
||||||
|
| `SI000217` | Query policies that have the attribute of LDAP deny list set | Medium | AD Infrastructure Security |
|
||||||
|
| `SI000236` | User accounts using Smart Card authentication with old password | High | Account Security |
|
||||||
|
| `SI000304` | Dangerous GPO logon script path | High | Group Policy Security |
|
||||||
|
| `SI000314` | Unexpected accounts in Cert Publishers Group | High | AD Infrastructure Security |
|
||||||
|
| `SI000316` | GPO with Scheduled Tasks configured | Low | Group Policy Security |
|
||||||
|
| `SI000339` | Users with the attribute userPassword set | Medium | Kerberos Security |
|
||||||
|
| `SI000355` | Distributed COM Users group or Performance Log Users group are not empty | High | Account Security |
|
||||||
|
|
||||||
|
## Maester
|
||||||
|
|
||||||
|
| Regel-ID | Titel | Severity (Quelle) | Kategorie (Quelle) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `CIS.M365.1.3.1` | (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire (recommended)' | High | ["CIS.M365.1.3.1","L1","CIS E3 Level 1","CIS E3","CIS","Security","All","CIS M365 v4.0.0"], CIS, CIS.M365.1.3.1, L1, CIS E3 Level 1, CIS E3, CIS, Security, CIS M365 v5.0.0 |
|
||||||
|
| `CISA.MS.AAD.2.1` | Users detected as high risk SHALL be blocked. | High | ["MS.AAD","MS.AAD.2.1","CISA.MS.AAD.2.1","CISA","Security","All","Entra ID P2"], CISA, MS.AAD, MS.AAD.2.1, CISA.MS.AAD.2.1, CISA, Security, Entra ID P2 |
|
||||||
|
| `CISA.MS.AAD.2.2` | A notification SHOULD be sent to the administrator when high-risk users are detected. | High | ["MS.AAD","MS.AAD.2.2","CISA.MS.AAD.2.2","CISA","Security","All","Entra ID P2"], CISA, MS.AAD, MS.AAD.2.2, CISA.MS.AAD.2.2, CISA, Security, Entra ID P2 |
|
||||||
|
| `CISA.MS.AAD.3.2` | If phishing-resistant MFA has not been enforced, an alternative MFA method SHALL be enforced for all users. | High | ["MS.AAD","MS.AAD.3.2","CISA.MS.AAD.3.2","CISA","Security","All","Entra ID P1"], CISA, MS.AAD, MS.AAD.3.2, CISA.MS.AAD.3.2, CISA, Security, Entra ID P1 |
|
||||||
|
| `CISA.MS.AAD.3.4` | The Authentication Methods Manage Migration feature SHALL be set to Migration Complete. | High | ["MS.AAD","MS.AAD.3.4","CISA.MS.AAD.3.4","CISA","Security","All","Entra ID P1"], CISA, MS.AAD, MS.AAD.3.4, CISA.MS.AAD.3.4, CISA, Security, Entra ID P1 |
|
||||||
|
| `CISA.MS.AAD.5.1` | Only administrators SHALL be allowed to register applications. | High | ["MS.AAD","MS.AAD.5.1","CISA.MS.AAD.5.1","CISA","Security","All","Entra ID Free"], CISA, MS.AAD, MS.AAD.5.1, CISA.MS.AAD.5.1, CISA, Security, Entra ID Free |
|
||||||
|
| `CISA.MS.AAD.6.1` | User passwords SHALL NOT expire. | High | ["MS.AAD","MS.AAD.6.1","CISA.MS.AAD.6.1","CISA","Security","All","Entra ID Free"], CISA, MS.AAD, MS.AAD.6.1, CISA.MS.AAD.6.1, CISA, Security, Entra ID Free |
|
||||||
|
| `CISA.MS.AAD.7.5` | Provisioning users to highly privileged roles SHALL NOT occur outside of a PAM system. | High | ["MS.AAD","MS.AAD.7.5","CISA.MS.AAD.7.5","CISA","Security","All","Entra ID P2"], CISA, MS.AAD, MS.AAD.7.5, CISA.MS.AAD.7.5, CISA, Security, Entra ID P2 |
|
||||||
|
| `CISA.MS.AAD.7.6` | Activation of the Global Administrator role SHALL require approval. | High | ["MS.AAD","MS.AAD.7.6","CISA.MS.AAD.7.6","CISA","Security","All","Entra ID P2"], CISA, MS.AAD, MS.AAD.7.6, CISA.MS.AAD.7.6, CISA, Security, Entra ID P2 |
|
||||||
|
| `CISA.MS.AAD.7.8` | User activation of the Global Administrator role SHALL trigger an alert. | High | ["MS.AAD","MS.AAD.7.8","CISA.MS.AAD.7.8","CISA","Security","All","Entra ID P2"], CISA, MS.AAD, MS.AAD.7.8, CISA.MS.AAD.7.8, CISA, Security, Entra ID P2 |
|
||||||
|
| `CT001` | User device quota should be set to 10 | — | Custom Security Tests, Custom, Security, Device |
|
||||||
|
| `EIDSCA.AF01` | Authentication Method - FIDO2 security key - State. | High | ["EIDSCA","Security","All","EIDSCA.AF01"], EIDSCA, EIDSCA, Security, EIDSCA.AF01 |
|
||||||
|
| `EIDSCA.AF02` | Authentication Method - FIDO2 security key - Allow self-service set up. | Medium | ["EIDSCA","Security","All","EIDSCA.AF02"], EIDSCA, EIDSCA, Security, EIDSCA.AF02 |
|
||||||
|
| `EIDSCA.AG01` | Authentication Method - General Settings - Manage migration. | High | ["EIDSCA","Security","All","EIDSCA.AG01"], EIDSCA, EIDSCA, Security, EIDSCA.AG01 |
|
||||||
|
| `EIDSCA.AG03` | Authentication Method - General Settings - Report suspicious activity - Included users/groups. | Medium | ["EIDSCA","Security","All","EIDSCA.AG03"], EIDSCA, EIDSCA, Security, EIDSCA.AG03 |
|
||||||
|
| `EIDSCA.AM01` | Authentication Method - Microsoft Authenticator - State. | High | ["EIDSCA","Security","All","EIDSCA.AM01"], EIDSCA, EIDSCA, Security, EIDSCA.AM01 |
|
||||||
|
| `EIDSCA.AM03` | Authentication Method - Microsoft Authenticator - Require number matching for push notifications. | Medium | ["EIDSCA","Security","All","EIDSCA.AM03"], EIDSCA, EIDSCA, Security, EIDSCA.AM03 |
|
||||||
|
| `EIDSCA.AM04` | Authentication Method - Microsoft Authenticator - Included users/groups of number matching for push notifications. | Medium | ["EIDSCA","Security","All","EIDSCA.AM04"], EIDSCA, EIDSCA, Security, EIDSCA.AM04 |
|
||||||
|
| `EIDSCA.AM06` | Authentication Method - Microsoft Authenticator - Show application name in push and passwordless notifications. | Medium | ["EIDSCA","Security","All","EIDSCA.AM06"], EIDSCA, EIDSCA, Security, EIDSCA.AM06 |
|
||||||
|
| `EIDSCA.AM07` | Authentication Method - Microsoft Authenticator - Included users/groups to show application name in push and passwordless notifications. | Medium | ["EIDSCA","Security","All","EIDSCA.AM07"], EIDSCA, EIDSCA, Security, EIDSCA.AM07 |
|
||||||
|
| `EIDSCA.AM09` | Authentication Method - Microsoft Authenticator - Show geographic location in push and passwordless notifications. | Medium | ["EIDSCA","Security","All","EIDSCA.AM09"], EIDSCA, EIDSCA, Security, EIDSCA.AM09 |
|
||||||
|
| `EIDSCA.AM10` | Authentication Method - Microsoft Authenticator - Included users/groups to show geographic location in push and passwordless notifications. | Medium | ["EIDSCA","Security","All","EIDSCA.AM10"], EIDSCA, EIDSCA, Security, EIDSCA.AM10 |
|
||||||
|
| `EIDSCA.AP01` | Default Authorization Settings - Enabled Self service password reset for administrators. | High | ["EIDSCA","Security","All","EIDSCA.AP01"], EIDSCA, EIDSCA, Security, EIDSCA.AP01 |
|
||||||
|
| `EIDSCA.AP08` | Default Authorization Settings - User consent policy assigned for applications. | Medium | ["EIDSCA","Security","All","EIDSCA.AP08"], EIDSCA, EIDSCA, Security, EIDSCA.AP08 |
|
||||||
|
| `EIDSCA.AP10` | Default Authorization Settings - Default User Role Permissions - Allowed to create Apps. | High | ["EIDSCA","Security","All","EIDSCA.AP10"], EIDSCA, EIDSCA, Security, EIDSCA.AP10 |
|
||||||
|
| `EIDSCA.AP14` | Default Authorization Settings - Default User Role Permissions - Allowed to read other users. | High | ["EIDSCA","Security","All","EIDSCA.AP14"], EIDSCA, EIDSCA, Security, EIDSCA.AP14 |
|
||||||
|
| `EIDSCA.AS04` | Authentication Method - SMS - Use for sign-in. | High | ["EIDSCA","Security","All","EIDSCA.AS04"], EIDSCA, EIDSCA, Security, EIDSCA.AS04 |
|
||||||
|
| `EIDSCA.AT01` | Authentication Method - Temporary Access Pass - State. | High | ["EIDSCA","Security","All","EIDSCA.AT01"], EIDSCA, EIDSCA, Security, EIDSCA.AT01 |
|
||||||
|
| `EIDSCA.AT02` | Authentication Method - Temporary Access Pass - One-time. | High | ["EIDSCA","Security","All","EIDSCA.AT02"], EIDSCA, EIDSCA, Security, EIDSCA.AT02 |
|
||||||
|
| `EIDSCA.AV01` | Authentication Method - Voice call - State. | High | ["EIDSCA","Security","All","EIDSCA.AV01"], EIDSCA, EIDSCA, Security, EIDSCA.AV01 |
|
||||||
|
| `EIDSCA.PR02` | Default Settings - Password Rule Settings - Password Protection - Enable password protection on Windows Server Active Directory. | High | ["EIDSCA","Security","All","EIDSCA.PR02"], EIDSCA, EIDSCA, Security, EIDSCA.PR02 |
|
||||||
|
| `EIDSCA.ST08` | Default Settings - Classification and M365 Groups - M365 groups - Allow Guests to become Group Owner. | Medium | ["EIDSCA","Security","All","EIDSCA.ST08"], EIDSCA, EIDSCA, Security, EIDSCA.ST08 |
|
||||||
|
| `EIDSCA.ST09` | Default Settings - Classification and M365 Groups - M365 groups - Allow Guests to have access to groups content. | Medium | ["EIDSCA","Security","All","EIDSCA.ST09"], EIDSCA, EIDSCA, Security, EIDSCA.ST09 |
|
||||||
|
| `MT.1001` | At least one Conditional Access policy is configured with device compliance. | Medium | ["Maester","CA","Security","All","MT.1001"], Maester/Entra, Maester, CA, Security, MT.1001 |
|
||||||
|
| `MT.1002` | App management restrictions on applications and service principals is configured and enabled. | High | ["Maester","App","Security","All","MT.1002"], Maester/Entra, Maester, App, Security, MT.1002 |
|
||||||
|
| `MT.1003` | At least one Conditional Access policy is configured with All Apps. | High | ["Maester","CA","Security","All","MT.1003"], Maester/Entra, Maester, CA, Security, MT.1003 |
|
||||||
|
| `MT.1004` | At least one Conditional Access policy is configured with All Apps and All Users. | High | ["Maester","CA","Security","All","MT.1004"], Maester/Entra, Maester, CA, Security, MT.1004 |
|
||||||
|
| `MT.1006` | At least one Conditional Access policy is configured to require MFA for admins. | High | ["Maester","CA","Security","All","MT.1006"], Maester/Entra, Maester, CA, Security, MT.1006 |
|
||||||
|
| `MT.1007` | At least one Conditional Access policy is configured to require MFA for all users. | High | ["Maester","CA","Security","All","MT.1007"], Maester/Entra, Maester, CA, Security, MT.1007 |
|
||||||
|
| `MT.1008` | At least one Conditional Access policy is configured to require MFA for Azure management. | High | ["Maester","CA","Security","All","MT.1008"], Maester/Entra, Maester, CA, Security, MT.1008 |
|
||||||
|
| `MT.1013` | At least one Conditional Access policy is configured to require new password when user risk is high. | High | ["Maester","CA","Security","All","MT.1013"], Maester/Entra, Maester, CA, Security, MT.1013 |
|
||||||
|
| `MT.1016` | At least one Conditional Access policy is configured to require MFA for guest access. | High | ["Maester","CA","Security","All","MT.1016"], Maester/Entra, Maester, CA, Security, MT.1016 |
|
||||||
|
| `MT.1022` | All users utilizing a P1 license should be licensed. | Medium | ["Entra","License","MT.1022"], Maester/Entra, Entra, License, MT.1022 |
|
||||||
|
| `MT.1023` | All users utilizing a P2 license should be licensed. | Medium | ["Entra","License","MT.1023"], Maester/Entra, Entra, License, MT.1023 |
|
||||||
|
| `MT.1024.oneAdmin` | Designate more than one global admin. | Low | Maester/Entra, Maester, Entra, Security, Recommendation, MT.1024, oneAdmin |
|
||||||
|
| `MT.1024.pwagePolicyNew` | Do not expire passwords. | High | Maester/Entra, Maester, Entra, Security, Recommendation, MT.1024, pwagePolicyNew |
|
||||||
|
| `MT.1024.roleOverlap` | Use least privileged administrative roles . | Low | Maester/Entra, Maester, Entra, Security, Recommendation, MT.1024, roleOverlap |
|
||||||
|
| `MT.1024.selfServicePasswordReset` | Enable self-service password reset. | Low | Maester/Entra, Maester, Entra, Security, Recommendation, MT.1024, selfServicePasswordReset |
|
||||||
|
| `MT.1024.servicePrincipalKeyExpiry` | Renew expiring service principal credentials. | High | Maester/Entra, Maester, Entra, Security, Recommendation, MT.1024, servicePrincipalKeyExpiry |
|
||||||
|
| `MT.1026` | No hybrid user with permanent role assignment on Control Plane. | High | ["Maester","Privileged","Security","All","MT.1026"], Maester/Entra, Maester, Privileged, Security, MT.1026 |
|
||||||
|
| `MT.1029` | Stale accounts are not assigned to privileged roles. | High | ["Privileged","Security","All","PIM","MT.1029"], Maester/Entra, Privileged, Security, PIM, MT.1029 |
|
||||||
|
| `MT.1031` | Privileged role on Control Plane are managed by PIM only. | High | ["Privileged","Security","All","PIM","MT.1031"], Maester/Entra, Privileged, Security, PIM, MT.1031 |
|
||||||
|
| `MT.1035` | All security groups assigned to Conditional Access Policies should be protected by RMAU. | High | ["Maester","CA","Security","All","MT.1035"], Maester/Entra, Maester, CA, Security, MT.1035 |
|
||||||
|
| `MT.1049` | Conditional Access policies for User Risk and Sign-in Risk should be configured separately. | High | ["Maester","CA","Security","All","MT.1049"], Maester/Entra, Maester, CA, Security, MT.1049 |
|
||||||
|
| `MT.1052` | At least one Conditional Access policy is targeting the Device Code authentication flow. | High | ["Maester","CA","Security","All","MT.1052"], Maester/Entra, Maester, CA, Security, MT.1052 |
|
||||||
|
| `MT.1055` | Microsoft 365 Group (and Team) creation should be restricted to approved users. | Medium | ["Maester","Group","Security","All","MT.1055"], Maester/Entra, Maester, Group, Security, MT.1055 |
|
||||||
|
| `MT.1059.d41d8cd98f00b204e9800998ecf8427e` | MDI Health Issues - . | — | Defender for Identity health issues, Maester, Defender, Security, MDI, MT.1059, Severity:, |
|
||||||
|
| `MT.1061` | Device registration MFA control conflicts with Conditional Access policies. | Medium | Maester/Entra, Maester, CA, Security, MT.1061 |
|
||||||
|
| `MT.1065` | Ensure all Recovery Services Vaults have soft delete enabled | High | AzureConfig, Backup, Azure, MT.1065 |
|
||||||
|
| `MT.1067` | Authentication method policies should not reference non-existent groups. | Medium | Maester/Entra, Maester, Authentication, Security, MT.1067 |
|
||||||
|
| `MT.1068` | Restrict non-admin users from creating tenants. | Medium | Maester/Entra, Governance, Entra, Security, MT.1068 |
|
||||||
|
| `MT.1069` | Restrict non-admin users from creating security groups. | Low | Maester/Entra, Governance, Entra, Security, MT.1069 |
|
||||||
|
| `MT.1070` | Restrict device join to selected users/groups or none. | Medium | Maester/Entra, Governance, Entra, Security, MT.1070 |
|
||||||
|
| `MT.1072` | Conditional access policies should not use the deprecated Approved Client App grant. | High | Maester/Entra, Maester, CA, Security, MT.1072 |
|
||||||
|
| `MT.1074` | Ensure no more then 100 outbound mails per day are send using the .onmicrosoft.com domain | Medium | Maester/Exchange, Maester, Exchange, MT.1074 |
|
||||||
|
| `MT.1085` | Pending approvals for Critical Asset Management should not be present. | Medium | Exposure Management, Entra, Graph, Security, XSPM, MT.1085 |
|
||||||
|
|
||||||
|
## CIS-CAT
|
||||||
|
|
||||||
|
Abschnitt = erste zwei Ebenen der Regelnummer. Der Context ordnet CIS-CAT über den Berichtsabschnitt zu; ein ganzer nicht katalogisierter Abschnitt kann daher mit einem Eintrag geschlossen werden.
|
||||||
|
|
||||||
|
| Regel-ID | Abschnitt | Titel |
|
||||||
|
|---|---|---|
|
||||||
|
| `1.1.1` | 1.1 | 1.1.1 Ensure 'Enforce password history' is set to '24 or more password(s)' |
|
||||||
|
| `1.1.2` | 1.1 | 1.1.2 Ensure 'Maximum password age' is set to '365 or fewer days, but not 0' |
|
||||||
|
| `1.1.3` | 1.1 | 1.1.3 Ensure 'Minimum password age' is set to '1 or more day(s)' |
|
||||||
|
| `1.1.7` | 1.1 | 1.1.7 Ensure 'Store passwords using reversible encryption' is set to 'Disabled' |
|
||||||
|
| `2.3.4.1` | 2.3 | 2.3.4.1 Ensure 'Devices: Prevent users from installing printer drivers' is set to 'Enabled' |
|
||||||
|
| `2.3.6.1` | 2.3 | 2.3.6.1 Ensure 'Domain member: Digitally encrypt or sign secure channel data (always)' is set to 'Enabled' |
|
||||||
|
| `2.3.6.2` | 2.3 | 2.3.6.2 Ensure 'Domain member: Digitally encrypt secure channel data (when possible)' is set to 'Enabled' |
|
||||||
|
| `2.3.6.3` | 2.3 | 2.3.6.3 Ensure 'Domain member: Digitally sign secure channel data (when possible)' is set to 'Enabled' |
|
||||||
|
| `2.3.6.4` | 2.3 | 2.3.6.4 Ensure 'Domain member: Disable machine account password changes' is set to 'Disabled' |
|
||||||
|
| `2.3.6.5` | 2.3 | 2.3.6.5 Ensure 'Domain member: Maximum machine account password age' is set to '30 or fewer days, but not 0' |
|
||||||
|
| `2.3.6.6` | 2.3 | 2.3.6.6 Ensure 'Domain member: Require strong (Windows 2000 or later) session key' is set to 'Enabled' |
|
||||||
|
| `2.3.8.2` | 2.3 | 2.3.8.2 Ensure 'Microsoft network client: Send unencrypted password to third-party SMB servers' is set to 'Disabled' |
|
||||||
|
| `2.3.9.1` | 2.3 | 2.3.9.1 Ensure 'Microsoft network server: Amount of idle time required before suspending session' is set to '15 or fewer minute(s)' |
|
||||||
|
| `2.3.9.3` | 2.3 | 2.3.9.3 Ensure 'Microsoft network server: Disconnect clients when logon hours expire' is set to 'Enabled' |
|
||||||
|
| `2.3.10.1` | 2.3 | 2.3.10.1 Ensure 'Network access: Allow anonymous SID/Name translation' is set to 'Disabled' |
|
||||||
|
| `2.3.10.5` | 2.3 | 2.3.10.5 Ensure 'Network access: Let Everyone permissions apply to anonymous users' is set to 'Disabled' |
|
||||||
|
| `2.3.10.8` | 2.3 | 2.3.10.8 Ensure 'Network access: Remotely accessible registry paths' is configured |
|
||||||
|
| `2.3.10.9` | 2.3 | 2.3.10.9 Ensure 'Network access: Remotely accessible registry paths and sub-paths' is configured |
|
||||||
|
| `2.3.10.10` | 2.3 | 2.3.10.10 Ensure 'Network access: Restrict anonymous access to Named Pipes and Shares' is set to 'Enabled' |
|
||||||
|
| `2.3.10.12` | 2.3 | 2.3.10.12 Ensure 'Network access: Shares that can be accessed anonymously' is set to 'None' |
|
||||||
|
| `2.3.10.13` | 2.3 | 2.3.10.13 Ensure 'Network access: Sharing and security model for local accounts' is set to 'Classic - local users authenticate as themselves' |
|
||||||
|
| `2.3.13.1` | 2.3 | 2.3.13.1 Ensure 'Shutdown: Allow system to be shut down without having to log on' is set to 'Disabled' |
|
||||||
|
| `2.3.15.1` | 2.3 | 2.3.15.1 Ensure 'System objects: Require case insensitivity for non-Windows subsystems' is set to 'Enabled' |
|
||||||
|
| `2.3.15.2` | 2.3 | 2.3.15.2 Ensure 'System objects: Strengthen default permissions of internal system objects (e.g. Symbolic Links)' is set to 'Enabled' |
|
||||||
|
| `2.3.17.4` | 2.3 | 2.3.17.4 Ensure 'User Account Control: Detect application installations and prompt for elevation' is set to 'Enabled' |
|
||||||
|
| `2.3.17.5` | 2.3 | 2.3.17.5 Ensure 'User Account Control: Only elevate UIAccess applications that are installed in secure locations' is set to 'Enabled' |
|
||||||
|
| `2.3.17.6` | 2.3 | 2.3.17.6 Ensure 'User Account Control: Run all administrators in Admin Approval Mode' is set to 'Enabled' |
|
||||||
|
| `2.3.17.7` | 2.3 | 2.3.17.7 Ensure 'User Account Control: Switch to the secure desktop when prompting for elevation' is set to 'Enabled' |
|
||||||
|
| `2.3.17.8` | 2.3 | 2.3.17.8 Ensure 'User Account Control: Virtualize file and registry write failures to per-user locations' is set to 'Enabled' |
|
||||||
|
| `19.7.5.1` | 19.7 | 19.7.5.1 Ensure 'Do not preserve zone information in file attachments' is set to 'Disabled' |
|
||||||
|
| `19.7.5.2` | 19.7 | 19.7.5.2 Ensure 'Notify antivirus programs when opening attachments' is set to 'Enabled' |
|
||||||
|
| `19.7.26.1` | 19.7 | 19.7.26.1 Ensure 'Prevent users from sharing files within their profile.' is set to 'Enabled' |
|
||||||
|
| `19.7.46.2.1` | 19.7 | 19.7.46.2.1 Ensure 'Prevent Codec Download' is set to 'Enabled' |
|
||||||
|
|
||||||
|
## GPO-Einstellungen (Verdict Passed)
|
||||||
|
|
||||||
|
| Einstellung | Scope | Technische Identität |
|
||||||
|
|---|---|---|
|
||||||
|
| allowdde | user | `gpo-policy-identity-fe0f949d14c555c06980c7f1be88ecab` |
|
||||||
|
| enableoleactions | user | `gpo-policy-identity-de40f1ecce2e45b1899716ec95948cb6` |
|
||||||
|
| EnableScriptBlockInvocationLogging | computer | `gpo-policy-identity-7b0456c1b66a87c2bb97a99315b119d4` |
|
||||||
|
| fAllowFullControl | computer | `gpo-policy-identity-6d0463b00d14f0674cebe9ed81bfca67` |
|
||||||
|
| fUseMailto | computer | `gpo-policy-identity-20a81de462534bd60111802c0ecd1ec6` |
|
||||||
|
| MaxTicketExpiry | computer | `gpo-policy-identity-8590d6843170a869ba261df8ce43b34e` |
|
||||||
|
| MaxTicketExpiryUnits | computer | `gpo-policy-identity-db2aa9231047c05e8914b11c1bbc1e18` |
|
||||||
|
| privilege rights · secreatepagefileprivilege | computer | `gpo-policy-identity-b6237595766e0910af2c9b5246f1c6ef` |
|
||||||
|
| privilege rights · seprofilesingleprocessprivilege | computer | `gpo-policy-identity-b79dd2a5cbf48fadac6a853bfa1588ce` |
|
||||||
|
| registry values · machine\system\currentcontrolset\control\lsa\nolmhash | computer | `gpo-policy-identity-8750b6186583a650aedbd8cde05b5dce` |
|
||||||
|
| registry values · machine\system\currentcontrolset\services\lanmanserver\parameters\enablesecuritysignature | computer | `gpo-policy-identity-07391ebdcd8b74f59c174e0dcf465774` |
|
||||||
|
| registry values · machine\system\currentcontrolset\services\lanmanserver\parameters\requiresecuritysignature | computer | `gpo-policy-identity-40df1cce2a18ab54de584d997a763dce` |
|
||||||
|
| registry values · machine\system\currentcontrolset\services\netlogon\parameters\requiresignorseal | computer | `gpo-policy-identity-68e6d42ea8fc7b9b8de723f2cba019ef` |
|
||||||
|
| registry values · machine\system\currentcontrolset\services\ntds\parameters\ldapserverintegrity | computer | `gpo-policy-identity-69964f975c2afa036c53821d833ac388` |
|
||||||
|
| Software\Policies\Microsoft\Edge\ExtensionInstallBlocklist | computer | `gpo-policy-identity-9c404b72d73c89d991f6bd879de714b2` |
|
||||||
|
| SOFTWARE\Policies\Microsoft\Windows\DeviceInstall\Restrictions\DenyDeviceClasses | computer | `gpo-policy-identity-9c23ac1bc5d4049c4460cfac648236af` |
|
||||||
|
| SOFTWARE\Policies\Microsoft\Windows\DeviceInstall\Restrictions\DenyDeviceIDs | computer | `gpo-policy-identity-71d55b57e12919c3eaf1ceb41f73f18d` |
|
||||||
|
| system access · lsaanonymousnamelookup | computer | `gpo-policy-identity-f9b99fd736dfaef934d63699413dc71f` |
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
# GPO-/Baselineanalyse – Bedrohungsmodell und Speichergrenze
|
# GPO-/Baselineanalyse – Bedrohungsmodell und Speichergrenze
|
||||||
|
|
||||||
Stand: 2026-08-04
|
Stand: 2026-09-06
|
||||||
|
|
||||||
|
## Tooluebergreifende AI search (Phase 7, 2026-09-06)
|
||||||
|
|
||||||
|
Die ausdrueckliche Nutzerfreigabe erweitert die Suche auf alle FindingReader-Tools. Der lokale, quellenbelegte Public-Identifier-Katalog ergaenzt die bisherigen GPO-Begriffe um exakte Registryziele, Windows-Rechte, ASR- und Scanner-Regelkennungen. Exportiert wird ausschliesslich die katalogeigene Schreibweise oder festes Fachvokabular; kein ungepruefter Berichtstext. Eng begrenzte numerische/boolesche Baselineziele werden nur fuer erkannte Einstellungen und als ausgewaehlter, nicht unabhaengig verifizierter Benchmarkwert weitergegeben. Kundenseitige Istwerte, Konten/SIDs, Namen, Quellpfade, freie Beschreibungen/Abhilfe und importierte URLs bleiben ausgeschlossen. Unbekannte Regeln bleiben als Topic-Suche gekennzeichnet. Keine automatische Anfrage oder Uebernahme von AI-Antworten in Bewertungen. Dies praezisiert/erweitert die historischen Freigaben unten; Details, Grenzen und Katalogpflege siehe `public-ai-search.md`. Google-Verbindungs-/Kontodatenverarbeitung und betriebliche Freigaben bleiben unveraendert relevant.
|
||||||
|
|
||||||
Phase: 13B, verifiziert bis zum Registry.pol-Parser sowie zur Job- und Ergebnispersistenz
|
Phase: 13B, verifiziert bis zum Registry.pol-Parser sowie zur Job- und Ergebnispersistenz
|
||||||
|
|
||||||
|
## AppLocker-Wertdarstellung (Phase 7, 2026-09-06)
|
||||||
|
|
||||||
|
Die reine Leseprojektion formatiert AppLocker-XML mit dem vorhandenen SAX-Parser als escapeten Text. Grenzen: 262.144 Zeichen, 2.000 Elemente, 32 Ebenen, 128 Attribute je Element. DTD, Processing Instructions, fremde Namespaces und fehlerhaftes XML werden nicht strukturiert dargestellt; der unveraenderte Wert bleibt inerte Textausgabe. Bedingungen, Ausnahmen und unbekannte Erweiterungen bleiben getrennt erhalten. Keine aktive HTML-Darstellung, externen Referenzabrufe, Bewertungsaenderung oder Mutation der Originaldaten.
|
||||||
|
|
||||||
## Schutzgüter
|
## Schutzgüter
|
||||||
|
|
||||||
- Vertraulichkeit unveränderter GPO- und Baselineoriginale
|
- Vertraulichkeit unveränderter GPO- und Baselineoriginale
|
||||||
|
|
@ -13,6 +22,14 @@ Phase: 13B, verifiziert bis zum Registry.pol-Parser sowie zur Job- und Ergebnisp
|
||||||
|
|
||||||
## Vertrauensgrenzen
|
## Vertrauensgrenzen
|
||||||
|
|
||||||
|
Conflict-Results-Leseprojektion (2026-09-06): Aus vorhandenen normalisierten Beobachtungen abgeleitete Konfliktvarianten und Quellreferenzen werden im bestehenden Results-Fenster ausschliesslich als escapete Tabellenzellen/Praeambel dargestellt, niemals als HTML, Markdown-Links oder aktive Pfade. Der interne JSON-Umschlag wird vor Darstellung auf Version, Form, zwei Tabellen, maximal acht Spalten/20.000 Zeilen je Tabelle und 10 MiB Text begrenzt; bei Fehlern bleibt die urspruengliche Evidenzansicht bestehen. Whitespace wird im Umschlag escapet, damit die generische Tabellenprojektion signifikante Wertunterschiede nicht verwischt. Keine neue externe Navigation, Uebermittlung, Datenbankmutation oder Vergleichsentscheidung.
|
||||||
|
|
||||||
|
Erweiterung AI-Suchkontext (2026-09-06, Phase 7): Auf ausdruecklichen Nutzerwunsch werden Scope (computer/user), exakt freigegebene oeffentliche Baselinebezeichnungen und feste Vergleichslabels als anonymes Backup-Pruefszenario uebermittelt. Diese Freigabe ersetzt die vorherige vollstaendige Auslassung dieser Kontextfelder. Audit-Abhilfe fuer bekannte Microsoft-Subkategorien System Integrity/Logoff wird nur aus einer vollstaendig passenden, begrenzten Grammatik mit zwei eindeutigen booleschen Sollflags und passendem Titel/Scope neu aufgebaut; Expected und Must include bleiben getrennt. Unbekannte IDs, Zusatztexte, mehrdeutige Felder und andere freie Abhilfetexte werden verworfen. Description-Platzhalter sowie nicht unabhaengig verifizierte importierte Beschreibungen bleiben ausgeschlossen. Keine automatische Uebermittlung, keine Kundennamen/Istwerte/Quellpfade und keine Live-Suche mit Test- oder Kundendaten. Oeffentliche Fachbegriffe allein sind keine Zusicherung von DSGVO-Konformitaet: Google verarbeitet weiterhin Verbindungs-/IP- und gegebenenfalls Kontodaten; betriebliche Freigaben bleiben erforderlich. Historische Schutzentscheidungen gelten ausserhalb dieser engen Nutzerfreigabe unveraendert.
|
||||||
|
|
||||||
|
Praezisierung AI search (2026-09-06): Auf ausdruecklichen Nutzerwunsch ersetzt ein Google-AI-Mode-Browserlink (`search?udm=50&q=...`) die klassische Suchnavigation. Der Prompt verlangt offizielle Microsoft-Quellen, genaue Konfigurationshinweise und versionsabhaengige Baselineempfehlungen; dies ist eine Anfrage an Google, keine Garantie ueber die Antwortqualitaet oder Quellenauswahl. Eine feste Liste oeffentlicher Firewall-Einstellungsnamen, Domain-/Private-/Public-Profilbezeichnungen und Microsoft-Produktversionen ergaenzt die bestehenden freigegebenen Begriffe. Nur exakte Einstellungsnamen werden uebernommen, indem auf die fest hinterlegte oeffentliche Schreibweise abgebildet wird; rohe Metadaten bleiben lokal. Unbekannte Namen fallen auf das Fachthema zurueck. Keine Aenderung an Opener-/Referrerschutz oder Klickpflicht; keine API-/Aboanbindung, keine automatische Pruefung privater Google-Kontodaten. Google kann AI Mode je nach Konto/Region sperren oder den Deep Link aendern; Suchantworten werden nicht in Assessor-Bewertungen uebernommen.
|
||||||
|
|
||||||
|
Nutzerfreigegebene Websuche (2026-09-06): Der GPO-FindingReader darf ausschliesslich nach einem bewussten Klick eine HTTPS-Suche bei Google mit `site:learn.microsoft.com` oeffnen. Die Query wird nur aus fest im Quellcode freigegebenem oeffentlichem Microsoft-Vokabular aufgebaut. Importierte Finding-/GPO-/Kunden-/Baseline-Namen, Ist-/Sollwerte, Registry-/Datei-/Netzwerkpfade, Beschreibungen, URLs und Identitaeten werden niemals in die Query kopiert. Fuer exakt bekannte oeffentliche Einstellungsnamen existiert eine kleine feste Zuordnung; ansonsten wird ein allgemeines Fachthema gesucht. `noopener`, `noreferrer` und `referrerPolicy=no-referrer` verhindern Opener-/Referrerweitergabe. Keine automatische Anfrage, Telemetrie, Vorabpruefung, Such-API oder Uebertragung von Uploads. Der Suchanbieter erhaelt technisch weiterhin die Browserverbindung/IP und ggf. seine eigenen Cookies; keine Zusage anonymer Nutzung. ADML-Beschreibungen und abgeleitete Abhilfe werden nur als escapeter Text gerendert, nicht als HTML oder ausfuehrbare Links. Diese ausdrueckliche Freigabe erweitert nicht die uebrigen lokalen Verarbeitungsgrenzen.
|
||||||
|
|
||||||
Alle Uploadbytes, Dateinamen, Browser-Relativpfade, Archivpfade, XML-Inhalte, Backupmetadaten, Zeitstempel und Herstellerpakete sind nicht vertrauenswürdig. Der Browser, Dateiendungen und Client-MIME sind keine Autorität. Das Backend validiert Bytes, Hash, Pfade, Größen, Beziehungen und Benutzerentscheidungen erneut. PostgreSQL speichert Fachzustand; Originalbytes liegen getrennt außerhalb des Webroots hinter einer backendkontrollierten Speicher-ID.
|
Alle Uploadbytes, Dateinamen, Browser-Relativpfade, Archivpfade, XML-Inhalte, Backupmetadaten, Zeitstempel und Herstellerpakete sind nicht vertrauenswürdig. Der Browser, Dateiendungen und Client-MIME sind keine Autorität. Das Backend validiert Bytes, Hash, Pfade, Größen, Beziehungen und Benutzerentscheidungen erneut. PostgreSQL speichert Fachzustand; Originalbytes liegen getrennt außerhalb des Webroots hinter einer backendkontrollierten Speicher-ID.
|
||||||
|
|
||||||
Ein Parserworker begrenzt Zeit und Speicher, isoliert aber keinen bösartigen nativen Code. Importierte Programme, Skripte, Bibliotheken und Makros werden niemals geladen oder ausgeführt. Ein später optionaler externer Adapter benötigt einen administrativ freigegebenen festen Pfad und Hash, einen niedrig privilegierten Prozess ohne Shell und Netzwerk sowie harte Zeit-, Umgebungs- und Ausgabegrenzen.
|
Ein Parserworker begrenzt Zeit und Speicher, isoliert aber keinen bösartigen nativen Code. Importierte Programme, Skripte, Bibliotheken und Makros werden niemals geladen oder ausgeführt. Ein später optionaler externer Adapter benötigt einen administrativ freigegebenen festen Pfad und Hash, einen niedrig privilegierten Prozess ohne Shell und Netzwerk sowie harte Zeit-, Umgebungs- und Ausgabegrenzen.
|
||||||
|
|
@ -36,7 +53,8 @@ Ein Parserworker begrenzt Zeit und Speicher, isoliert aber keinen bösartigen na
|
||||||
| Skript- oder Binärausführung | Nur inventarisieren und hashen; niemals importieren, laden, entschlüsseln oder starten. |
|
| Skript- oder Binärausführung | Nur inventarisieren und hashen; niemals importieren, laden, entschlüsseln oder starten. |
|
||||||
| Manipulierte oder herstellerspezifisch erweiterte `Registry.pol`-Datensaetze erzeugen falsche Instruktionen oder Ressourcenverbrauch. | PReg-Signatur und Version, Feldabschluesse, Schluessel und Wertnamen, Typen, Datengroessen, Datensatzanzahl und Gesamtgroesse vor Reduktion begrenzen; nur freigegebene Typen und REG_NONE als exakt leere Schluesselanlage akzeptieren, sonst das Artefakt ohne Salvage blockieren. Keine Datei ausfuehren und keinen externen Prozess starten. |
|
| Manipulierte oder herstellerspezifisch erweiterte `Registry.pol`-Datensaetze erzeugen falsche Instruktionen oder Ressourcenverbrauch. | PReg-Signatur und Version, Feldabschluesse, Schluessel und Wertnamen, Typen, Datengroessen, Datensatzanzahl und Gesamtgroesse vor Reduktion begrenzen; nur freigegebene Typen und REG_NONE als exakt leere Schluesselanlage akzeptieren, sonst das Artefakt ohne Salvage blockieren. Keine Datei ausfuehren und keinen externen Prozess starten. |
|
||||||
| GPP-`cpassword` oder andere Geheimnisse | Nur Vorhandensein und Locator erfassen; Wert bereits an der Parsergrenze maskieren, niemals entschlüsseln, loggen oder exportieren. |
|
| GPP-`cpassword` oder andere Geheimnisse | Nur Vorhandensein und Locator erfassen; Wert bereits an der Parsergrenze maskieren, niemals entschlüsseln, loggen oder exportieren. |
|
||||||
| Pfad-/Namensheuristik erzeugt falsche Policy- oder Zielidentität | Technische IDs ausschließlich aus unterstützten Artefaktinhalten und versionierten Regeln; Namen und Pfade bleiben Provenienz. |
|
| Manipulierter oder unbeabsichtigt ausgetauschter Policy-Regelkatalog | Einen extern kuratierten Katalog nur aus einer lokalen regulären Nicht-Symlink-Datei bis 5 MiB laden, wenn Pfad und erwarteter SHA-256 gemeinsam konfiguriert sind. Hash vor UTF-8-/JSON-Parsing prüfen, danach den vollständigen fachlichen Katalogvertrag fail-closed validieren und bei jeder Abweichung vor Repository- und Serverstart abbrechen. Keine automatische Netzwerkquelle oder stille Aktivierung. |
|
||||||
|
| Pfad-/Namensheuristik erzeugt falsche Policy- oder Zielidentität | Technische IDs ausschließlich aus unterstützten Artefaktinhalten und versionierten Regeln; Namen und Pfade bleiben Provenienz. Microsoft-Vorlagennamen werden nur auf ausdruecklichen Analyzer-Opt-in aus begrenzten XML-Feldern gelesen, nie als GPO-/Backup-/Policy-ID. Eine enge, versionierte Namensgrammatik darf den dokumentierten Vorlagenkontext beschreiben, aber nicht die Anwendung einer Kunden-GPO beweisen. Auswahl nur gegen genau einen expliziten Vergleichskontext; Inventarbeobachtungen, unbekannte Vorlagen und widerspruechliche Namen erzwingen keine Rollenwahl. |
|
||||||
| Historische Backups werden gemeinsam aggregiert | Explizite Backup-GUID, GPO-GUID, Domänenidentität, manifestgestützte Set-ID und Erhebungsfenster prüfen; doppelte GPOs, Zeitkonflikte und Domänenmischung blockieren. |
|
| Historische Backups werden gemeinsam aggregiert | Explizite Backup-GUID, GPO-GUID, Domänenidentität, manifestgestützte Set-ID und Erhebungsfenster prüfen; doppelte GPOs, Zeitkonflikte und Domänenmischung blockieren. |
|
||||||
| Ausgewählte Teilmenge wird als vollständige Umgebung bewertet | Setvollständigkeit getrennt von Instanzvollständigkeit modellieren; negative Schlussfolgerungen nur bei manifestgestütztem vollständigem Set mit erwarteter und beobachteter Anzahl. |
|
| Ausgewählte Teilmenge wird als vollständige Umgebung bewertet | Setvollständigkeit getrennt von Instanzvollständigkeit modellieren; negative Schlussfolgerungen nur bei manifestgestütztem vollständigem Set mit erwarteter und beobachteter Anzahl. |
|
||||||
| Backup wird mit effektiver Anwendung verwechselt | Ohne RSoP-/Group-Policy-Results-Evidenz niemals Scope oder Wirksamkeit bestätigen; Ergebnisse als konfigurierte, nicht als effektiv angewandte Abweichung qualifizieren. |
|
| Backup wird mit effektiver Anwendung verwechselt | Ohne RSoP-/Group-Policy-Results-Evidenz niemals Scope oder Wirksamkeit bestätigen; Ergebnisse als konfigurierte, nicht als effektiv angewandte Abweichung qualifizieren. |
|
||||||
|
|
@ -58,6 +76,9 @@ Ein unsicherer Pfad, eine Kollision, ein Link, eine Grenzverletzung, ein Hashfeh
|
||||||
|
|
||||||
## Verifizierte technische Kontrollen
|
## Verifizierte technische Kontrollen
|
||||||
|
|
||||||
|
- Nachtrag 2026-09-06: Metadaten-XML verwendet einen SAX-Parser ohne DOM, mit maximal 64 MiB je Datei, 500.000 Elementen, Tiefe 128 und begrenztem Identitaetsfeldpuffer. Das gemeinsame selektive Entpackbudget betraegt hoechstens 128 MiB; globale ZIP-, Datei-, Workerzeit- und Heapgrenzen bleiben erhalten. Die fruehere Anforderung an ein schliessbares Metadaten-JSDOM entfaellt; Preference-DOMs bleiben unveraendert. Synthetischer Lasttest: 2300 GPOs ueber ZIP und Ordnertransport, einschliesslich einer Metadatendatei groesser als 2 MiB.
|
||||||
|
- Nachtrag 2026-09-06: Baselinepakete lesen auch Security-Template- und Advanced-Audit-Artefakte durch dieselben begrenzten Parser/Reducer wie Kundenbackups. Native Abschnitt-/Schluesselidentitaeten ersetzen keine Registry- oder Targetingsemantik. Automatische Profilableitung bleibt auf gespeicherte Beobachtungen desselben Assessments begrenzt; unbekannte Plattformen und mehrere Snapshotgruppen erzeugen keine erfundene Freigabe. Bestehende Vollstaendigkeits-, Konflikt-, Risiko- und Finding-Grenzen gelten unveraendert.
|
||||||
|
|
||||||
- Der persistente GPO-Statusendpunkt prüft den Mandanten und gibt je GPO-Artefakt nur technischen Dateinamen, Größe, Queue-/Processingzustand, Versuche, Zeitpunkte, Fehlercode und Backupinstanzzahl aus. Die Mandantenansicht lädt diese Werte neu und pollt nur solange sie sichtbar ist; Speicherschlüssel und Rohinhalte werden nicht ausgegeben.
|
- Der persistente GPO-Statusendpunkt prüft den Mandanten und gibt je GPO-Artefakt nur technischen Dateinamen, Größe, Queue-/Processingzustand, Versuche, Zeitpunkte, Fehlercode und Backupinstanzzahl aus. Die Mandantenansicht lädt diese Werte neu und pollt nur solange sie sichtbar ist; Speicherschlüssel und Rohinhalte werden nicht ausgegeben.
|
||||||
- Die Artefakt-/Jobanlage ist in lokaler und PostgreSQL-Persistenz atomar. PostgreSQL-Rollback, unmittelbares Entfernen einer neuen Dateikopie bei Commitfehler, idempotente Reparatur eines Legacy-Artefakts ohne Job sowie alters- und mengenbegrenztes Entfernen eines synthetischen unreferenzierten Objekts sind verifiziert. Reconciliation startet mit dem Backend und läuft standardmäßig stündlich; unbekannte Namen, Links und junge Objekte werden nicht gelöscht; bei leerem vertrauenswürdigem Referenzbestand findet überhaupt keine Objektlöschung statt.
|
- Die Artefakt-/Jobanlage ist in lokaler und PostgreSQL-Persistenz atomar. PostgreSQL-Rollback, unmittelbares Entfernen einer neuen Dateikopie bei Commitfehler, idempotente Reparatur eines Legacy-Artefakts ohne Job sowie alters- und mengenbegrenztes Entfernen eines synthetischen unreferenzierten Objekts sind verifiziert. Reconciliation startet mit dem Backend und läuft standardmäßig stündlich; unbekannte Namen, Links und junge Objekte werden nicht gelöscht; bei leerem vertrauenswürdigem Referenzbestand findet überhaupt keine Objektlöschung statt.
|
||||||
|
|
||||||
|
|
@ -71,6 +92,7 @@ Ein unsicherer Pfad, eine Kollision, ein Link, eine Grenzverletzung, ein Hashfeh
|
||||||
- Links, Spezialdateien, verschachtelte ZIPs, verschlüsselte oder mehrteilige Archive, ZIP64 sowie nicht eindeutig UTF-8-kodierte Nicht-ASCII-Pfade blockieren das gesamte Inventar.
|
- Links, Spezialdateien, verschachtelte ZIPs, verschlüsselte oder mehrteilige Archive, ZIP64 sowie nicht eindeutig UTF-8-kodierte Nicht-ASCII-Pfade blockieren das gesamte Inventar.
|
||||||
- Der GPO-Metadatenparser läuft im begrenzten Worker, akzeptiert nur begrenztes UTF-8 beziehungsweise BOM-markiertes UTF-16, lehnt DTD, Entitäten, externe Referenzen, XInclude, Schemas und fremde Processing Instructions ab und übernimmt technische IDs nur konfliktfrei aus Inhalt. `cpassword` wird nur als Indikator erkannt; der Wert verlässt die Parsergrenze nicht.
|
- Der GPO-Metadatenparser läuft im begrenzten Worker, akzeptiert nur begrenztes UTF-8 beziehungsweise BOM-markiertes UTF-16, lehnt DTD, Entitäten, externe Referenzen, XInclude, Schemas und fremde Processing Instructions ab und übernimmt technische IDs nur konfliktfrei aus Inhalt. `cpassword` wird nur als Indikator erkannt; der Wert verlässt die Parsergrenze nicht.
|
||||||
- Der Security-Template-Parser läuft in derselben Ressourcenbegrenzung und erzwingt Datei-, Zeichen-, Zeilen- und INF-Feldgrenzen. Unterstützte Abschnitte werden semantisch getrennt reduziert; unterschiedliche Dubletten bleiben Konflikte, unbekannte Abschnitte `partial`, ungültige Registry-Instruktionen blockiert und effektive Anwendung bleibt ohne RSoP immer `unknown`. Rohwerte werden nicht über die bestehende Backend-/Projektgrenze veröffentlicht.
|
- Der Security-Template-Parser läuft in derselben Ressourcenbegrenzung und erzwingt Datei-, Zeichen-, Zeilen- und INF-Feldgrenzen. Unterstützte Abschnitte werden semantisch getrennt reduziert; unterschiedliche Dubletten bleiben Konflikte, unbekannte Abschnitte `partial`, ungültige Registry-Instruktionen blockiert und effektive Anwendung bleibt ohne RSoP immer `unknown`. Rohwerte werden nicht über die bestehende Backend-/Projektgrenze veröffentlicht.
|
||||||
|
- Präzisierung 2026-09-08 (Security-Template-Parser 1.1.0): Metadaten und unbekannte Abschnitte werden vor der Key/Value-Validierung übersprungen, da beispielsweise Service-Einträge eine eigene CSV-artige Grammatik haben. Isolierte defekte Einträge in unterstützten Abschnitten werden mit Diagnose verworfen; gültige Instruktionen behalten Originalwert und Quellzeile bei Dokumentstatus `partial`. Dies ersetzt ausschließlich die bisherige artefaktweite Verwerfung wegen einzelner defekter INF-Einträge. Beschädigte Abschnittsgrenzen, Einträge ohne Abschnitt und Ressourcenverletzungen bleiben blockiert; Dekodierungsfehler werden weiterhin abgewiesen. `partial`/`inventory-only` bleiben in Baselineanalyse und Kundeninventar sichtbar und erlauben keine Vollständigkeitsbehauptung. Historische Analysen bleiben erhalten; Baseline-Analyzer 1.2.0 und GPO-Processing 2.2.3 erzeugen versionierte Nachfolger.
|
||||||
- Der Advanced-Audit-Parser verwendet den allgemeinen CSV-Pfad nur zur begrenzten lexikalischen Dekodierung mit physischen Quellzeilen. Er verlangt die technischen Spalten `Policy Target`, `Subcategory GUID` und `Setting Value`, akzeptiert nur kanonische Subcategory-GUIDs sowie numerische Flags 0 bis 3 und ignoriert lokalisierte Anzeigenamen. Nur Systemziele werden reduziert; benutzerspezifische Ziele bleiben `partial`. Abweichende GUID-Dubletten bleiben Konflikte, ungültige unterstützte Zeilen blockieren ohne Salvage und `effectiveState` bleibt ohne RSoP `unknown`. Advanced und Legacy Audit werden nicht zusammengeführt.
|
- Der Advanced-Audit-Parser verwendet den allgemeinen CSV-Pfad nur zur begrenzten lexikalischen Dekodierung mit physischen Quellzeilen. Er verlangt die technischen Spalten `Policy Target`, `Subcategory GUID` und `Setting Value`, akzeptiert nur kanonische Subcategory-GUIDs sowie numerische Flags 0 bis 3 und ignoriert lokalisierte Anzeigenamen. Nur Systemziele werden reduziert; benutzerspezifische Ziele bleiben `partial`. Abweichende GUID-Dubletten bleiben Konflikte, ungültige unterstützte Zeilen blockieren ohne Salvage und `effectiveState` bleibt ohne RSoP `unknown`. Advanced und Legacy Audit werden nicht zusammengeführt.
|
||||||
- Der `Registry.pol`-Parser laeuft im begrenzten GPO-Worker und akzeptiert ausschliesslich PReg Version 1 mit festen Datei-, Datensatz- und Datenfeldgrenzen. Er dekodiert freigegebene Registrytypen typisiert, erhaelt Instruktionsreihenfolge und Byteoffset-Provenienz und behandelt REG_NONE nur als leere Schluesselanlage. Delete-, DeleteValues-, DeleteKeys-, SecureKey- und Soft-Operationen bleiben getrennt; unbekannte Typen und fehlerhafte unterstuetzte Strukturen blockieren ohne Salvage, unbekannte reservierte Operationen bleiben als nicht unterstuetzt sichtbar. Weder Shell noch LGPO.exe noch Netzwerk werden verwendet.
|
- Der `Registry.pol`-Parser laeuft im begrenzten GPO-Worker und akzeptiert ausschliesslich PReg Version 1 mit festen Datei-, Datensatz- und Datenfeldgrenzen. Er dekodiert freigegebene Registrytypen typisiert, erhaelt Instruktionsreihenfolge und Byteoffset-Provenienz und behandelt REG_NONE nur als leere Schluesselanlage. Delete-, DeleteValues-, DeleteKeys-, SecureKey- und Soft-Operationen bleiben getrennt; unbekannte Typen und fehlerhafte unterstuetzte Strukturen blockieren ohne Salvage, unbekannte reservierte Operationen bleiben als nicht unterstuetzt sichtbar. Weder Shell noch LGPO.exe noch Netzwerk werden verwendet.
|
||||||
- Der Group-Policy-Preferences-Parser läuft in derselben Ressourcenbegrenzung, akzeptiert nur begrenztes UTF-8 beziehungsweise BOM-markiertes UTF-16 und lehnt DTD, Entitäten, externe Referenzen, Schemas sowie fremde Processing Instructions ab. Registry Preferences benötigen die dokumentierten technischen CLSIDs, kanonische Item-UIDs, gültige Hives, Ziele, Werttypen, Aktionen und boolesche Common Options. Verarbeitungsreihenfolge, Disabled, Remove-on-unapply und Apply-once bleiben erhalten. Item-Level-Targeting wird strukturell validiert, aber ohne Umgebungsevidenz nur durch Filtertypen und Ausdruckshash als nicht auswertbar gebunden. Unbekannte CSEs beziehungsweise Registry-SubProperties bleiben `partial`; fehlerhafte unterstützte Strukturen blockieren ohne Salvage. `cpassword` wird nur als Indikator erkannt, nie entschlüsselt oder in Dokument, Beobachtung, Diagnose beziehungsweise Export übernommen.
|
- Der Group-Policy-Preferences-Parser läuft in derselben Ressourcenbegrenzung, akzeptiert nur begrenztes UTF-8 beziehungsweise BOM-markiertes UTF-16 und lehnt DTD, Entitäten, externe Referenzen, Schemas sowie fremde Processing Instructions ab. Registry Preferences benötigen die dokumentierten technischen CLSIDs, kanonische Item-UIDs, gültige Hives, Ziele, Werttypen, Aktionen und boolesche Common Options. Verarbeitungsreihenfolge, Disabled, Remove-on-unapply und Apply-once bleiben erhalten. Item-Level-Targeting wird strukturell validiert, aber ohne Umgebungsevidenz nur durch Filtertypen und Ausdruckshash als nicht auswertbar gebunden. Unbekannte CSEs beziehungsweise Registry-SubProperties bleiben `partial`; fehlerhafte unterstützte Strukturen blockieren ohne Salvage. `cpassword` wird nur als Indikator erkannt, nie entschlüsselt oder in Dokument, Beobachtung, Diagnose beziehungsweise Export übernommen.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Navigation performance
|
||||||
|
|
||||||
|
This is the Phase 7 measurement and regression procedure for area switches.
|
||||||
|
|
||||||
|
## Local measurement
|
||||||
|
|
||||||
|
1. Start the existing local app and backend using the canonical PostgreSQL datastore. Do not create a performance-test database.
|
||||||
|
2. Use synthetic fixtures or an explicitly authorized dummy workspace. Open the development app at `http://127.0.0.1:5173/?measureNavigation=1` and load that workspace. Ordinary app URLs do not enable instrumentation; production builds exclude it.
|
||||||
|
3. Wait for the workspace to finish loading. Keep viewport, data, filter settings and development/production mode identical between comparisons. Stop concurrent builds and load tests before recording timings.
|
||||||
|
4. Click each main navigation area. Read `#assessor-navigation-performance` after the next three animation frames, waiting for `sequence` to advance. It exposes only `area`, `sequence`, `paintMs`, `longestFrameMs` and named calculation durations in `stepsMs`. The interval starts in the click capture handler. This measures click-to-render responsiveness, not the completion of backend requests. Check asynchronous readiness separately using control availability and row counts. Initial data loading must not overlap a navigation measurement.
|
||||||
|
5. Measure a first visit and at least two repeat visits. Test Work before and after opening Pathfinder, and test the return from Datahub and Result DB. Confirm that filter changes made while the graph is hidden are applied on reactivation and that a workspace change resets retained state.
|
||||||
|
6. Close temporary browser tabs after testing. Never copy customer content into timing reports.
|
||||||
|
|
||||||
|
## Known causes and controls
|
||||||
|
|
||||||
|
| Cause found on 2026-09-09 | Control |
|
||||||
|
| --- | --- |
|
||||||
|
| Filter bar, sidebar and viewer independently repeated equivalent queries after remounts. | Shared bounded sheet selectors; reference/semantic keys include every input that changes the result. |
|
||||||
|
| Status matching rescanned source fields for each consumer and facet. | Profile-owned weak record caches; keep the existing matching semantics and profile separation. |
|
||||||
|
| Rejected numbered column groups reread all siblings for every column. | Reuse the group values, skip ineligible text columns early, stop eligibility checks beyond the category limit. |
|
||||||
|
| Sort comparators normalized both records on every comparison. | Normalize once per record before sorting; preserve tie-breaking and null ordering. |
|
||||||
|
| Work serialized every filtered finding into Raw Data text while displaying other modes. | Mount and memoize the complete text projection only in Raw Data. Derive titles only for rendered records. |
|
||||||
|
| Virtualization initially mounted 40 rows before measuring the viewport. | Bound the first window by screen height plus overscan; use stable callback refs and reset cancelled frame handles. |
|
||||||
|
| Work mounted 2,537 native sheet options on every visit to the authorized dummy. | Keep the selected option mounted; synchronously prepare all options before pointer opening or keyboard focus. Collapse on blur without changing selection. |
|
||||||
|
| Export buttons scanned the workspace and constructed export rows during rendering. | Determine availability with a short-circuit check; construct the payload on the export action. |
|
||||||
|
| Hidden Pathfinder built its graph, consumed context updates and measured hidden geometry. | First-activation mount, retained input boundary, scope reset, memoized component, paused hidden observers/animations. |
|
||||||
|
| Graph text collision layout ran with text switched off; identical MITRE headers were repeatedly classified. | Gate text layout by display mode and reuse header classifications within each graph derivation. |
|
||||||
|
| An unchanged periodic health response replaced the global app state. | Preserve state identity unless the health status changes. |
|
||||||
|
| Repeated identical geometry observations replaced graph/dashboard state. | Compare dimensions and connector paths before setting state. |
|
||||||
|
|
||||||
|
## Regression checks
|
||||||
|
|
||||||
|
`tests/features/navigation-performance.test.ts` exercises 8,257 synthetic records with repeated numbered columns. The unchanged repeat visit must read zero source fields. Smaller cases verify review, status, severity, search, path, profile, dataset and sheet invalidation, source preservation and bounded cache eviction.
|
||||||
|
|
||||||
|
`tests/features/navigation-rendering.test.tsx` verifies retained-view suspension/resumption, raw-text reuse and the first virtual window. `tests/features/dataset-viewer-cost.test.tsx` mounts the actual viewer: Matrix must not read an offscreen finding's fields, while Raw Data must include its complete text. `tests/features/sheet-selection-cost.test.tsx` verifies 2,537 synthetic choices, pointer/keyboard preparation and preservation of the selected sheet after blur.
|
||||||
|
|
||||||
|
Run the focused tests while iterating, then `npm test` and `npm run build`. Existing facet, severity, GPO, result-window and graph tests protect the underlying behavior. Timing output is diagnostic; CI uses deterministic behavior and operation counts rather than machine-dependent millisecond limits.
|
||||||
|
|
||||||
|
## Observations on 2026-09-09
|
||||||
|
|
||||||
|
Development browser, authorized dummy, 1280 × 720 viewport: the initial observed Work switch took 5,324 ms. After shared calculations, lazy raw text, bounded first rendering and deferred sheet options, unchanged Work revisits measured 121–211 ms, including two consecutive measurements of 136/138 ms. One return immediately after a filter reset and graph rebuild took 406 ms. These are individual observations, not a hardware-independent latency guarantee.
|
||||||
|
|
||||||
|
The final cross-area sweep measured 34 ms for Result DB, 44 ms for DB Structure and 109 ms for Datahub until the third frame; their asynchronous data requests are not included. Client Info measured 353 ms on a warm visit, while the first visit after filter reset took 991 ms. Pathfinder still performs a necessary first build or rebuild after data/filter changes (1,005 ms after the reset in this sweep); retained unchanged visits avoid rebuilding its data. Continue measuring these separate cold costs when changing either area.
|
||||||
|
|
||||||
|
The synthetic 8,257-record benchmark initially measured 5,360 ms cold / 398 ms repeated. Focused post-change runs measured 623–978 ms cold / 1 ms repeated; concurrent full-suite load raised cold times, so those runs are not directly comparable. The reliable regression invariant is zero source-field reads on an unchanged repeat visit. No tenant data, source artifacts or database schema were changed for these measurements.
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# GPO comparison contract
|
||||||
|
|
||||||
|
## Microsoft evidence reviewed on 2026-09-06
|
||||||
|
|
||||||
|
- [Microsoft Security Compliance Toolkit](https://www.microsoft.com/en-us/download/details.aspx?id=55319): official PolicyAnalyzer.zip, bundled **Policy Analyzer.pdf**, version 4.0, pages 1 and 6. Read directly from the official ZIP in memory; no executables run.
|
||||||
|
- [Policy Analyzer introduction](https://techcommunity.microsoft.com/blog/microsoft-security-baselines/new-tool-policy-analyzer/701049).
|
||||||
|
- [ControlPolicyConflict / MDMWinsOverGP](https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-controlpolicyconflict).
|
||||||
|
- [Windows Hello for Business policy conflicts](https://learn.microsoft.com/en-us/windows/security/identity-protection/hello-for-business/configure#policy-conflicts-from-multiple-policy-sources).
|
||||||
|
|
||||||
|
Policy Analyzer distinguishes differing configured values from absent settings. Repeated definitions of the same setting are indicated separately. The comparison retains registry data types: a numeric 5 and a text "5" differ. It can compare policy sets and local effective state; the latter is a separate operation.
|
||||||
|
|
||||||
|
## User-facing outcomes
|
||||||
|
|
||||||
|
| Outcome | Evidence required |
|
||||||
|
| --- | --- |
|
||||||
|
| Fail / Misconfigured | Explicit Microsoft recommendation and a persisted deterministic mismatch. A missing required key or typed empty value is a mismatch; ordinary missing value recommendations remain Not configured. |
|
||||||
|
| Not configured | Explicit nonempty Microsoft recommendation and a missing customer setting; informational, excluded from Fail and Deviation. |
|
||||||
|
| Pass | Explicit Microsoft recommendation and a persisted deterministic match. For Registry.pol deletion requirements, target absence in the analyzed backup set is an explicit matching state. |
|
||||||
|
| Conflicts | Multiple distinct typed customer values for the same setting, or explicitly recorded contradictory recommendations within one baseline set. |
|
||||||
|
| Duplicates | Multiple source entries configuring the same setting to the same typed value. |
|
||||||
|
|
||||||
|
Registry addresses include computer/user scope (implicit HKLM/HKCU in Registry.pol), key and value name. Address matching ignores case; value data retains case and type. Different value names under the same key are distinct settings. Native security-template and advanced-audit settings use their resolved identities. Assessments and projects are never merged for collision detection.
|
||||||
|
|
||||||
|
Registry.pol `delete-value`, `delete-all-values` and `create-key` are compared as typed target states rather than discarded as missing values. A typed empty string remains a present value. These rules describe only the imported backup configuration; local effective state remains a separate Policy Analyzer operation and requires separate evidence.
|
||||||
|
|
||||||
|
Microsoft also calls configured differences *between* policy sets conflicts. ASSESSOR labels configured customer-versus-benchmark mismatches Fail. The user's 2026-09-06 correction replaces the earlier four-outcome contract: missing settings remain a separate informational outcome. GPO Deviation includes only Misconfigured, not Conflicts, Duplicates, missing values or review-only records. Other scanner deviation semantics remain unchanged. A conflict in the imported set is not a claim that both settings apply to the same endpoint: GPO backup analysis does not establish resultant policy.
|
||||||
|
|
||||||
|
One baseline comparison creates one Work row with all source references. One customer collision group creates one Conflicts/Duplicates row. A duplicate group may also have a separate Pass/Fail benchmark result; these are different questions. Multiple benchmarks do not themselves create duplicate customer settings.
|
||||||
|
|
||||||
|
Unsupported, unbound, not-evaluated, and ordinary review-only observations do not create Work rows. Diagnostics and original evidence remain available separately. Existing persisted data is not deleted or reclassified by this presentation change.
|
||||||
|
|
||||||
|
## Hybrid AD / Intune
|
||||||
|
|
||||||
|
A GPO and an Intune policy can prescribe conflicting values for an equivalent setting. This requires both policy sources, device/user assignments, an explicit CSP-to-GPO mapping, and applicable precedence rules. Matching raw registry strings is insufficient because equivalent GPO and CSP settings may use different locations.
|
||||||
|
|
||||||
|
MDMWinsOverGP is device-scoped, defaults to 0, and governs Policy CSP equivalents only. It is not a universal MDM priority rule. Microsoft documents separate behavior for PassportForWork/Windows Hello for Business, where mixed GPO/CSP configuration can prevent CSP settings from applying. MDM diagnostics can report GP settings blocked by an MDM equivalent.
|
||||||
|
|
||||||
|
The current importer does not provide Intune policy settings and assignment evidence. Hybrid status from another report alone cannot establish such a conflict. Hybrid conflict evaluation therefore remains an explicit future input/mapping task; no synthetic Intune conflict is inferred from GPO backups.
|
||||||
|
|
||||||
|
## Verification and remaining parity work
|
||||||
|
|
||||||
|
Synthetic regression coverage includes absent/empty baselines, missing customer values, typed collisions, address case, different value names, scope and timeslot isolation, baseline separation, duplicate read rows, source preservation, and a 2300-GPO group. Work and the analysis summary use the same builder.
|
||||||
|
|
||||||
|
This is not a claim of complete executable parity with Policy Analyzer. A differential run of the same synthetic packages through Policy Analyzer and ASSESSOR remains required, including cross-format Registry.pol/security-template equivalence, ordered delete instructions, supported type families and translated identifiers. Conditional Group Policy Preferences and resultant endpoint policy require additional semantics/evidence and stay excluded from automatic claims.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Public AI search
|
||||||
|
|
||||||
|
Presentation-only, Phase 7. The shared FindingReader exposes AI search for every tool, including mixed-tool workspaces. An explicit click opens Google AI Mode with opener/referrer protection. No background lookup, runtime catalog download, automatic AI evaluation or report upload is introduced.
|
||||||
|
|
||||||
|
## Public evidence boundary
|
||||||
|
|
||||||
|
`src/profiles/public-search/catalog.json` is a public-identifier snapshot reviewed on 2026-09-06: 67 registry targets, 45 privilege constants, 19 ASR identifiers, 161 Maester IDs and 51 PingCastle IDs. Source URLs accompany the identifiers. It is not a complete or automatically updated vendor catalog. It contains identifiers, not copied vendor descriptions or customer report prose. Noncanonical source notations (query-style value names or ambiguous path separators) are excluded rather than guessed.
|
||||||
|
|
||||||
|
- Registry matches require the complete canonical key plus value name; only the catalog-owned spelling is exported. Private extensions do not match.
|
||||||
|
- Native sections and keys resolve to public security-policy identities. ASR identifiers also require the Defender ASR registry family.
|
||||||
|
- Scanner IDs must exist in the tool-specific snapshot. Dynamic instance suffixes are discarded.
|
||||||
|
- CIS-CAT, Purple Knight, generic and unresolved rules use locally recognized fixed technical concepts. Only fixed vocabulary is sent, not the input excerpt. Registry lookup is tool-independent.
|
||||||
|
- Public product families and exact scope/status enums add context without proving OS/role applicability. Existing exact GPO baseline/audit context remains available.
|
||||||
|
- An identified numeric/boolean setting may include a bounded selected benchmark target (integer 0–65535 or boolean). It is labeled as selected, not independently verified as a Microsoft recommendation. Assigned accounts, SID lists, strings, arbitrary JSON and all customer actual values are excluded.
|
||||||
|
- Imported descriptions, remediation, locators, identities, URLs, owner information and free-form titles are never passed through. Imported ADML provenance alone does not establish that arbitrary prose is public.
|
||||||
|
|
||||||
|
The search requests the exact control, purpose, risk, configuration steps, verification and direct primary-source links, in German. Azure registry identity references do not replace the selected SCT benchmark or determine its expected value. AI output never changes findings or grading.
|
||||||
|
|
||||||
|
## Incomplete evidence and maintenance
|
||||||
|
|
||||||
|
The link exposes its specificity (`setting`, `rule`, `topic`) and full query in its tooltip. Unresolved cases explicitly request topic discussion rather than an invented rule or recommendation. No promise of complete coverage, vendor-verbatim guidance, anonymity or GDPR compliance is made. Google handles connection/IP and potentially account/cookie data; organizational approval remains necessary.
|
||||||
|
|
||||||
|
Extend the catalog only from independently reviewed public publisher sources, with attribution and synthetic tests. Never learn public vocabulary automatically from customer reports. Tests cover all tool links, mixed workspaces, private suffixes, complete path matching, numeric bounds, ambiguous fields and catalog integrity. Tests do not query Google or inspect real tenant content.
|
||||||
|
|
||||||
|
GPO badges use existing section/type and registry family evidence. System Access, Privilege Rights, Registry Values and related sections are display categories, not new outcomes. Registry/Software/System are structural fallbacks; specific product/security families take precedence. Description text does not assign badges.
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# Synthetic source fixtures
|
# Synthetic source fixtures
|
||||||
|
|
||||||
|
These inputs are now registered in `tests/importers/import-contracts.json` and checksum-protected by the mandatory import compatibility gate. See `docs/IMPORT_COMPATIBILITY.md`. Keep existing inputs and expected behavior; extend with new synthetic cases instead of replacing a working format. Customer reports are not permitted as compatibility baselines.
|
||||||
|
|
||||||
All files below are deliberately small, synthetic, and free of customer or company data. They are regression inputs for the generic import pipeline, not complete vendor golden files. Source-specific recognition and semantic extraction remain part of Phase 6.
|
All files below are deliberately small, synthetic, and free of customer or company data. They are regression inputs for the generic import pipeline, not complete vendor golden files. Source-specific recognition and semantic extraction remain part of Phase 6.
|
||||||
|
|
||||||
## PingCastle
|
## PingCastle
|
||||||
|
|
@ -30,4 +32,16 @@ All files below are deliberately small, synthetic, and free of customer or compa
|
||||||
- https://github.com/maester365/maester/blob/main/powershell/public/core/Convert-MtResultsToFlatObject.ps1
|
- https://github.com/maester365/maester/blob/main/powershell/public/core/Convert-MtResultsToFlatObject.ps1
|
||||||
- https://maester.dev/docs/commands/Invoke-Maester/
|
- https://maester.dev/docs/commands/Invoke-Maester/
|
||||||
|
|
||||||
When an anonymized real export can legally be added, it should supplement these structural fixtures and must be reviewed for tenant names, domains, accounts, identifiers, evidence, comments, and embedded metadata first.
|
- File: `maester/test-results.synthetic.html`
|
||||||
|
- Models the inert JSON object embedded by the official single-file Maester HTML report generator; it contains synthetic test values only.
|
||||||
|
- Sources:
|
||||||
|
- https://github.com/maester365/maester/blob/main/powershell/public/Get-MtHtmlReport.ps1
|
||||||
|
- https://github.com/maester365/maester/blob/main/powershell/public/core/ConvertTo-MtMaesterResult.ps1
|
||||||
|
- https://maester.dev/docs/commands/Get-MtHtmlReport/
|
||||||
|
|
||||||
|
- File: `purple-knight/security-assessment.synthetic.html`
|
||||||
|
- Models the script-embedded `window.reportJSON` and category indicator assignments observed in a locally supplied PurpleKnight dummy report. No source values were copied into this fixture.
|
||||||
|
|
||||||
|
`maester/application-json-binding.synthetic.html` protects the current static declaration in an application/json script. `maester/multi-tenant.synthetic.html` protects separate native tenant paths. Both contain only invented Dummy values.
|
||||||
|
|
||||||
|
Compatibility references use synthetic data only, including when a real report reveals a new structure.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,89 @@
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Kopfangaben, Bewertungsanzeige und Abschnittsuebersicht eines
|
||||||
|
CIS-CAT-Pro-Berichts.
|
||||||
|
|
||||||
|
Die Zahlen des Summary-Blocks entsprechen genau den beiden
|
||||||
|
Ergebniszeilen dieser Datei (1 Pass, 1 Fail), damit die bestehende
|
||||||
|
Gegenprobe des Imports unveraendert aufgeht. Die Spaltennamen der
|
||||||
|
Abschnittsuebersicht sind woertlich uebernommen ("Unkn.", "Man.",
|
||||||
|
"Exc."), weil der Import genau an ihnen erkennt, welche Tabelle den
|
||||||
|
Punktwert je Benchmarkabschnitt traegt. Die Systemangaben stehen als
|
||||||
|
beschriftete Wertepaare, nicht an einer festen Stelle im Aufbau.
|
||||||
|
-->
|
||||||
|
<!--
|
||||||
|
Titelseite eines CIS-CAT-Pro-Berichts, im Aufbau uebernommen: das
|
||||||
|
bewertete System steht als "for <Host>" in der zweiten Ueberschrift, die
|
||||||
|
Netzadresse als "Target IP Address" im Listenpunkt darunter, und Profil,
|
||||||
|
Zeitpunkt sowie Dauer stehen als unbeschriftete Listenpunkte unter der
|
||||||
|
Benchmarkzeile. Beschriftete Wertepaare gibt es hier nicht.
|
||||||
|
-->
|
||||||
|
<div id="coverPageTitle">
|
||||||
|
<h1>Security Configuration Assessment Report</h1>
|
||||||
|
<h1>for SYNTHETIC-HOST-01</h1>
|
||||||
|
<ul>
|
||||||
|
<li>Target IP Address: 203.0.113.10</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id="coverPageSubTitle">
|
||||||
|
<h2>Synthetic CIS Benchmark v2.0.0</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Level 1 - Synthetic Member Server</li>
|
||||||
|
<li>Friday, July 17 2026 08:00:00</li>
|
||||||
|
<li> Assessment Duration: 1 minutes, 5 seconds</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="introFooter">
|
||||||
|
<p>Report generated by the Center for Internet Security's Configuration Assessment Tool (CIS-CAT Pro Assessor) v4.61.0.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section id="summary-metrics">
|
||||||
|
<table class="summary-full">
|
||||||
|
<tr>
|
||||||
|
<th colspan="6">
|
||||||
|
<div class="metric-value">1<span class="total-metric-value"> / 2</span></div>
|
||||||
|
<div class="summary-label">Passed Recommendations</div>
|
||||||
|
</th>
|
||||||
|
<td rowspan="2" class="summary-score"><div class="gauge" data-score="50"></div></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><div class="metric-value pass">1</div><div class="metric-label">Pass</div></td>
|
||||||
|
<td><div class="metric-value fail">1</div><div class="metric-label">Fail</div></td>
|
||||||
|
<td><div class="metric-value error">0</div><div class="metric-label">Error</div></td>
|
||||||
|
<td><div class="metric-value unknown">0</div><div class="metric-label">Unknown</div></td>
|
||||||
|
<td><div class="metric-value manual">0</div><div class="metric-label">Manual</div></td>
|
||||||
|
<td><div class="metric-value excepted">0</div><div class="metric-label">Excepted</div></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="sectionScores">
|
||||||
|
<table class="summary-sections">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Description</th><th>Pass</th><th>Fail</th><th>Error</th><th>Unkn.</th>
|
||||||
|
<th>Man.</th><th>Exc.</th><th>Score</th><th>Max</th><th>Percent</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1 Account Policies</td><td>1</td><td>1</td><td>0</td><td>0</td>
|
||||||
|
<td>0</td><td>0</td><td>1.0</td><td>2.0</td><td>50%</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>1.1 Password Policy</td><td>1</td><td>0</td><td>0</td><td>0</td>
|
||||||
|
<td>0</td><td>0</td><td>1.0</td><td>1.0</td><td>100%</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2 Local Policies</td><td>0</td><td>0</td><td>0</td><td>0</td>
|
||||||
|
<td>0</td><td>0</td><td>0.0</td><td>0.0</td><td>0%</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="profiles">
|
<section id="profiles">
|
||||||
<h1>Profiles</h1>
|
<h1>Profiles</h1>
|
||||||
<table class="profile">
|
<table class="profile">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head><meta charset="utf-8"><title>Maester · Dummy · Synthetic import regression</title></head>
|
||||||
|
<body><div id="root">Synthetic Dummy data only. No customer data.</div>
|
||||||
|
<!-- Matches the public Maester ReportTemplate.html data binding; every value below is synthetic. -->
|
||||||
|
<script id="maester-report-data" type="application/json">const testResults={"Result":"Failed","TenantId":"dummy-tenant","TenantName":"Dummy","TotalCount":2,"FailedCount":1,"PassedCount":1,"Tests":[{"Id":"MT.99001","Title":"Dummy failed check","Result":"Failed","Severity":"High","ResultDetail":{"TestResult":"Synthetic evidence only"}},{"Id":"MT.99002","Title":"Dummy passed check","Result":"Passed"}],"EndOfJson":"EndOfJson"}</script>
|
||||||
|
</body></html>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<!doctype html><html><head><title>Maester Dummy multi-tenant report</title></head><body>
|
||||||
|
<p>Synthetic regression fixture only. No customer data.</p>
|
||||||
|
<script>const data={"Tenants":[
|
||||||
|
{"TenantId":"dummy-one","Result":"Passed","Tests":[{"Id":"MT.9101","Title":"Synthetic tenant one","Result":"Passed","Severity":"Low"}],"EndOfJson":true},
|
||||||
|
{"TenantId":"dummy-two","Result":"Failed","Tests":[{"Id":"MT.9102","Title":"Synthetic tenant two","Result":"Failed","Severity":"High"}],"EndOfJson":true}
|
||||||
|
],"EndOfJson":true};</script></body></html>
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
<!doctype html><html><head><title>Maester Security Tests</title></head><body>
|
||||||
|
<div id="root"></div><script>
|
||||||
|
const unrelated = "This is not report data", testResults = {
|
||||||
|
Result: "Failed", TotalCount: 5, FailedCount: 0, PassedCount: 1, InvestigateCount: 1,
|
||||||
|
ErrorCount: 1, SkippedCount: 1, NotRunCount: 1,
|
||||||
|
TenantId: "synthetic-tenant-id", TenantName: "Synthetic tenant",
|
||||||
|
ExecutedAt: "2026-09-09T08:00:00Z", CurrentVersion: "1.0.0", TotalDuration: "00:00:10",
|
||||||
|
SystemInfo: { OS: "Synthetic OS", Available: !0, Count: 0, Nullable: null },
|
||||||
|
PowerShellInfo: { Version: "7.4" }, LoadedModules: [], FutureMetadata: { nested: ['retained'] },
|
||||||
|
Tests: [
|
||||||
|
{ Id: "MT.9901", Title: "Synthetic failed test", Result: "Failed", Severity: "High",
|
||||||
|
Tag: ["Synthetic", "Identity"], Block: "Synthetic block", Duration: "00:00:01",
|
||||||
|
ResultDetail: { TestDescription: "<p>Synthetic description</p>", TestResult: "<p>Synthetic evidence</p>",
|
||||||
|
Severity: "High", FutureDetail: { retained: !0 }, TestSkipped: !1 },
|
||||||
|
ScriptBlock: `Write-Output 'inert text'
|
||||||
|
@{ Key = "value" }`, HelpUrl: "https://maester.dev/docs/tests/MT.9901", FutureField: 0 },
|
||||||
|
{ Id: "MT.9902", Title: "Synthetic investigate test", Result: "Investigate", Severity: "Medium", ResultDetail: { TestResult: "Manual review" } },
|
||||||
|
{ Id: "MT.9903", Title: "Synthetic passed test", Result: "Passed", Severity: "Low", ResultDetail: { TestResult: "Synthetic success" } },
|
||||||
|
{ Id: "MT.9904", Title: "Synthetic skipped test", Result: "Skipped", Severity: "Info", ResultDetail: { TestSkipped: !0, SkippedReason: "Synthetic missing permission" } },
|
||||||
|
{ Id: "MT.9905", Title: "Synthetic not-run test", Result: "NotRun", ResultDetail: { SkippedReason: "Synthetic disabled test" } },
|
||||||
|
{ Id: "MT.9906", Title: "Synthetic error test", Result: "Error", ErrorRecord: [{ Message: "Synthetic execution error" }] },
|
||||||
|
], Blocks: [{ Name: "Synthetic block" }], EndOfJson: "EndOfJson",
|
||||||
|
};
|
||||||
|
globalThis.__maesterActiveContentExecuted = true;
|
||||||
|
</script></body></html>
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head><meta charset="utf-8"><title>Maester synthetic report</title></head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script>
|
||||||
|
var syntheticMaesterResults = {
|
||||||
|
"Result": "Failed",
|
||||||
|
"FailedCount": 1,
|
||||||
|
"PassedCount": 1,
|
||||||
|
"TotalCount": 2,
|
||||||
|
"Tests": [
|
||||||
|
{
|
||||||
|
"Index": 1,
|
||||||
|
"Id": "MT.9001",
|
||||||
|
"Title": "Ensure the synthetic policy is configured",
|
||||||
|
"Name": "MT.9001: Ensure the synthetic policy is configured",
|
||||||
|
"HelpUrl": "https://maester.dev/docs/tests/MT.9001",
|
||||||
|
"Severity": "High",
|
||||||
|
"Tag": ["Synthetic", "Entra"],
|
||||||
|
"Result": "Failed",
|
||||||
|
"ScriptBlock": "Describe 'synthetic test' { It 'is deterministic' { $true | Should -BeTrue } }",
|
||||||
|
"ScriptBlockFile": "MT.9001.Tests.ps1",
|
||||||
|
"ErrorRecord": null,
|
||||||
|
"Block": "Maester Security Tests",
|
||||||
|
"Duration": "00:00:01",
|
||||||
|
"ResultDetail": {
|
||||||
|
"TestTitle": "Synthetic policy",
|
||||||
|
"TestDescription": "Checks a synthetic policy without tenant data.",
|
||||||
|
"TestResult": "The synthetic policy needs review.",
|
||||||
|
"TestSkipped": false,
|
||||||
|
"SkippedReason": "",
|
||||||
|
"TestInvestigate": false,
|
||||||
|
"Severity": "High",
|
||||||
|
"Service": "Entra"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Index": 2,
|
||||||
|
"Id": "MT.9002",
|
||||||
|
"Title": "Ensure the synthetic control is enabled",
|
||||||
|
"Name": "MT.9002: Ensure the synthetic control is enabled",
|
||||||
|
"HelpUrl": "https://maester.dev/docs/tests/MT.9002",
|
||||||
|
"Severity": "Medium",
|
||||||
|
"Tag": ["Synthetic"],
|
||||||
|
"Result": "Passed",
|
||||||
|
"ScriptBlockFile": "MT.9002.Tests.ps1",
|
||||||
|
"Block": "Maester Security Tests",
|
||||||
|
"Duration": "00:00:01",
|
||||||
|
"ResultDetail": {
|
||||||
|
"TestDescription": "Checks a second synthetic control.",
|
||||||
|
"TestResult": "The synthetic control is enabled.",
|
||||||
|
"TestSkipped": false,
|
||||||
|
"Severity": "Medium",
|
||||||
|
"Service": "Entra"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Blocks": [],
|
||||||
|
"EndOfJson": true
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script>window.__syntheticActiveContentExecuted = true;</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
Tool edition,Community
|
||||||
|
Generated date,2026-01-15 09:30
|
||||||
|
Selected indicators,120
|
||||||
|
AD: scan duration,00:00:21
|
||||||
|
AD: forest name,synthetic.example
|
||||||
|
AD: number of domains,2
|
||||||
|
AD: requested by,SYNTHETIC\Administrator
|
||||||
|
AD: security posture score,71
|
||||||
|
AD category score: AD Delegation,84
|
||||||
|
AD category score: Account Security,79
|
||||||
|
AD category score: AD Infrastructure Security,92
|
||||||
|
AD category score: Group Policy Security,97
|
||||||
|
AD category score: Kerberos Security,95
|
||||||
|
AD category score: Hybrid,100
|
||||||
|
AD: Evaluated,117
|
||||||
|
AD: IOEs found,17
|
||||||
|
AD: Passed,100
|
||||||
|
AD: Failed to run,0
|
||||||
|
AD: Canceled,0
|
||||||
|
AD: Not selected,1
|
||||||
|
AD: Not relevant,2
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head><meta charset="utf-8"><title>Purple Knight synthetic report</title></head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script>
|
||||||
|
window.reportJSON = {
|
||||||
|
"IndicatorsFiles": ["synthetic-indicator.json"],
|
||||||
|
"reportResultsList": [{
|
||||||
|
"ADType": "AD",
|
||||||
|
"IsCustom": false,
|
||||||
|
"ReportResults": {"TotalScore": 50, "TotalCount": 2},
|
||||||
|
"ReportCategories": [{"Category": {"ID": 1, "Name": "Synthetic Category"}, "Indicators": ["00000000-0000-4000-8000-000000000001", "00000000-0000-4000-8000-000000000002"]}]
|
||||||
|
}],
|
||||||
|
"GeneralConfig": {"Categories": [{"ID": 1, "Name": "Synthetic Category", "Description": "Synthetic only", "Weight": 1}]},
|
||||||
|
"AppVersion": "synthetic",
|
||||||
|
"ReportName": "Purple Knight synthetic report"
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
window.reportJSON.GeneralConfig.Categories.map((c) => "Category_" + c.ID).forEach((n) => { window[n] = {} });
|
||||||
|
window["Category_1"]["00000000-0000-4000-8000-000000000001"] = {
|
||||||
|
"ResIndicator": {
|
||||||
|
"State": "Completed", "Grade": "F", "IsEvaluated": true, "IsPassed": false, "IsFailed": true,
|
||||||
|
"ExposureType": "IOE", "IsFailedToRun": false, "IsAborted": false,
|
||||||
|
"UUID": "00000000-0000-4000-8000-000000000001", "CategoryID": 1,
|
||||||
|
"ShortName": "SI900001", "Name": "Synthetic privileged group exposure",
|
||||||
|
"Description": "<p>A synthetic description.</p>", "Version": "1.0.0", "Weight": 10, "Severity": "High",
|
||||||
|
"SecurityFrameworks": [{"Name": "MITRE ATT&CK", "Tags": ["T0000"]}],
|
||||||
|
"Selected": true, "Targets": ["AD"], "CustomIndicator": false, "DataSources": ["Synthetic"]
|
||||||
|
},
|
||||||
|
"ExecutionResult": {"Status": "Failed", "Score": 10, "ResultMessage": "Synthetic exposure found.", "Remediation": "<p>Apply the synthetic remediation.</p>", "Filename": null},
|
||||||
|
"IndicatorReportObjects": [["Object: Synthetic value", "EventTimestamp: 5/27/2026 12:37:57 PM", "Ignored: False"]],
|
||||||
|
"TotalResultsCount": 1
|
||||||
|
};
|
||||||
|
window["Category_1"]["00000000-0000-4000-8000-000000000002"] = {
|
||||||
|
"ResIndicator": {
|
||||||
|
"State": "Completed", "Grade": "A", "IsEvaluated": true, "IsPassed": true, "IsFailed": false,
|
||||||
|
"ExposureType": "None", "IsFailedToRun": false, "IsAborted": false,
|
||||||
|
"UUID": "00000000-0000-4000-8000-000000000002", "CategoryID": 1,
|
||||||
|
"ShortName": "SI900002", "Name": "Synthetic secure configuration",
|
||||||
|
"Description": "<p>A second synthetic description.</p>", "Version": "1.0.0", "Weight": 5, "Severity": "Medium",
|
||||||
|
"SecurityFrameworks": [], "Selected": true, "Targets": ["AD"], "CustomIndicator": false, "DataSources": ["Synthetic"]
|
||||||
|
},
|
||||||
|
"ExecutionResult": {"Status": "Pass", "Score": 0, "ResultMessage": "Synthetic check passed.", "Remediation": "", "Filename": null},
|
||||||
|
"IndicatorReportObjects": [], "TotalResultsCount": 0
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script>window.__syntheticActiveContentExecuted = true;</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="__ASSESSOR_CSP__"
|
content="__ASSESSOR_CSP__"
|
||||||
/>
|
/>
|
||||||
<link rel="icon" type="image/png" sizes="128x128" href="/src/assets/branding/assessor-icon-dark-128.png" />
|
<link rel="icon" type="image/png" href="/src/assets/branding/assessor-icon-transparent.png" />
|
||||||
<title>ASSESSOR</title>
|
<title>ASSESSOR</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,951 @@
|
||||||
|
{"kind":"workbook","id":"wb/g5a6mv","sheets":12,"tables":12}
|
||||||
|
{"kind":"sheet","id":"ws/e4sy1m","name":"September 2026","index":0,"range":"A1:AG5","address":"A1:AG5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"September 2026","address":"A1:AG5","rows":5,"cols":33,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,"Monatssumme"],["Bereich","ASSESSOR","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Std."],["Tätigkeit",null,null,null,"Client Info; GPO-Regeln und Analysepfad; Tests","GPO-Import robuster; nicht lesbare Einträge behandelt",null,"GPO-Auswertung testbereit; UI/Pfade; Regressionstests","Scan-Navigation; GPO-Duplikate und Ergebnisabgleich","GPO-Quellen konsolidiert; Historie und Tests","Import-Timeout; Navigation-Performance; Tests/Build",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,5,9.5,null,9,4,7,8,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,42.5],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,42.5]]}
|
||||||
|
{"kind":"region","sheet":"September 2026","address":"A1:AG5","rows":5,"cols":33,"nonEmpty":84,"blank":81,"text":84,"maxTextLength":53,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Di","Mi","Do","Fr"],["Tätigkeit",null,null,null,"Client Info; GPO-Regeln und Analysepfad; Tests","GPO-Import robuster; nicht lesbare Einträge behandelt"],["Stunden",null,null,null,"5","9.5"],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"September 2026","address":"AG4","formula":"=SUM(C4:AF4)"}
|
||||||
|
{"kind":"formula","sheet":"September 2026","address":"AG5","formula":"=AG4"}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":3}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":5}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":6}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":8}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"E3","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"left","styleId":62}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"F3","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"left","styleId":62}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"H3","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":65}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"I3","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"left","styleId":62}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"J3","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"left","styleId":62}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"K3","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"left","styleId":62}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AG3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"E4","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":67}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"F4","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":67}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"H4","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"numberFormat":"0.0","wrapText":true,"horizontalAlignment":"center","styleId":68}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"I4","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":67}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"J4","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":67}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"K4","style":{"fill":{"type":3,"color":{"type":1,"value":"F2F2F2"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":67}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AG4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":70}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"September 2026","for":"AG5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":71}}
|
||||||
|
{"kind":"sheet","id":"ws/hwa6ib","name":"Oktober 2026","index":1,"range":"A1:AH5","address":"A1:AH5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"Oktober 2026","address":"A1:AH5","rows":5,"cols":34,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,"Monatssumme"],["Bereich","ASSESSOR","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"Oktober 2026","address":"A1:AH5","rows":5,"cols":34,"nonEmpty":74,"blank":96,"text":74,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Do","Fr","Sa","So"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"Oktober 2026","address":"AH4","formula":"=SUM(C4:AG4)"}
|
||||||
|
{"kind":"formula","sheet":"Oktober 2026","address":"AH5","formula":"=AH4"}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":3}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AH1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":6}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AH2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AH3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AH4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"Oktober 2026","for":"AH5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/t14qa1","name":"November 2026","index":2,"range":"A1:AG5","address":"A1:AG5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"November 2026","address":"A1:AG5","rows":5,"cols":33,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,"Monatssumme"],["Bereich","ASSESSOR","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"November 2026","address":"A1:AG5","rows":5,"cols":33,"nonEmpty":72,"blank":93,"text":72,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","So","Mo","Di","Mi"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"November 2026","address":"AG4","formula":"=SUM(C4:AF4)"}
|
||||||
|
{"kind":"formula","sheet":"November 2026","address":"AG5","formula":"=AG4"}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":5}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":8}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AG3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AG4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"November 2026","for":"AG5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/cyk7i5","name":"Dezember 2026","index":3,"range":"A1:AH5","address":"A1:AH5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"Dezember 2026","address":"A1:AH5","rows":5,"cols":34,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,"Monatssumme"],["Bereich","ASSESSOR","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"Dezember 2026","address":"A1:AH5","rows":5,"cols":34,"nonEmpty":74,"blank":96,"text":74,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Di","Mi","Do","Fr"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"Dezember 2026","address":"AH4","formula":"=SUM(C4:AG4)"}
|
||||||
|
{"kind":"formula","sheet":"Dezember 2026","address":"AH5","formula":"=AH4"}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":3}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":5}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AH1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":6}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":8}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AH2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AH3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AH4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"Dezember 2026","for":"AH5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/pbjxg6","name":"Januar 2027","index":4,"range":"A1:AH5","address":"A1:AH5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"Januar 2027","address":"A1:AH5","rows":5,"cols":34,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,"Monatssumme"],["Bereich","ASSESSOR","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"Januar 2027","address":"A1:AH5","rows":5,"cols":34,"nonEmpty":74,"blank":96,"text":74,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Fr","Sa","So","Mo"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"Januar 2027","address":"AH4","formula":"=SUM(C4:AG4)"}
|
||||||
|
{"kind":"formula","sheet":"Januar 2027","address":"AH5","formula":"=AH4"}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AH1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AH2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AH3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AH4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"Januar 2027","for":"AH5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/w1giyz","name":"Februar 2027","index":5,"range":"A1:AE5","address":"A1:AE5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"Februar 2027","address":"A1:AE5","rows":5,"cols":31,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,"Monatssumme"],["Bereich","ASSESSOR","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"Februar 2027","address":"A1:AE5","rows":5,"cols":31,"nonEmpty":68,"blank":87,"text":68,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Mo","Di","Mi","Do"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"Februar 2027","address":"AE4","formula":"=SUM(C4:AD4)"}
|
||||||
|
{"kind":"formula","sheet":"Februar 2027","address":"AE5","formula":"=AE4"}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":3}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":6}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AE3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AE4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"Februar 2027","for":"AE5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/hidpcf","name":"März 2027","index":6,"range":"A1:AH5","address":"A1:AH5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"März 2027","address":"A1:AH5","rows":5,"cols":34,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,"Monatssumme"],["Bereich","ASSESSOR","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"März 2027","address":"A1:AH5","rows":5,"cols":34,"nonEmpty":74,"blank":96,"text":74,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Mo","Di","Mi","Do"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"März 2027","address":"AH4","formula":"=SUM(C4:AG4)"}
|
||||||
|
{"kind":"formula","sheet":"März 2027","address":"AH5","formula":"=AH4"}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":3}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":5}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AH1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":6}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":8}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AH2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AH3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AH4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"März 2027","for":"AH5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/7bmf40","name":"April 2027","index":7,"range":"A1:AG5","address":"A1:AG5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"April 2027","address":"A1:AG5","rows":5,"cols":33,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,"Monatssumme"],["Bereich","ASSESSOR","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"April 2027","address":"A1:AG5","rows":5,"cols":33,"nonEmpty":72,"blank":93,"text":72,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Do","Fr","Sa","So"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"April 2027","address":"AG4","formula":"=SUM(C4:AF4)"}
|
||||||
|
{"kind":"formula","sheet":"April 2027","address":"AG5","formula":"=AG4"}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":3}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":5}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":6}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":8}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AG3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AG4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"April 2027","for":"AG5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/9bfmxu","name":"Mai 2027","index":8,"range":"A1:AH5","address":"A1:AH5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"Mai 2027","address":"A1:AH5","rows":5,"cols":34,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,"Monatssumme"],["Bereich","ASSESSOR","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"Mai 2027","address":"A1:AH5","rows":5,"cols":34,"nonEmpty":74,"blank":96,"text":74,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Sa","So","Mo","Di"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"Mai 2027","address":"AH4","formula":"=SUM(C4:AG4)"}
|
||||||
|
{"kind":"formula","sheet":"Mai 2027","address":"AH5","formula":"=AH4"}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":23}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":5}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AH1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"C0504D"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":24}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":8}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AH2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AH3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AH4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"Mai 2027","for":"AH5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/dnm34v","name":"Juni 2027","index":9,"range":"A1:AG5","address":"A1:AG5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"Juni 2027","address":"A1:AG5","rows":5,"cols":33,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,"Monatssumme"],["Bereich","ASSESSOR","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"Juni 2027","address":"A1:AG5","rows":5,"cols":33,"nonEmpty":72,"blank":93,"text":72,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Di","Mi","Do","Fr"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"Juni 2027","address":"AG4","formula":"=SUM(C4:AF4)"}
|
||||||
|
{"kind":"formula","sheet":"Juni 2027","address":"AG5","formula":"=AG4"}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":3}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":5}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":6}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":8}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AG3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AG4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juni 2027","for":"AG5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/eilfeu","name":"Juli 2027","index":10,"range":"A1:AH5","address":"A1:AH5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"Juli 2027","address":"A1:AH5","rows":5,"cols":34,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,"Monatssumme"],["Bereich","ASSESSOR","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0]]}
|
||||||
|
{"kind":"region","sheet":"Juli 2027","address":"A1:AH5","rows":5,"cols":34,"nonEmpty":74,"blank":96,"text":74,"maxTextLength":16,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Do","Fr","Sa","So"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"Juli 2027","address":"AH4","formula":"=SUM(C4:AG4)"}
|
||||||
|
{"kind":"formula","sheet":"Juli 2027","address":"AH5","formula":"=AH4"}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"A1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"B1","style":{"fill":{"type":3,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":27}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"C1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":3}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"D1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"E1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"F1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"G1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"H1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"I1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"J1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"K1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"L1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"M1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"N1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"O1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"P1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"Q1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"R1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"S1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"T1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"U1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"V1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"W1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"X1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"Y1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"Z1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AA1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AB1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AC1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AD1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AE1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AF1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":4}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AG1","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":19}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AH1","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"A2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"B2","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":1}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"C2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":6}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"D2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"E2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"F2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"G2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"H2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"I2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"J2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"K2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"L2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"M2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"N2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"O2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"P2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"Q2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"R2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"S2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"T2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"U2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"V2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"W2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"X2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"Y2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"Z2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AA2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AB2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AC2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AD2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AE2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AF2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":7}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AG2","style":{"fill":{"type":3,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":true,"horizontalAlignment":"center","styleId":20}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AH2","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{},"wrapText":false,"horizontalAlignment":"center","styleId":2}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"A3","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":28}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AH3","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":15}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"A4","style":{"fill":{"type":3,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":30}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AH4","style":{"fill":{"type":3,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":16}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"A5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"left":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":32}}
|
||||||
|
{"kind":"computedStyle","sheet":"Juli 2027","for":"AH5","style":{"fill":{"type":3,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pattern":{"patternType":2},"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito"},"border":{"top":{"style":"thin","color":{"type":1,"value":"000000"}},"bottom":{"style":"thin","color":{"type":1,"value":"000000"}},"right":{"style":"thin","color":{"type":1,"value":"000000"}}},"wrapText":false,"horizontalAlignment":"center","styleId":18}}
|
||||||
|
{"kind":"sheet","id":"ws/y1qvb8","name":"August 2026","index":11,"range":"A1:AH5","address":"A1:AH5","tables":1,"formulas":2}
|
||||||
|
{"kind":"table","sheet":"August 2026","address":"A1:AH5","rows":5,"cols":34,"values":[["ASSESSOR","Zeiterfassung",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,"Monatssumme"],["Bereich","ASSESSOR","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Di","Mi","Do","Fr","Sa","So","Mo","Std."],["Tätigkeit",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"Repo-Import; PingCastle-Mapping; Dashboard; PostgreSQL/CI",null,"UI-Anpassungen; Purple Knight/PingCastle HTML-Import",null,null,null,null,null,"Summe"],["Stunden",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,7,null,7.5,null,null,null,null,null,14.5],["Gesamtmonatsumme",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,14.5]]}
|
||||||
|
{"kind":"region","sheet":"August 2026","address":"A1:AH5","rows":5,"cols":34,"nonEmpty":78,"blank":92,"text":78,"maxTextLength":57,"preview":[["ASSESSOR","Zeiterfassung","1","2","3","4"],["Bereich","ASSESSOR","Sa","So","Mo","Di"],["Tätigkeit",null,null,null,null,null],["Stunden",null,null,null,null,null],["Gesamtmonatsumme",null,null,null,null,null]],"previewAddress":"A1:F5","previewRows":5,"previewCols":6}
|
||||||
|
{"kind":"formula","sheet":"August 2026","address":"AH4","formula":"=SUM(C4:AG4)"}
|
||||||
|
{"kind":"formula","sheet":"August 2026","address":"AH5","formula":"=AH4"}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"A1","style":{"fill":{"type":1,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":39}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"B1","style":{"fill":{"type":1,"color":{"type":1,"value":"00E640"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":12,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":39}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"C1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"D1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"E1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"F1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"G1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"H1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"I1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"J1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"K1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"L1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"M1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"N1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"O1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"P1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"Q1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"R1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"S1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"T1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"U1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"V1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"W1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"X1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"Y1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"Z1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AA1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AB1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AC1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AD1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AE1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AF1","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AG1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AH1","style":{"fill":{"type":1,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":55}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"A2","style":{"fill":{"type":1,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":41}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"B2","style":{"fill":{"type":1,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":41}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"C2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"D2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"E2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"F2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"G2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"H2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"I2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"J2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"K2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"L2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"M2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"N2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"O2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"P2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"Q2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"R2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"S2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"T2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"U2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"V2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"W2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"X2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"Y2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"Z2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AA2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AB2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AC2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AD2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AE2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AF2","style":{"fill":{"type":1,"color":{"type":1,"value":"2E65A3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":52}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AG2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFACD"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":45}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AH2","style":{"fill":{"type":1,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":55}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"A3","style":{"fill":{"type":1,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":43}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"Z3","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":56}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AB3","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":true,"horizontalAlignment":"center","styleId":56}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AH3","style":{"fill":{"type":1,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":55}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"A4","style":{"fill":{"type":1,"color":{"type":1,"value":"E7E6E6"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":43}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"Z4","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":58}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AB4","style":{"fill":{"type":1,"color":{"type":1,"value":"FFFFFF"},"gradientStops":[],"pictureEffects":[]},"font":{"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":58}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AH4","style":{"fill":{"type":1,"color":{"type":1,"value":"FFF2CC"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":59}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"A5","style":{"fill":{"type":1,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"wrapText":false,"horizontalAlignment":"center","styleId":41}}
|
||||||
|
{"kind":"computedStyle","sheet":"August 2026","for":"AH5","style":{"fill":{"type":1,"color":{"type":1,"value":"D9EAD3"},"gradientStops":[],"pictureEffects":[]},"font":{"bold":true,"fontSize":11,"typeface":"Carlito","fill":{"type":0,"color":{"type":1,"value":"000000"},"gradientStops":[],"pictureEffects":[]}},"border":{"top":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"bottom":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"left":{"style":"thin","color":{"type":1,"value":"B7B7B7"}},"right":{"style":"thin","color":{"type":1,"value":"B7B7B7"}}},"numberFormat":"0.0","wrapText":false,"horizontalAlignment":"center","styleId":60}}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import fs from "node:fs/promises";
|
||||||
|
import { FileBlob, SpreadsheetFile } from "@oai/artifact-tool";
|
||||||
|
|
||||||
|
const inputPath = "C:/Users/milu/OneDrive - Exxeta/Dokumente/Assessor Zeiterfassung/ASSESSOR_Zeiterfassung_Sep2026_Jul2027_Form_wie_vorher.xlsx";
|
||||||
|
const outputDir = "C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/outputs/01a089e9-f688-7f21-87c9-db223e98841f";
|
||||||
|
|
||||||
|
const workbook = await SpreadsheetFile.importXlsx(await FileBlob.load(inputPath));
|
||||||
|
const overview = await workbook.inspect({
|
||||||
|
kind: "workbook,sheet,table,definedName,drawing",
|
||||||
|
maxChars: 12000,
|
||||||
|
tableMaxRows: 8,
|
||||||
|
tableMaxCols: 10,
|
||||||
|
tableMaxCellChars: 100,
|
||||||
|
});
|
||||||
|
console.log(overview.ndjson);
|
||||||
|
|
||||||
|
const sheets = workbook.worksheets.items;
|
||||||
|
for (const sheet of sheets) {
|
||||||
|
const used = sheet.getUsedRange();
|
||||||
|
console.log(`SHEET\t${sheet.name}\t${used?.address ?? "(empty)"}`);
|
||||||
|
if (used) {
|
||||||
|
const region = await workbook.inspect({
|
||||||
|
kind: "region,formula,computedStyle",
|
||||||
|
sheetId: sheet.name,
|
||||||
|
range: used.address,
|
||||||
|
maxChars: 10000,
|
||||||
|
tableMaxRows: 45,
|
||||||
|
tableMaxCols: 12,
|
||||||
|
tableMaxCellChars: 120,
|
||||||
|
options: { maxResults: 120 },
|
||||||
|
});
|
||||||
|
console.log(region.ndjson);
|
||||||
|
const preview = await workbook.render({ sheetName: sheet.name, autoCrop: "all", scale: 1, format: "png" });
|
||||||
|
const safe = sheet.name.replace(/[^a-z0-9_-]+/gi, "_");
|
||||||
|
await fs.writeFile(`${outputDir}/preview_${safe}.png`, new Uint8Array(await preview.arrayBuffer()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { FileBlob, SpreadsheetFile } from "@oai/artifact-tool";
|
||||||
|
const wb = await SpreadsheetFile.importXlsx(await FileBlob.load("C:/Users/milu/OneDrive - Exxeta/Dokumente/Assessor Zeiterfassung/ASSESSOR_Zeiterfassung_Sep2026_Jul2027_Form_wie_vorher.xlsx"));
|
||||||
|
for (const address of ["E1","F1","E2","F2","E3","F3","E4","F4"]) {
|
||||||
|
const out = await wb.inspect({ kind: "computedStyle", sheetId: "Oktober 2026", range: address, maxChars: 1500 });
|
||||||
|
console.log(address, out.ndjson);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
import fs from "node:fs/promises";
|
||||||
|
import { FileBlob, SpreadsheetFile } from "@oai/artifact-tool";
|
||||||
|
|
||||||
|
const inputPath = "C:/Users/milu/OneDrive - Exxeta/Dokumente/Assessor Zeiterfassung/ASSESSOR_Zeiterfassung_Sep2026_Jul2027_Form_wie_vorher.xlsx";
|
||||||
|
const outputDir = "C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/outputs/01a089e9-f688-7f21-87c9-db223e98841f";
|
||||||
|
const outputPath = `${outputDir}/ASSESSOR_Zeiterfassung_Aug2026_Jul2027_aktualisiert.xlsx`;
|
||||||
|
|
||||||
|
const workbook = await SpreadsheetFile.importXlsx(await FileBlob.load(inputPath));
|
||||||
|
|
||||||
|
// The supplied workbook starts in September. Add August using the existing
|
||||||
|
// 31-day October sheet as the structural and formatting reference.
|
||||||
|
const october = workbook.worksheets.getItem("Oktober 2026");
|
||||||
|
const august = workbook.worksheets.add("August 2026");
|
||||||
|
august.getRange("A1:AH5").copyFrom(october.getRange("A1:AH5"), "all");
|
||||||
|
|
||||||
|
// Normalize all date columns to the existing weekday style, then apply the
|
||||||
|
// existing Saturday/Sunday pair style to the five August weekends.
|
||||||
|
for (let col = 2; col <= 32; col += 1) {
|
||||||
|
august.getRangeByIndexes(0, col, 4, 1).copyFrom(october.getRange("C1:C4"), "all");
|
||||||
|
}
|
||||||
|
for (const startCol of [2, 9, 16, 23, 30]) {
|
||||||
|
august.getRangeByIndexes(0, startCol, 4, 2).copyFrom(october.getRange("E1:F4"), "all");
|
||||||
|
}
|
||||||
|
|
||||||
|
const weekdayNames = ["Sa", "So", "Mo", "Di", "Mi", "Do", "Fr"];
|
||||||
|
for (let day = 1; day <= 31; day += 1) {
|
||||||
|
const col = day + 1;
|
||||||
|
august.getCell(0, col).values = [[day]];
|
||||||
|
august.getCell(1, col).values = [[weekdayNames[(day - 1) % 7]]];
|
||||||
|
august.getCell(2, col).clear({ applyTo: "contents" });
|
||||||
|
august.getCell(3, col).clear({ applyTo: "contents" });
|
||||||
|
}
|
||||||
|
august.getRange("A1:B5").copyFrom(october.getRange("A1:B5"), "all");
|
||||||
|
august.getRange("AH1:AH5").copyFrom(october.getRange("AH1:AH5"), "all");
|
||||||
|
august.getRange("AH4").formulas = [["=SUM(C4:AG4)"]];
|
||||||
|
august.getRange("AH5").formulas = [["=AH4"]];
|
||||||
|
|
||||||
|
// Recreate the supplied visual grammar on the newly added sheet. Range copies
|
||||||
|
// preserve content/formulas but not the source sheet's merged cells or widths.
|
||||||
|
august.mergeCells("A3:B3");
|
||||||
|
august.mergeCells("A4:B4");
|
||||||
|
august.mergeCells("A5:AG5");
|
||||||
|
august.getRange("A1:AH5").format = {
|
||||||
|
font: { name: "Carlito", size: 11, color: "#000000" },
|
||||||
|
verticalAlignment: "center",
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
borders: { preset: "all", style: "thin", color: "#B7B7B7" },
|
||||||
|
};
|
||||||
|
august.getRange("A1:B1").format = {
|
||||||
|
fill: "#00E640",
|
||||||
|
font: { name: "Carlito", size: 12, bold: true, color: "#000000" },
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
};
|
||||||
|
august.getRange("A2:B2").format = {
|
||||||
|
fill: "#D9EAD3",
|
||||||
|
font: { name: "Carlito", size: 11, bold: true, color: "#000000" },
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
};
|
||||||
|
august.getRange("A3:B4").format = {
|
||||||
|
fill: "#E7E6E6",
|
||||||
|
font: { name: "Carlito", size: 11, bold: true, color: "#000000" },
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
verticalAlignment: "center",
|
||||||
|
};
|
||||||
|
august.getRange("A5:AH5").format = {
|
||||||
|
fill: "#D9EAD3",
|
||||||
|
font: { name: "Carlito", size: 11, bold: true, color: "#000000" },
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
};
|
||||||
|
august.getRange("C1:AG2").format = {
|
||||||
|
fill: "#FFFACD",
|
||||||
|
font: { name: "Carlito", size: 11, bold: true, color: "#000000" },
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
};
|
||||||
|
august.getRange("C3:AG4").format = {
|
||||||
|
fill: "#FFFFFF",
|
||||||
|
font: { name: "Carlito", size: 11, color: "#000000" },
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
verticalAlignment: "top",
|
||||||
|
};
|
||||||
|
for (const weekend of ["C1:D4", "J1:K4", "Q1:R4", "X1:Y4", "AE1:AF4"]) {
|
||||||
|
august.getRange(weekend).format = {
|
||||||
|
fill: "#2E65A3",
|
||||||
|
font: { name: "Carlito", size: 11, bold: true, color: "#FFFFFF" },
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
verticalAlignment: "center",
|
||||||
|
wrapText: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
august.getRange("AH1:AH4").format = {
|
||||||
|
fill: "#FFF2CC",
|
||||||
|
font: { name: "Carlito", size: 11, bold: true, color: "#000000" },
|
||||||
|
horizontalAlignment: "center",
|
||||||
|
};
|
||||||
|
august.getRange("A1:B5").format.columnWidthPx = 110;
|
||||||
|
august.getRange("C1:AG5").format.columnWidthPx = 108;
|
||||||
|
august.getRange("AH1:AH5").format.columnWidthPx = 118;
|
||||||
|
august.getRange("A1:AH1").format.rowHeightPx = 22;
|
||||||
|
august.getRange("A2:AH2").format.rowHeightPx = 22;
|
||||||
|
august.getRange("A3:AH3").format.rowHeightPx = 96;
|
||||||
|
august.getRange("A4:AH5").format.rowHeightPx = 24;
|
||||||
|
|
||||||
|
const augustEntries = {
|
||||||
|
24: { task: "Repo-Import; PingCastle-Mapping; Dashboard; PostgreSQL/CI", hours: 7 },
|
||||||
|
26: { task: "UI-Anpassungen; Purple Knight/PingCastle HTML-Import", hours: 7.5 },
|
||||||
|
};
|
||||||
|
for (const [dayText, entry] of Object.entries(augustEntries)) {
|
||||||
|
const col = Number(dayText) + 1;
|
||||||
|
august.getCell(2, col).values = [[entry.task]];
|
||||||
|
august.getCell(3, col).values = [[entry.hours]];
|
||||||
|
}
|
||||||
|
august.getRange("C3:AG3").format.wrapText = true;
|
||||||
|
august.getRange("C3:AG3").format.verticalAlignment = "top";
|
||||||
|
august.getRange("C3:AG3").format.rowHeightPx = 96;
|
||||||
|
august.getRange("C4:AG4").format.numberFormat = "0.0";
|
||||||
|
august.getRange("AH4:AH5").format.numberFormat = "0.0";
|
||||||
|
|
||||||
|
const september = workbook.worksheets.getItem("September 2026");
|
||||||
|
const septemberEntries = {
|
||||||
|
3: { task: "Client Info; GPO-Regeln und Analysepfad; Tests", hours: 5 },
|
||||||
|
4: { task: "GPO-Import robuster; nicht lesbare Einträge behandelt", hours: 9.5 },
|
||||||
|
6: { task: "GPO-Auswertung testbereit; UI/Pfade; Regressionstests", hours: 9 },
|
||||||
|
7: { task: "Scan-Navigation; GPO-Duplikate und Ergebnisabgleich", hours: 4 },
|
||||||
|
8: { task: "GPO-Quellen konsolidiert; Historie und Tests", hours: 7 },
|
||||||
|
9: { task: "Import-Timeout; Navigation-Performance; Tests/Build", hours: 8 },
|
||||||
|
};
|
||||||
|
for (const [dayText, entry] of Object.entries(septemberEntries)) {
|
||||||
|
const col = Number(dayText) + 1;
|
||||||
|
september.getCell(2, col).values = [[entry.task]];
|
||||||
|
september.getCell(3, col).values = [[entry.hours]];
|
||||||
|
}
|
||||||
|
september.getRange("C3:AF3").format.wrapText = true;
|
||||||
|
september.getRange("C3:AF3").format.verticalAlignment = "top";
|
||||||
|
september.getRange("C3:AF3").format.rowHeightPx = 96;
|
||||||
|
september.getRange("C4:AF4").format.numberFormat = "0.0";
|
||||||
|
september.getRange("AG4:AG5").format.numberFormat = "0.0";
|
||||||
|
|
||||||
|
workbook.recalculate();
|
||||||
|
|
||||||
|
const checks = [];
|
||||||
|
for (const range of ["August 2026!A1:AH5", "September 2026!A1:AG5"]) {
|
||||||
|
const check = await workbook.inspect({
|
||||||
|
kind: "table",
|
||||||
|
range,
|
||||||
|
include: "values,formulas",
|
||||||
|
tableMaxRows: 5,
|
||||||
|
tableMaxCols: 40,
|
||||||
|
maxChars: 12000,
|
||||||
|
});
|
||||||
|
checks.push(check.ndjson);
|
||||||
|
}
|
||||||
|
console.log(checks.join("\n"));
|
||||||
|
|
||||||
|
const errors = await workbook.inspect({
|
||||||
|
kind: "match",
|
||||||
|
searchTerm: "#REF!|#DIV/0!|#VALUE!|#NAME\\?|#N/A|#NUM!|#NULL!|#SPILL!|#CALC!",
|
||||||
|
options: { useRegex: true, maxResults: 300 },
|
||||||
|
summary: "final formula error scan",
|
||||||
|
});
|
||||||
|
console.log(errors.ndjson);
|
||||||
|
|
||||||
|
for (const sheetName of ["August 2026", "September 2026"]) {
|
||||||
|
const preview = await workbook.render({ sheetName, autoCrop: "all", scale: 1, format: "png" });
|
||||||
|
await fs.writeFile(`${outputDir}/updated_${sheetName.replace(/[^a-z0-9_-]+/gi, "_")}.png`, new Uint8Array(await preview.arrayBuffer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.mkdir(outputDir, { recursive: true });
|
||||||
|
const output = await SpreadsheetFile.exportXlsx(workbook);
|
||||||
|
await output.save(outputPath);
|
||||||
|
console.log(`OUTPUT\t${outputPath}`);
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import fs from "node:fs/promises";
|
||||||
|
import { FileBlob, SpreadsheetFile } from "@oai/artifact-tool";
|
||||||
|
|
||||||
|
const sourcePath = "C:/Users/milu/OneDrive - Exxeta/Dokumente/Assessor Zeiterfassung/ASSESSOR_Zeiterfassung_Sep2026_Jul2027_Form_wie_vorher.xlsx";
|
||||||
|
const outputPath = "C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/outputs/01a089e9-f688-7f21-87c9-db223e98841f/ASSESSOR_Zeiterfassung_Aug2026_Jul2027_aktualisiert.xlsx";
|
||||||
|
const outputDir = "C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/outputs/01a089e9-f688-7f21-87c9-db223e98841f";
|
||||||
|
|
||||||
|
const source = await SpreadsheetFile.importXlsx(await FileBlob.load(sourcePath));
|
||||||
|
const result = await SpreadsheetFile.importXlsx(await FileBlob.load(outputPath));
|
||||||
|
|
||||||
|
const unchanged = [];
|
||||||
|
for (const sheet of source.worksheets.items) {
|
||||||
|
if (sheet.name === "September 2026") continue;
|
||||||
|
const sourceUsed = sheet.getUsedRange();
|
||||||
|
const resultSheet = result.worksheets.getItem(sheet.name);
|
||||||
|
const resultUsed = resultSheet.getUsedRange();
|
||||||
|
unchanged.push({
|
||||||
|
sheet: sheet.name,
|
||||||
|
address: sourceUsed?.address ?? null,
|
||||||
|
sameAddress: sourceUsed?.address === resultUsed?.address,
|
||||||
|
sameValues: JSON.stringify(sourceUsed?.values ?? []) === JSON.stringify(resultUsed?.values ?? []),
|
||||||
|
sameFormulas: JSON.stringify(sourceUsed?.formulas ?? []) === JSON.stringify(resultUsed?.formulas ?? []),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(JSON.stringify({ unchanged }, null, 2));
|
||||||
|
|
||||||
|
const summary = await result.inspect({
|
||||||
|
kind: "sheet,table,formula",
|
||||||
|
maxChars: 14000,
|
||||||
|
tableMaxRows: 5,
|
||||||
|
tableMaxCols: 40,
|
||||||
|
options: { maxResults: 100 },
|
||||||
|
});
|
||||||
|
console.log(summary.ndjson);
|
||||||
|
|
||||||
|
const errors = await result.inspect({
|
||||||
|
kind: "match",
|
||||||
|
searchTerm: "#REF!|#DIV/0!|#VALUE!|#NAME\\?|#N/A|#NUM!|#NULL!|#SPILL!|#CALC!",
|
||||||
|
options: { useRegex: true, maxResults: 300 },
|
||||||
|
summary: "saved-file formula error scan",
|
||||||
|
});
|
||||||
|
console.log(errors.ndjson);
|
||||||
|
|
||||||
|
for (const sheetName of ["August 2026", "September 2026"]) {
|
||||||
|
const preview = await result.render({ sheetName, autoCrop: "all", scale: 1, format: "png" });
|
||||||
|
await fs.writeFile(`${outputDir}/saved_${sheetName.replace(/[^a-z0-9_-]+/gi, "_")}.png`, new Uint8Array(await preview.arrayBuffer()));
|
||||||
|
}
|
||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en" data-theme="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self' ipc: http://ipc.localhost"
|
||||||
|
/>
|
||||||
|
<link rel="icon" type="image/png" href="/assets/assessor-icon-transparent-CwoRGD4B.png" />
|
||||||
|
<title>ASSESSOR</title>
|
||||||
|
<script type="module" crossorigin src="/assets/index-574eOw2u.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/assets/index-DLrW8Yip.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,202 @@
|
||||||
|
# Dummy: Prüfung von Policy Analyzer gegen ASSESSOR
|
||||||
|
|
||||||
|
Prüfdatum: 18.09.2026. Auftrag: korrektes Einlesen und Zuordnen ausschließlich am freigegebenen Client **Dummy** prüfen. Keine Änderungen an Anwendungslogik, GPOs, Datenbank oder Bewertungen. Einordnung: Diagnose im Bereich Phase 13B; keine Implementierung und kein abgeschlossener Meilenstein.
|
||||||
|
|
||||||
|
## Ergebnis
|
||||||
|
|
||||||
|
**Bei den tatsächlich vorhandenen Dummy-GPO-Werten wurde keine Fehlzuordnung und kein Auslesefehler gefunden.** Alle 51 Originalanweisungen aus drei GPO-Backups stimmen mit den gespeicherten Beobachtungen, den zugehörigen 50 Excel-Einstellungen und ihrer Quellenzuordnung in der Work-Projektion überein.
|
||||||
|
|
||||||
|
Das ist ausdrücklich keine uneingeschränkte Bestätigung vollständiger Policy-Analyzer-Parität. Zusätzlich wurden vier Auffälligkeiten belegt:
|
||||||
|
|
||||||
|
1. Ein identischer Registry-Zielwert wird je nach Quelldateiformat unter zwei technischen Identitäten geführt: `LdapEnforceChannelBinding`, Excel-Zeile 871. Das ist eine formatübergreifende Identitätslücke, keine Vertauschung vorhandener Dummy-Werte.
|
||||||
|
2. Bei score-neutralem `Not configured` können Beschreibung und Vergleichstext dennoch eine Abweichung beziehungsweise einen erforderlichen fehlenden Wert behaupten.
|
||||||
|
3. Der gespeicherte Vergleich enthält vier Baselinepakete jeweils mit zwei Analyseversionen. Das erzeugt 1.620 zusätzliche Vergleichszeilen, aber im geprüften Dummy-Stand keine veränderten Einstellungsurteile.
|
||||||
|
4. Fünf in Excel enthaltene Dienst-Einstellungen werden vom Baseline-Parser nicht unterstützt; betroffen sind 29 belegte Baseline-/Einstellungskombinationen. Dummy konfiguriert diese fünf Dienste nicht.
|
||||||
|
|
||||||
|
Der frühere Screenshot und andere Kundendaten sind nicht Bestandteil dieser Bewertung.
|
||||||
|
|
||||||
|
## 1. Prüfgrundlagen und Abgrenzung
|
||||||
|
|
||||||
|
- Originaldatei: [Output-PolAnal-Dummy.xlsx](<C:/Users/milu/OneDrive - Exxeta/OC_SOC - General/Projekt Assessor/Dummy-Scans/Output-PolAnal-Dummy.xlsx>), Blatt `Policy Analyzer`, Bereich `A1:BO953`: 952 Datenzeilen, 67 Spalten.
|
||||||
|
- Dummy-Werte: `G:I`; technische Adresse: `D:F`; Dummy-GPO-Namen und Quelldateien: `AR:AS`.
|
||||||
|
- Elf Microsoft-Baselinepakete: Wert-/Options-/Typblöcke `J:AP`, zugehörige Quellen `AT:BO`.
|
||||||
|
- Projekt: `Dummy`, ID `project-7cbcb7ad`.
|
||||||
|
- Datenspeicher: ausschließlich die kanonische lokale PostgreSQL-Datenbank `assessor` auf `127.0.0.1:5432`; Abfragen mit schreibgeschützter Transaktion beziehungsweise schreibgeschützter Verbindung und exaktem Dummy-Projektfilter. Gemeinsam verwendete Microsoft-Baselines wurden nur als Vergleichsquellen dieses Projekts gelesen.
|
||||||
|
- Original-Dummy-Archiv: `gpo-backups/dummy-gpo-folder-2026-07-22.zip`, 23.163 Bytes; SHA-256 `2d083c277c36ad357dec97a44b50323f12de8fe1875db2d7024089754397acb2`.
|
||||||
|
- Ausgewählter gespeicherter Vergleich: `policy-evaluation-run-84d8b95c8e7610994abb842a4de05e0e`, Engine `1.9.0`, abgeschlossen `2026-09-17T13:53:44.060Z`.
|
||||||
|
- Repository-Revision bei Beginn und Abschluss: `420`.
|
||||||
|
|
||||||
|
Die Prüfung umfasst importierte Backup-Konfiguration, keine tatsächlich wirksame Endgerätekonfiguration, keine GPO-Verknüpfungen oder Vererbungsreihenfolge und keine Aussage zur sachgerechten Auswahl einer Betriebssystem-/Rollenbaseline.
|
||||||
|
|
||||||
|
## 2. Methode
|
||||||
|
|
||||||
|
Die Prüfkette war: Excel → unverändertes Original-ZIP → unabhängig dekodierte Quelldateien → persistierte Dummy-Beobachtungen → ausgewählte Bewertung → produktive Work-/Reader-Datenprojektion.
|
||||||
|
|
||||||
|
`Registry.pol` wurde unabhängig als Binärformat gelesen, einschließlich Datentyp, Nutzdaten, Datensatzposition und Byteoffset. Die INF-Dateien wurden unabhängig nach Sektion, Schlüssel, Originalwert und Zeile ausgewertet. Anschließend wurden sämtliche Dummy-Beobachtungen einzeln dagegen geprüft. Die Work-Projektion wurde im Speicher mit den produktiven Projektionsfunktionen und den Dummy-API-Daten aufgebaut; die Anwendung wurde nicht neu bewertet oder verändert. Dies ist eine Prüfung der Datenprojektion, kein zusätzlicher visueller Browser-/CSS-Test.
|
||||||
|
|
||||||
|
Verglichen wurde anhand vollständiger technischer Adressen und exakter Quellen, nicht anhand ähnlich klingender Anzeigenamen. Groß-/Kleinschreibung von Registry-Adressen und Pfadseparatoren wurden für den Adressvergleich normalisiert; Wertinhalt und Typ blieben erhalten. Principal-Sammlungen wurden als Mengen geprüft; zusätzlich wurde die ursprüngliche Quellenreihenfolge gegenüber den gespeicherten Werten kontrolliert.
|
||||||
|
|
||||||
|
Wichtig beim Excel-Vergleich: Policy Analyzer fasst Wertlisten unabhängig von GPO-/Quelldateilisten zusammen. Mehrzeilige Wert- und Quellenzellen dürfen nicht positionsweise miteinander verbunden werden. Die Wert-/Typmengen und Quellenmengen wurden daher separat verglichen; die einzelne Quellenzuordnung wurde gegen die Originaldateien geprüft.
|
||||||
|
|
||||||
|
Die Excel-Zellen wurden mit einem zweiten, unabhängigen XLSX-Leser verifiziert. Leere Zeichenfolgen wurden nicht als Zahlen oder fehlende Registry-Schlüssel interpretiert. Der erste XLSX-Importweg zeigte bei leeren Shared Strings technische Indexwerte; dessen Ergebnis wurde verworfen, nicht als Prüfgrundlage verwendet.
|
||||||
|
|
||||||
|
## 3. Vollständigkeit und Zuordnung der Dummy-Werte
|
||||||
|
|
||||||
|
| Prüfschritt | Ergebnis |
|
||||||
|
|---|---:|
|
||||||
|
| GPO-Backups | 3 |
|
||||||
|
| Policy-Quelldateien | 4 |
|
||||||
|
| Originalanweisungen aus Registry.pol | 4 |
|
||||||
|
| Originalanweisungen aus Security-Template-INF | 47 |
|
||||||
|
| Originalanweisungen insgesamt | 51 |
|
||||||
|
| Exakt passende gespeicherte Beobachtungen | 51 von 51 |
|
||||||
|
| Excel-Einstellungen mit nichtleerem Datenwert | 48 |
|
||||||
|
| Zusätzlich explizite leere REG_NONE-Schlüsselanlagen | 2 |
|
||||||
|
| Unterschiedliche konfigurierte Dummy-Einstellungen | 50 |
|
||||||
|
| In Work wiedergefundene Originalbeobachtungen | 51 von 51 |
|
||||||
|
| Quellenbehaftete Work-Vergleichszeilen mit geprüfter Zuordnung | 339 von 339 |
|
||||||
|
| Falsche Werte, Typen, Quellen oder Backup-Zuordnungen | 0 |
|
||||||
|
|
||||||
|
Geprüft wurden insbesondere Originaldatei-SHA, vollständiger Schlüssel, Wertname, Typ, Daten, Projekt-/Backup-Zuordnung und die jeweiligen INF-Zeilen beziehungsweise Registry.pol-Datensatzpositionen. Auch die in Excel angegebenen GPO-Namen wurden gegen die Originalmetadaten geprüft.
|
||||||
|
|
||||||
|
### Warum 51 Anweisungen nur 50 Einstellungen ergeben
|
||||||
|
|
||||||
|
`LDAPServerIntegrity` in Excel-Zeile **873** wird in zwei Dummy-GPOs auf denselben DWORD-Wert **2** gesetzt: `Default Domain Controllers Policy` und `LDqp`. ASSESSOR erhält beide Quellen und kennzeichnet die identische Mehrfachkonfiguration als Duplicate. Das ist kein Konflikt und kein verlorener Datensatz. Maßgebliche Excel-Zellen: `D873:I873` und `AR873:AS873`.
|
||||||
|
|
||||||
|
### Vollständige Liste der geprüften Dummy-Einstellungen
|
||||||
|
|
||||||
|
Für jede nachfolgende Zeile sind Originalquelle, Excel, gespeicherter Wert und Work-Quellenzuordnung bestätigt. „Menge identisch“ bedeutet vollständigen SID-Mengenvergleich; keine Stichprobe.
|
||||||
|
|
||||||
|
| Excel-Zeile | Technischer Wert / Operation | Bestätigter Dummy-Wert |
|
||||||
|
|---:|---|---|
|
||||||
|
| 400 | EFSBlob | REG_BINARY, vollständig bytegleich |
|
||||||
|
| 401 | EFS-Zertifikat / Blob | REG_BINARY, vollständig bytegleich |
|
||||||
|
| 402 | EFS / CRLs: Schlüsselanlage | REG_NONE, explizit leer |
|
||||||
|
| 403 | EFS / CTLs: Schlüsselanlage | REG_NONE, explizit leer |
|
||||||
|
| 429 | RequireLogonToChangePassword | 0 |
|
||||||
|
| 845 | PasswordComplexity | 0 |
|
||||||
|
| 846 | PasswordHistorySize | 24 |
|
||||||
|
| 847 | ClearTextPassword | 0 |
|
||||||
|
| 848 | MaximumPasswordAge | 37201 |
|
||||||
|
| 849 | MinimumPasswordLength | 5 |
|
||||||
|
| 850 | MinimumPasswordAge | 1 |
|
||||||
|
| 851 | TicketValidateClient | 1 |
|
||||||
|
| 852 | MaxTicketAge | 10 |
|
||||||
|
| 853 | MaxServiceAge | 600 |
|
||||||
|
| 854 | MaxClockSkew | 5 |
|
||||||
|
| 855 | MaxRenewAge | 7 |
|
||||||
|
| 856 | LockoutBadCount | 5 |
|
||||||
|
| 857 | LockoutDuration | 5 |
|
||||||
|
| 858 | ResetLockoutCount | 5 |
|
||||||
|
| 859 | NoLMHash | DWORD 1 |
|
||||||
|
| 873 | LDAPServerIntegrity | DWORD 2, zwei Originalquellen |
|
||||||
|
| 877 | RequireSignOrSeal | DWORD 1 |
|
||||||
|
| 888 | RequireSecuritySignature | DWORD 1 |
|
||||||
|
| 889 | EnableSecuritySignature | DWORD 1 |
|
||||||
|
| 891 | ForceLogoffWhenHourExpire | 0 |
|
||||||
|
| 904 | LSAAnonymousNameLookup | 0 |
|
||||||
|
| 912 | SeSystemTimePrivilege | Menge identisch |
|
||||||
|
| 913 | SeIncreaseBasePriorityPrivilege | Menge identisch |
|
||||||
|
| 914 | SeBatchLogonRight | Menge identisch |
|
||||||
|
| 918 | SeIncreaseQuotaPrivilege | Menge identisch |
|
||||||
|
| 920 | SeNetworkLogonRight | Menge identisch |
|
||||||
|
| 921 | SeChangeNotifyPrivilege | Menge identisch |
|
||||||
|
| 922 | SeDebugPrivilege | Menge identisch |
|
||||||
|
| 925 | SeUndockPrivilege | Menge identisch |
|
||||||
|
| 926 | SeEnableDelegationPrivilege | Menge identisch |
|
||||||
|
| 927 | SeAssignPrimaryTokenPrivilege | Menge identisch |
|
||||||
|
| 928 | SeCreatePagefilePrivilege | Menge identisch |
|
||||||
|
| 929 | SeSystemProfilePrivilege | Menge identisch |
|
||||||
|
| 930 | SeProfileSingleProcessPrivilege | Menge identisch |
|
||||||
|
| 935 | SeRemoteShutdownPrivilege | Menge identisch |
|
||||||
|
| 936 | SeAuditPrivilege | Menge identisch |
|
||||||
|
| 937 | SeShutdownPrivilege | Menge identisch |
|
||||||
|
| 938 | SeMachineAccountPrivilege | Menge identisch |
|
||||||
|
| 939 | SeLoadDriverPrivilege | Menge identisch |
|
||||||
|
| 941 | SeInteractiveLogonRight | Menge identisch |
|
||||||
|
| 942 | SeBackupPrivilege | Menge identisch |
|
||||||
|
| 944 | SeTakeOwnershipPrivilege | Menge identisch |
|
||||||
|
| 945 | SeSystemEnvironmentPrivilege | Menge identisch |
|
||||||
|
| 946 | SeSecurityPrivilege | Menge identisch |
|
||||||
|
| 947 | SeRestorePrivilege | Menge identisch |
|
||||||
|
|
||||||
|
Die produktive Verdichtungsfunktion liefert für diese **50 quellenbehafteten Einstellungen**: **16 Fail, 15 Pass, 19 No baseline rule**, zusätzlich **eine Duplicate-Markierung und keine Konfliktmarkierung**. Diese Zählung bestätigt die technische Zusammenführung; sie ist keine unabhängige Freigabe der fachlichen Baseline-Auswahl.
|
||||||
|
|
||||||
|
## 4. Abgleich der Baseline-Seite der Excel
|
||||||
|
|
||||||
|
Alle elf referenzierten Baseline-Archive wurden gegen ihre gespeicherte Paket-SHA geprüft. Für 5.031 aktuelle Baseline-Setting-Referenzen wurde die SHA der zugrunde liegenden 122 Policy-Dateien gegen die Originalarchive verifiziert.
|
||||||
|
|
||||||
|
| Baseline | Belegte Nicht-Audit-Kombinationen | Strikt passend | Nur Optionsreihenfolge | Nicht unterstützte Dienste | Audit-Kombinationen passend |
|
||||||
|
|---|---:|---:|---:|---:|---:|
|
||||||
|
| Microsoft 365 Apps 2512 | 385 | 385 | 0 | 0 | 0 |
|
||||||
|
| Microsoft Edge 139 | 20 | 20 | 0 | 0 | 0 |
|
||||||
|
| Windows 10 1809 / Server 2019 | 366 | 359 | 2 | 5 | 27 |
|
||||||
|
| Windows 11 23H2 | 369 | 363 | 2 | 4 | 23 |
|
||||||
|
| Windows 11 24H2 | 403 | 397 | 2 | 4 | 23 |
|
||||||
|
| Windows 11 25H2 | 402 | 396 | 2 | 4 | 23 |
|
||||||
|
| Windows Server 2025 | 360 | 357 | 2 | 1 | 30 |
|
||||||
|
| Windows Server 2022 | 309 | 306 | 2 | 1 | 29 |
|
||||||
|
| Windows 10 1607 / Server 2016 | 356 | 353 | 2 | 1 | 24 |
|
||||||
|
| Windows 10 22H2 | 343 | 337 | 2 | 4 | 23 |
|
||||||
|
| Windows 10 20H2 / Server 20H2 | 357 | 350 | 2 | 5 | 29 |
|
||||||
|
| **Summe** | **3.670** | **3.623** | **18** | **29** | **231** |
|
||||||
|
|
||||||
|
Eine Kombination bezeichnet hier eine belegte Einstellung in einem Baselinepaket, nicht jede einzelne Zelle und nicht jede enthaltene GPO-Variante.
|
||||||
|
|
||||||
|
Die 32 unterschiedlichen Audit-Policy-Zeilen `813:844` umfassen 231 belegte Baseline-Kombinationen: alle stimmen hinsichtlich Erfolgs-/Fehlerflags und Quelldateien überein. Zusätzlich wurden die gespeicherten Audit-GUIDs, Flags und Quellenzeilen unmittelbar gegen die originalen `audit.csv`-Dateien geprüft. Die deutschen Anzeigenamen wurden mit dem lokalen, nur lesend abgefragten Audit-Unterkategorienkatalog und vier dokumentierten sprachlichen Aliaszuordnungen auf GUIDs abgebildet; es wurden keine Audit-Einstellungen des Rechners verändert.
|
||||||
|
|
||||||
|
Die 18 reinen Darstellungsunterschiede liegen in den HardenedPaths-Zeilen **507 und 508**. Excel schreibt `RequireIntegrity=1,RequireMutualAuthentication=1`; ASSESSOR erhält `RequireMutualAuthentication=1,RequireIntegrity=1`. Adresse, Quellen und enthaltene Optionen sind gleich; lediglich die Reihenfolge unterscheidet sich. Das ist keine Vertauschung der Einstellung. Die Aussage bezieht sich auf diese konkrete Optionsliste und rechtfertigt keine allgemeine Sortierung beliebiger Registry-Strings.
|
||||||
|
|
||||||
|
## 5. Bestätigte Auffälligkeiten
|
||||||
|
|
||||||
|
### A. Eine Registry-Adresse, zwei technische Identitäten
|
||||||
|
|
||||||
|
Excel-Zeile **871** führt `HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\LdapEnforceChannelBinding` einmal. ASSESSOR führt für denselben Zielwert eine `registry-value`-Identität aus Registry.pol und eine `security-template`-Identität aus dem INF-Abschnitt `Registry Values`.
|
||||||
|
|
||||||
|
Ursache: [policy-target-resolver.ts](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/server/gpo/policy-target-resolver.ts:8>) leitet nur `registry-policy` an den Registry-Resolver weiter; die INF-Identität wird danach mit eigenem Mechanismus/Sektionsschlüssel gebildet. [admx-policy-catalog.ts](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/server/gpo/admx-policy-catalog.ts:344>) bildet hingegen die Registry-Identität aus Scope, Zielart, Hive, vollständigem Schlüssel und Wertname.
|
||||||
|
|
||||||
|
Nachweisbarer Effekt: **948 ASSESSOR-Identitäten entsprechen 947 unterschiedlichen Excel-Adressen**. Die übrigen fünf der 952 Excel-Zeilen sind die nicht unterstützten Dienste. Genau eine Adresse ist doppelt identifiziert: Zeile 871.
|
||||||
|
|
||||||
|
Für Dummy ist `G871:I871` leer; alle fünf betroffenen gespeicherten Vergleiche stehen auf `Not configured` und haben keine Dummy-Quelle. Damit ist hier eine fehlende formatübergreifende Gleichsetzung belegt, aber kein dadurch falsch zugeordneter konfigurierter Dummy-Wert. Ein allgemeiner Gleichwertigkeitstest von INF und Registry.pol ist im Vergleichsvertrag bereits als offene Paritätsprüfung benannt.
|
||||||
|
|
||||||
|
### B. Widersprüchlicher Text bei Not configured
|
||||||
|
|
||||||
|
Reproduzierbares Dummy-Beispiel: Excel-Zeile **529**, `ACSettingIndex` unter PowerSettings-GUID `0e796bdb-100d-47d6-a2d5-f7d2daa51f51`. Dummy enthält dafür keinen Wert. ASSESSOR projiziert korrekt `Status = Not configured`, `Configured Value = Not configured` und keine Quellen.
|
||||||
|
|
||||||
|
Gleichzeitig lautet `Comparison` beziehungsweise der Beschreibungstext `Differs from every assessed baseline variant`; eine Variantenbeschreibung nennt `Missing required value or key`, und der Remediation-Text gibt einen Sollwert an. Damit wirkt eine score-neutrale Abwesenheit sprachlich wie eine konfigurierte Fehlstellung.
|
||||||
|
|
||||||
|
Ursache: [policy-observations-to-dataset.ts](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/features/project-import/policy-observations-to-dataset.ts:91>) verwendet die Varianten-Zusammenfassung vor dem späteren Missing-Zweig. [guidance.ts](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/profiles/group-policy/guidance.ts:81>) formuliert fehlende Werte pauschal als erforderlich. Die korrekte Abgrenzung normaler Abwesenheit ist dagegen bereits in [policy-comparison-disposition.ts](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/domain/policy-comparison-disposition.ts:6>) vorhanden.
|
||||||
|
|
||||||
|
Dies ist ein belegter Darstellungs-/Semantikfehler, kein falsch eingelesener Istwert. Explizite Schlüsselpräsenz- und Leerstring-Anforderungen sind hiervon gesondert zu behandeln; ihre dokumentierten Vergleichsregeln dürfen durch eine spätere Korrektur nicht verändert werden.
|
||||||
|
|
||||||
|
### C. Doppelte Baseline-Analysegenerationen im gespeicherten Lauf
|
||||||
|
|
||||||
|
Der aktuelle gespeicherte Dummy-Lauf referenziert 15 Analysen zu elf Paketen. Vier Pakete sind gleichzeitig mit Analyzer `1.2.0` und `1.3.0` enthalten, jeweils mit identischer Baseline-ID und identischer Paket-SHA.
|
||||||
|
|
||||||
|
| Paket | Redundante Vergleiche |
|
||||||
|
|---|---:|
|
||||||
|
| Windows 11 23H2 | 388 |
|
||||||
|
| Windows 11 24H2 | 422 |
|
||||||
|
| Windows 11 25H2 | 421 |
|
||||||
|
| Windows Server 2025 | 389 |
|
||||||
|
| **Summe** | **1.620** |
|
||||||
|
|
||||||
|
Die paarweise Prüfung ergab null Unterschiede bei technischem Ziel, Ist-/Sollwerten, Klassifikation und Quellen. Eine ausschließlich im Speicher vorgenommene Bereinigung würde die Bewertungszeilen von 5.492 auf 3.872 und die Work-Vergleichszeilen von 5.511 auf 3.891 reduzieren. **Alle 948 verdichteten Einstellungsurteile bleiben dabei unverändert.**
|
||||||
|
|
||||||
|
Die laufende Status-API meldet dafür bereits `evaluationUpdates`, Grund `baseline-analysis`, mit elf Zielanalysen. Work zeigt den gespeicherten Lauf, bereinigt dessen innere historischen Referenzen aber nicht beim Lesen. Es wurde kein neuer Bewertungslauf gestartet.
|
||||||
|
|
||||||
|
### D. Nicht unterstützte Dienst-Einstellungen
|
||||||
|
|
||||||
|
Excel-Zeilen **949 bis 953** enthalten `AppIDSvc`, `XboxGipSvc`, `XblAuthManager`, `XboxNetApiSvc` und `XblGameSave` aus dem INF-Abschnitt `Service General Setting`. Insgesamt sind 29 Baseline-/Einstellungskombinationen belegt und in den Original-INF-Dateien vorhanden.
|
||||||
|
|
||||||
|
[security-template-parser.ts](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/server/gpo/security-template-parser.ts:13>) unterstützt nur `System Access`, `Kerberos Policy`, `Privilege Rights` und `Registry Values`. Andere Abschnitte werden mit `gpo-security-template-section-unsupported` diagnostiziert und nicht als vergleichbare Settings ausgegeben.
|
||||||
|
|
||||||
|
Das ist eine echte Abdeckungslücke gegenüber Excel, aber keine falsche Zuordnung. `G949:I953` ist für Dummy leer: Es ging dadurch kein in Dummy konfigurierter Dienstwert verloren. Die Diagnose erlaubt keine Aussage, dass nicht unterstützte Konfiguration allgemein fehlerfrei bewertet würde.
|
||||||
|
|
||||||
|
## 6. Integrität und mögliche nächste Schritte
|
||||||
|
|
||||||
|
Die Datenbankrevision blieb `420`; keine Re-Evaluation, kein Import und keine Änderung der Original-GPO-Dateien wurden ausgeführt. Vorhandene Änderungen im Arbeitsverzeichnis blieben unangetastet. Die 25 temporären Datenbankauszüge, Originalkopien und Prüfscripte wurden anschließend entfernt; erhalten bleibt dieser Bericht. Die Originale sind unverändert verfügbar; die temporären Kopien wurden nicht als dauerhafte Prüfartefakte aufbewahrt.
|
||||||
|
|
||||||
|
Der erste XLSX-Snapshot hatte SHA-256 `b3349781e8c7555ac2edcf5ecec9bd9b15c999e5213ca9112c7069643c7cf308`; beim abschließenden Gegenlesen hatte der Dateicontainer SHA-256 `4001bc5c6347c28b5677ed9235c2d015305aea5b95ccd12b6b917d20440076fb`. Der erneute vollständige Zellvergleich ergab **null geänderte Zellinhalte**. Im ZIP wurden zusätzliche Custom-XML-/Dokumentmetadaten und geänderte Paketbeziehungen festgestellt; die Tabellenbestandteile blieben identisch. ASSESSOR und diese Prüfung haben die Arbeitsmappe nicht beschrieben.
|
||||||
|
|
||||||
|
Sinnvolle, separat zu beauftragende Änderungen sind: erstens die formatübergreifende Registry-Identität mit synthetischem INF-/Registry.pol-End-to-End-Test schließen; zweitens Not-configured-Beschreibung und Remediation konsistent zum autoritativen Status machen; drittens veraltete Baseline-Analysegenerationen über den vorgesehenen Updatepfad behandeln. Die Unterstützung der Dienstsektion ist eine eigene Parsererweiterung. Keine dieser Änderungen wurde im Rahmen dieser Diagnose umgesetzt.
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
# Dummy: Result DB / Work count reconciliation
|
||||||
|
|
||||||
|
Read-only snapshot captured at 2026-09-18T12:11:13.513Z. Only project `project-7cbcb7ad`, verified as `Dummy`, was queried. No application or database changes were made. The companion `dummy-result-count-audit.mjs` uses scoped GET requests and reconstructs Work through the actual production selectors, projection and status filter.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
| Measure | Screenshot and reproduced output | Correct current population under existing rules |
|
||||||
|
| --- | ---: | ---: |
|
||||||
|
| Result DB GPO deviation comparisons | 179 | 127 |
|
||||||
|
| Work unique settings with Fail verdict | 16 selected by Deviation | 25 |
|
||||||
|
|
||||||
|
The two corrected numbers are not meant to be equal: 127 baseline-package comparisons belong to 25 unique technical policy identities. Both stored runs contain the same 25 failing identities and the same 127 distinct identity/baseline-package pairs. The issues are therefore not explained by different granularity alone.
|
||||||
|
|
||||||
|
## 1. Result DB selects an older run with repeated baseline versions
|
||||||
|
|
||||||
|
Both runs use aggregation `policy-snapshot-aggregation-d0bd18c02cbc8d8e4102391f089c4c7f` and evaluator 1.9.0.
|
||||||
|
|
||||||
|
| Property | Result DB selection | Work selection |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Run | policy-evaluation-run-84d8b95c8e7610994abb842a4de05e0e | policy-evaluation-run-ac3189bde3afae087102bc4e77ad9cba |
|
||||||
|
| Completed (UTC) | 2026-09-17T13:53:44.060Z | 2026-09-18T11:24:05.072Z |
|
||||||
|
| Baseline analysis references | 15 | 11 |
|
||||||
|
| Distinct baseline packages | 11 | 11 |
|
||||||
|
| Evaluation results | 5492 | 3872 |
|
||||||
|
| Fail comparisons | 179 | 127 |
|
||||||
|
|
||||||
|
The PostgreSQL selector in `server/postgres/postgres-assessor-repository.ts:1101` ranks raw `baselineAnalysisIds` count before completion time. Fifteen references therefore outrank eleven, even though both cover eleven packages. Work's selector in `src/domain/unified-result.ts:599` measures distinct compared `baselineId` values instead and selects the newer run.
|
||||||
|
|
||||||
|
The older run contains repeated analysis versions for Windows 11 23H2, 24H2, 25H2 and Windows Server 2025. Deduplicating its Fail comparisons by technical identity plus baseline package yields exactly the newer run's 127 pairs: zero old-only and zero new-only pairs. The excess is exactly 52 comparisons.
|
||||||
|
|
||||||
|
Result DB returned 5493 GPO results: 179 misconfigured, 227 match, 5080 not-configured, 6 review-required, and 1 duplicate diagnostic. The extra diagnostic explains the difference from 5492 evaluation results. All 179 deviations belong to Dummy assessment `assessment-batch-1785225371107-78`.
|
||||||
|
|
||||||
|
## 2. Work suppresses nine authoritative Fail verdicts
|
||||||
|
|
||||||
|
Reconstructed Work contains 948 records: 25 Fail, 25 Pass, 879 Not configured and 19 No baseline rule. Running the production Deviation filter returns only 16.
|
||||||
|
|
||||||
|
The guard in `src/profiles/profile-factory.ts:67` rejects a record whenever its displayed Configured Value equals Not configured, even when its authoritative status is Fail. This contradicts the rule that status filters follow the authoritative verdict and the explicit required-registry-presence exception.
|
||||||
|
|
||||||
|
The excluded records are eight required key-presence states and one required present empty string. Each retained comparison is independently Different and its retained baseline variant assessment is all-fail. Missing required presence is Fail under the binding rules; ordinary missing recommendations remain score-neutral Not configured.
|
||||||
|
|
||||||
|
| Excluded setting | Current Fail comparisons |
|
||||||
|
| --- | ---: |
|
||||||
|
| Software\Policies\Microsoft\Windows\SrpV2\Dll | 3 |
|
||||||
|
| Software\Policies\Microsoft\Windows\SrpV2\Msi | 3 |
|
||||||
|
| Software\Policies\Microsoft\Windows\SrpV2\Script | 3 |
|
||||||
|
| Software\Policies\Microsoft\WindowsFirewall\PublicProfile\Logging | 1 |
|
||||||
|
| fbaenabledhosts (present empty string, Microsoft 365 Apps 2512) | 1 |
|
||||||
|
| Software\Policies\Microsoft\Windows\Safer | 5 |
|
||||||
|
| Software\Policies\Microsoft\WindowsFirewall\PrivateProfile\Logging | 1 |
|
||||||
|
| Software\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig | 1 |
|
||||||
|
| Software\Policies\Microsoft\WindowsFirewall\DomainProfile\Logging | 1 |
|
||||||
|
|
||||||
|
These nine settings account for 19 comparisons. The 16 visible failing settings account for the other 108 comparisons, totaling 127.
|
||||||
|
|
||||||
|
The exact screenshot filter counts were reproduced: Deviation 16, Passed 25, Not configured 879, No baseline rule 19, Not comparable 0, Conflicts 0, Duplicates 1. Diagnostic categories overlap verdicts and must not be summed as exclusive status buckets.
|
||||||
|
|
||||||
|
## Repair scope identified, not implemented
|
||||||
|
|
||||||
|
Align PostgreSQL current-run selection with the shared distinct-baseline-coverage rule, and remove the display-value override of authoritative GPO Fail verdicts. Preserve ordinary Not configured neutrality, diagnostic overlap and historical evaluations. Permanent synthetic behavioral tests must cover current-run selection with repeated baseline versions and both GPO Work entry paths for a missing required key/empty-string presence.
|
||||||
|
|
||||||
|
## Follow-up: authorized fix and verification
|
||||||
|
|
||||||
|
The user subsequently authorized implementation. The diagnosis above is preserved as the pre-fix snapshot; the following is the post-fix acceptance, captured at **2026-09-18T12:40:30.253Z**.
|
||||||
|
|
||||||
|
- PostgreSQL now counts distinct nonempty compared baseline IDs, falls back to declared analysis references only when no compared baseline is named, and retains completion time / run ID ordering. No evaluation record was edited or removed.
|
||||||
|
- Work Deviation now follows the authoritative Fail verdict without a Configured Value text override. Ordinary Not configured, historical readiness and diagnostic flags retain their existing behavior.
|
||||||
|
- Both views select `policy-evaluation-run-ac3189bde3afae087102bc4e77ad9cba` for the same Dummy aggregation and assessment.
|
||||||
|
- Result DB: **127 Fail comparisons**, **25 distinct technical identities**, 3873 total results (3872 evaluations plus one duplicate diagnostic). Classification counts: misconfigured 127, match 162, not-configured 3578, review-required 5, duplicate 1.
|
||||||
|
- Work: **25 Deviation**, 25 Passed, 879 Not configured, 19 No baseline rule, 0 Not comparable, 0 Conflicts, 1 Duplicates. Total 948 rows.
|
||||||
|
- Identity sets are exactly equal in both directions. There are 127 distinct identity/package comparison pairs, no additional pairs and **zero hidden Fail settings**.
|
||||||
|
- The running backend reports PostgreSQL and release `3e9d353e9e4d5e96d395252edbc02a4826b55075ab155b3d094954e27ae55c90`, matching the SHA-256 of the built server bundle containing the corrected selection SQL. The existing managed development process applied the update; no alternate datastore was started.
|
||||||
|
|
||||||
|
Verification: 50 targeted tests passed, including five actual PostgreSQL executions of the repository's production SELECT over fully synthetic CTE relations on a read-only connection. Cases cover repeated versions, broader versus newer coverage, deterministic ties, unavailable baseline-ID fallback and empty-result fallback, plus foreign project and independent snapshot scope. Permanent Work interaction tests cover both entry paths and required presence versus ordinary absence and history; Result DB interaction checks current comparison counts and filtering. Client and server production builds, local-only security and design checks passed.
|
||||||
|
|
||||||
|
The initial full suite reported 1357 passed, two failures and 13 optional skips, including a transient schema-file read/RPC timeout during an approximately six-minute runtime interruption. The schema test passed immediately on isolated rerun. The unrelated `gpo-xml-presentation` assertion remains reproducible: it forbids any code tag in the entire Reader, including the existing Policy Analyzer search-key control. The configured/expected XML value blocks remain inert text. Neither that Reader feature nor its test was changed for this fix.
|
||||||
|
|
||||||
|
The final full-suite rerun completed in 97.73 seconds: **1358 passed, one failed, 13 optional tests skipped**, with no timeout or unhandled RPC error. Only the unchanged XML-Reader assertion above fails. Five of the optional PostgreSQL cases are the new read-only parity checks, all separately executed successfully among the 50 targeted tests. The remaining eight are existing opt-in mutating integration tests and were not run against the canonical populated database. Local-only and design checks were run separately and passed because the full-suite command stops at the unrelated failed assertion.
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Read-only reconciliation. Every live request is scoped to the explicitly authorized Dummy project.
|
||||||
|
import { createServer } from 'vite';
|
||||||
|
|
||||||
|
const projectId = 'project-7cbcb7ad';
|
||||||
|
const base = 'http://127.0.0.1:4173/api/v1';
|
||||||
|
const requests = [];
|
||||||
|
async function get(path) {
|
||||||
|
requests.push(path);
|
||||||
|
const response = await fetch(base + path, { signal: AbortSignal.timeout(60000) });
|
||||||
|
if (!response.ok) throw new Error(`Read failed: ${response.status} ${path}`);
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
const project = await get(`/projects/${projectId}`);
|
||||||
|
if (project.name !== 'Dummy') throw new Error('Refusing a project other than Dummy');
|
||||||
|
const [evaluations, overview, resultRows, observations] = await Promise.all([
|
||||||
|
get(`/projects/${projectId}/policy-evaluations`),
|
||||||
|
get(`/projects/${projectId}/policy-analysis-overview`),
|
||||||
|
get(`/results?projectId=${projectId}&sourceTool=GPOs&limit=10000`),
|
||||||
|
get(`/results?projectId=${projectId}&sourceKind=policy-observation&audience=internal-analysis&limit=10000`)
|
||||||
|
]);
|
||||||
|
if (resultRows.total !== resultRows.items.length || observations.total !== observations.items.length) throw new Error('Incomplete page');
|
||||||
|
const counts = (items, key) => Object.fromEntries([...Map.groupBy(items, key)].map(([value, group]) => [value, group.length]));
|
||||||
|
const vite = await createServer({ configFile: false, appType: 'custom', server: { middlewareMode: true, hmr: false, watch: null } });
|
||||||
|
try {
|
||||||
|
const { currentPolicyEvaluationsForWork } = await vite.ssrLoadModule('/src/features/project-import/load-canonical-project-workspace.ts');
|
||||||
|
const { appendPolicyObservationsToDataset } = await vite.ssrLoadModule('/src/features/project-import/policy-observations-to-dataset.ts');
|
||||||
|
const { createPolicyEvaluationUnifiedResult } = await vite.ssrLoadModule('/src/domain/unified-result.ts');
|
||||||
|
const { genericProfile } = await vite.ssrLoadModule('/src/profiles/generic/generic-profile.ts');
|
||||||
|
const { filterAndSortRecords } = await vite.ssrLoadModule('/src/features/search-filter/filter-records.ts');
|
||||||
|
const workRuns = currentPolicyEvaluationsForWork(evaluations.records,
|
||||||
|
new Set(observations.items.map(item => item.sourceResultId)), new Set(overview.current.evaluationRunIds));
|
||||||
|
const dataset = appendPolicyObservationsToDataset(null, 'Dummy', observations.items, workRuns);
|
||||||
|
const workRecords = dataset.sheets[0].analysis.records;
|
||||||
|
const field = (record, label) => record.fields.find(item => item.label === label)?.value ?? '';
|
||||||
|
const dbFails = resultRows.items.filter(item => item.isDeviation);
|
||||||
|
const workFails = workRecords.filter(item => field(item, 'Status') === 'Fail');
|
||||||
|
const filteredWork = filterAndSortRecords(dataset.sheets[0], { activeProfile: genericProfile, dataset,
|
||||||
|
search: '', sortColumn: 'all', sortDirection: 'asc', statusFilter: 'deviation', facetFilters: {}, actionOnly: false, reviews: {} });
|
||||||
|
const hiddenFails = workFails.filter(item => !filteredWork.includes(item));
|
||||||
|
const failsByIdentity = Map.groupBy(dbFails, item => item.policyIdentityKey);
|
||||||
|
const workFailIds = new Set(workFails.map(item => field(item, 'Technical Identity')));
|
||||||
|
const unique = items => [...new Set(items)];
|
||||||
|
const pair = item => JSON.stringify([item.identity?.id ?? item.policyIdentityKey, item.baselineId ?? item.policyBaselineId]);
|
||||||
|
const newFails = workRuns.flatMap(record => record.results.filter(item => createPolicyEvaluationUnifiedResult(record.run, item).isDeviation));
|
||||||
|
const oldPairs = new Set(dbFails.map(pair));
|
||||||
|
const newPairs = new Set(newFails.map(pair));
|
||||||
|
const summaryRun = record => ({ id: record.run.id, aggregation: record.run.aggregationRunId,
|
||||||
|
engine: record.run.evaluator.version, completedAt: record.run.completedAt, results: record.results.length,
|
||||||
|
analysisCount: record.run.baselineAnalysisIds.length,
|
||||||
|
packages: unique(record.results.map(item => item.baselineId)).length,
|
||||||
|
failComparisons: record.results.filter(item => createPolicyEvaluationUnifiedResult(record.run, item).isDeviation).length });
|
||||||
|
console.log(JSON.stringify({ capturedAt: new Date().toISOString(), projectId,
|
||||||
|
observationCount: observations.total, observationAssessments: counts(observations.items, item => item.assessmentId),
|
||||||
|
resultDb: { total: resultRows.total, classifications: counts(resultRows.items, item => item.policyClassification),
|
||||||
|
failRows: dbFails.length, failIdentities: failsByIdentity.size,
|
||||||
|
failRuns: counts(dbFails, item => item.evaluationRunId), failAssessments: counts(dbFails, item => item.assessmentId) },
|
||||||
|
work: { rows: workRecords.length, statuses: counts(workRecords, item => field(item, 'Status')),
|
||||||
|
actualDeviationFilterCount: filteredWork.length,
|
||||||
|
actualStatusFilterCounts: Object.fromEntries(['deviation', 'passed', 'not-configured', 'no-baseline-rule', 'not-comparable', 'conflicts', 'duplicates']
|
||||||
|
.map(status => [status, workRecords.filter(record => genericProfile.matchesStatus(record, status)).length])),
|
||||||
|
conflicts: workRecords.filter(item => field(item, 'Conflict')).length,
|
||||||
|
duplicates: workRecords.filter(item => field(item, 'Duplicates')).length,
|
||||||
|
selectedRuns: workRuns.map(summaryRun) },
|
||||||
|
relevantRuns: evaluations.records.filter(record => workRuns.some(current => current.run.aggregationRunId === record.run.aggregationRunId)).map(summaryRun),
|
||||||
|
reconciliation: { missingFromWork: unique(dbFails.filter(item => !workFailIds.has(item.policyIdentityKey)).map(item => item.policyIdentityKey)),
|
||||||
|
missingFromResultDb: [...workFailIds].filter(id => !failsByIdentity.has(id)),
|
||||||
|
uniqueOldFailPairs: oldPairs.size, uniqueNewFailPairs: newPairs.size,
|
||||||
|
excessOldFailComparisons: dbFails.length - oldPairs.size,
|
||||||
|
oldOnlyPairs: [...oldPairs].filter(key => !newPairs.has(key)), newOnlyPairs: [...newPairs].filter(key => !oldPairs.has(key)),
|
||||||
|
hiddenFailSettings: hiddenFails.map(record => ({ title: record.title, status: field(record, 'Status'), configured: field(record, 'Configured Value'),
|
||||||
|
identity: field(record, 'Technical Identity'),
|
||||||
|
comparisons: newFails.filter(item => item.identity.id === field(record, 'Technical Identity')).map(item => ({
|
||||||
|
baseline: item.baselineLabel, customerState: item.customerState, classification: item.classification,
|
||||||
|
baselineValue: item.comparator.baseline, decision: item.baselineVariantAssessment?.decision,
|
||||||
|
variants: item.baselineVariantAssessment?.variants.map(variant => ({ expected: variant.comparator.baseline, relation: variant.comparator.relation })) })) })),
|
||||||
|
settings: workFails.map(record => {
|
||||||
|
const identity = field(record, 'Technical Identity');
|
||||||
|
const rows = failsByIdentity.get(identity) ?? [];
|
||||||
|
const comparisons = evaluations.records.flatMap(evaluation => evaluation.results
|
||||||
|
.filter(item => rows.some(row => row.sourceResultId === item.id)));
|
||||||
|
const combinations = Map.groupBy(comparisons, item => item.baselineId);
|
||||||
|
return { title: record.title, identity, resultDbRows: rows.length,
|
||||||
|
distinctPackages: unique(rows.map(item => item.policyBaselineId)).length,
|
||||||
|
repeatedPackages: [...combinations].filter(([, items]) => items.length > 1).map(([, items]) => ({
|
||||||
|
baseline: items[0].baselineLabel, comparisons: items.length,
|
||||||
|
analysisVersions: unique(items.map(item => item.baselineAnalysisId)).length })) };
|
||||||
|
}) }, requests }, null, 2));
|
||||||
|
} finally { await vite.close(); }
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Abnahme: aktueller Work-Ansichtsexport
|
||||||
|
|
||||||
|
Stand: 18.09.2026. Ausschließlich der ausdrücklich freigegebene **Dummy** wurde
|
||||||
|
über GET-Aufrufe gelesen. Keine Änderung an Kundendaten, GPOs, Reviews,
|
||||||
|
Auswertungen oder Datenbank. Keine Dummy-Inhalte als Testfixtures gespeichert.
|
||||||
|
|
||||||
|
## Umsetzung
|
||||||
|
|
||||||
|
- CSV, MD und HTML verwenden dieselbe eingefrorene Ansicht und Sortierung.
|
||||||
|
- Genau zwei Inhaltsspalten: **Soll-Zustand** und **Abweichungen**.
|
||||||
|
- Soll-Zustand enthält Regel, erwartete Werte, native Behebung, Prüfhinweise und
|
||||||
|
Scope. Abweichungen enthält tatsächlichen Status, Werte, Quellen und Results.
|
||||||
|
- Native GPO-Anweisungen, Backup-Identitäten, Reihenfolge innerhalb der Quelle,
|
||||||
|
Baseline-Varianten und Typinformation bleiben nachvollziehbar. Kein erfundener
|
||||||
|
einheitlicher Sollwert, keine Aussage zur wirksamen Endpoint-Konfiguration.
|
||||||
|
- Nicht konfigurierte oder nicht vergleichbare Empfehlungen bleiben neutral.
|
||||||
|
Review „Done“ und Risikoakzeptanz ersetzen keinen Rescan-Nachweis.
|
||||||
|
- Fehlende Belege und fehlende separate Lösungstexte werden benannt. Bei Maester
|
||||||
|
bleiben eingebettete Remediation-Texte vollständig erhalten.
|
||||||
|
- HTML: offline, skriptfrei, eingebettetes Inter, Umbruch statt Abschneiden.
|
||||||
|
- CSV: genau zwei Spalten; große Inhalte bleiben durch beschriftete
|
||||||
|
Fortsetzungszeilen unter derselben Finding-ID vollständig erhalten.
|
||||||
|
|
||||||
|
## Vollständigkeitsabgleich
|
||||||
|
|
||||||
|
| Dummy-Auswahl | Work-Treffer | Results-Zeilen | Geprüfte Results-Zellen | Results-Textabschnitte | GPO-Observations |
|
||||||
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||||
|
| Deviation | 477 | 7.599 | 20.434 | 395 | 16 |
|
||||||
|
| All | 1.833 | 9.917 | 27.059 | 708 | 51 |
|
||||||
|
|
||||||
|
Die geladenen Work-Daten enthalten 119 Purple-Knight-, 411 CIS-CAT-, 318
|
||||||
|
Maester-, 37 PingCastle- und 948 GPO-Einträge. Dies ist der gesamte Dummy-Scope,
|
||||||
|
nicht die Zusage identischer Zahlen für einen einzelnen Timeslot oder Filter.
|
||||||
|
|
||||||
|
Jede exportierte Finding-ID und ihre Reihenfolge wurden gegen die Work-Auswahl
|
||||||
|
geprüft. Jede verknüpfte Result-Zeile und Zelle sowie alle Results-Textabschnitte
|
||||||
|
und GPO-Quellen wurden abgeglichen. Die öffentliche Baseline-Dokumentation wurde
|
||||||
|
über denselben Workspace-Loader wie in Work berücksichtigt.
|
||||||
|
|
||||||
|
Keine fehlenden Result-Referenzen im geprüften Dummy. CSV wurde wieder eingelesen
|
||||||
|
und zellengenau abgeglichen: keine Datenverluste, keine Zellen über 32.767 Zeichen.
|
||||||
|
Zehn zusätzliche Fortsetzungszeilen für Deviation, elf für All; sie zählen nicht
|
||||||
|
als zusätzliche Findings. HTML und MD behalten eine Einheit pro Work-Eintrag.
|
||||||
|
|
||||||
|
## Technische Abnahme
|
||||||
|
|
||||||
|
53 fokussierte Export-/Results-Tests bestanden. Nach finaler Schriftintegration
|
||||||
|
nochmals 16 neue Export-/UI-Tests bestanden. Enthalten sind Filter, Suche,
|
||||||
|
Sortierung, 90 Treffer unabhängig vom Viewport, ignorierte Fremdmarkierungen,
|
||||||
|
aktives My Workspace, Nulltreffer, Results, Quellattribution, neutrale GPO-Urteile,
|
||||||
|
Review/Rescan-Trennung, CSV-Langzellen, Teilbelege, inert gerendertes Markup und
|
||||||
|
Fehler ohne Teil-Download.
|
||||||
|
|
||||||
|
Gesamtsuite: 1.412 bestanden, 13 optionale PostgreSQL-Tests übersprungen;
|
||||||
|
ein bestehender, unabhängiger Fehler in `gpo-xml-presentation.test.tsx:70`
|
||||||
|
(globale Annahme „kein code-Element“ im Reader). Unverändert offengehalten.
|
||||||
|
|
||||||
|
Client-/Server-Build, Local-only- und Designprüfung bestanden. App HTTP 200,
|
||||||
|
Backend `ok`. Synthetische Browserprüfung bei normaler Breite und 390 px:
|
||||||
|
kein horizontaler Überhang, vollständiges Dokumentende sichtbar.
|
||||||
|
|
||||||
|
Reproduzierbarer, lesender Opt-in-Abgleich:
|
||||||
|
`node outputs/dummy-work-export-audit.mjs --verify`.
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Opt-in, read-only check of the explicitly authorized Dummy workspace.
|
||||||
|
// No source content is persisted; all HTTP calls are GET and project-scoped.
|
||||||
|
import { createServer } from 'vite';
|
||||||
|
const projectId = 'project-7cbcb7ad';
|
||||||
|
const base = 'http://127.0.0.1:4173/api/v1';
|
||||||
|
const get = async path => {
|
||||||
|
const response = await fetch(base + path);
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
if ((await get(`/projects/${projectId}`)).name !== 'Dummy') throw new Error('Not Dummy');
|
||||||
|
const assessments = await get(`/projects/${projectId}/assessments`);
|
||||||
|
const allowed = new Set(assessments.map(item => item.id));
|
||||||
|
const reader = {
|
||||||
|
listProjectAssessments: async id => { if (id !== projectId) throw new Error('Wrong scope'); return assessments; },
|
||||||
|
loadAssessmentModel: async id => {
|
||||||
|
if (!allowed.has(id)) throw new Error('Wrong assessment');
|
||||||
|
const model = await get(`/assessments/${encodeURIComponent(id)}/model`);
|
||||||
|
if (model.project.id !== projectId) throw new Error('Wrong model');
|
||||||
|
return model;
|
||||||
|
},
|
||||||
|
queryUnifiedResults: async query => {
|
||||||
|
if (query.projectId !== projectId) throw new Error('Wrong result scope');
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
for (const [key, value] of Object.entries(query)) if (value !== undefined) params.set(key, Array.isArray(value) ? value.join(',') : String(value));
|
||||||
|
return get(`/results?${params}`);
|
||||||
|
},
|
||||||
|
listPolicyEvaluations: () => get(`/projects/${projectId}/policy-evaluations`),
|
||||||
|
getPolicyAnalysisOverview: () => get(`/projects/${projectId}/policy-analysis-overview`),
|
||||||
|
// Public reference data, only IDs actually referenced by Dummy's evaluations.
|
||||||
|
listMicrosoftSecurityBaselineAnalyses: id => get(`/reference-data/microsoft-security-baselines/${encodeURIComponent(id)}/analysis`)
|
||||||
|
};
|
||||||
|
const vite = await createServer({ configFile: false, appType: 'custom', server: { middlewareMode: true, hmr: false, watch: null } });
|
||||||
|
try {
|
||||||
|
const { loadCanonicalProjectWorkspace } = await vite.ssrLoadModule('/src/features/project-import/load-canonical-project-workspace.ts');
|
||||||
|
const { toolIdForRecord } = await vite.ssrLoadModule('/src/importers/status-normalization.ts');
|
||||||
|
const { dataset } = await loadCanonicalProjectWorkspace(reader, projectId);
|
||||||
|
const sheet = dataset.sheets.find(item => item.isProjectSheet) ?? dataset.sheets[0];
|
||||||
|
const groups = Map.groupBy(sheet.analysis.records, record => toolIdForRecord(record, sheet.toolId ?? dataset.profileId));
|
||||||
|
console.log(JSON.stringify({ assessments: assessments.length, sheets: dataset.sheets.length,
|
||||||
|
groups: [...groups].map(([tool, records]) => ({ tool, count: records.length,
|
||||||
|
fields: process.argv.includes('--inspect') ? [...new Set(records.flatMap(record => record.fields.filter(field => field.value).map(field => field.label)))] : undefined,
|
||||||
|
sample: process.argv.includes('--inspect') && tool !== 'group-policy' ? records.find(record => record.fields.some(field => /^(Status|Result)$/.test(field.label) && /fail/i.test(field.value)))?.fields.filter(field => /^(Description|Remediation|Advised solution|Result|Result message|Details|Results|Notes)$/.test(field.label) && field.value).map(field => ({ label: field.label, value: field.value.slice(0, 1300) })) : undefined
|
||||||
|
})) }, null, 2));
|
||||||
|
if (process.argv.includes('--verify')) {
|
||||||
|
const { verify } = await import('./dummy-work-export-verification.mjs');
|
||||||
|
await verify(vite, dataset, sheet);
|
||||||
|
}
|
||||||
|
} finally { await vite.close(); }
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
|
||||||
|
export async function verify(vite, dataset, sheet) {
|
||||||
|
const exports = await vite.ssrLoadModule('/src/exports/work-view-export.ts');
|
||||||
|
const { genericProfile } = await vite.ssrLoadModule('/src/profiles/generic/generic-profile.ts');
|
||||||
|
const { filterAndSortRecords } = await vite.ssrLoadModule('/src/features/search-filter/filter-records.ts');
|
||||||
|
const { recordResultSheets } = await vite.ssrLoadModule('/src/features/linked-results/record-results.ts');
|
||||||
|
const { resultSectionsFor } = await vite.ssrLoadModule('/src/features/linked-results/result-message.ts');
|
||||||
|
const { toolIdForRecord } = await vite.ssrLoadModule('/src/importers/status-normalization.ts');
|
||||||
|
const options = { dataset, activeProfile: genericProfile, search: '', statusFilter: 'deviation', facetFilters: {},
|
||||||
|
sortColumn: 'all', sortDirection: 'asc', actionOnly: false, reviews: {} };
|
||||||
|
const counts = [];
|
||||||
|
for (const statusFilter of ['deviation', 'all']) {
|
||||||
|
const records = filterAndSortRecords(sheet, { ...options, statusFilter });
|
||||||
|
const report = exports.buildWorkViewExport({ dataset, sheet, records, generatedAt: new Date().toISOString() });
|
||||||
|
assert.deepEqual(report.entries.map(entry => entry.id), records.map(record => record.id));
|
||||||
|
let cells = 0;
|
||||||
|
let sources = 0;
|
||||||
|
let sections = 0;
|
||||||
|
for (const [index, record] of records.entries()) {
|
||||||
|
const entry = report.entries[index];
|
||||||
|
const tool = toolIdForRecord(record, sheet.toolId ?? dataset.profileId);
|
||||||
|
const linked = recordResultSheets(record, dataset.sheets, sheet.id, tool);
|
||||||
|
assert.deepEqual(entry.resultSheetIds, linked.map(item => item.id));
|
||||||
|
assert.equal(entry.resultRowCount, linked.reduce((sum, item) => sum + item.analysis.records.length, 0));
|
||||||
|
const actual = entry.observed.map(block => block.value).join('\n');
|
||||||
|
for (const item of linked) for (const row of item.analysis.records) for (const field of row.fields) {
|
||||||
|
assert.ok(actual.includes(`${field.label}: ${field.value}`), 'Lost result cell');
|
||||||
|
cells++;
|
||||||
|
}
|
||||||
|
for (const section of resultSectionsFor(record, tool)) {
|
||||||
|
assert.ok(entry.observed.some(block => block.value === section.value), 'Lost result section');
|
||||||
|
sections++;
|
||||||
|
}
|
||||||
|
for (const source of record.policySourceEvidence ?? []) {
|
||||||
|
assert.ok(actual.includes(source.id), 'Lost GPO observation');
|
||||||
|
assert.ok(actual.includes(source.sourceReference), 'Lost GPO evidence path');
|
||||||
|
sources++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const csv = exports.renderWorkViewCsv(report);
|
||||||
|
const html = exports.renderWorkViewHtml(report);
|
||||||
|
const md = exports.renderWorkViewMarkdown(report);
|
||||||
|
const { read, utils } = await import('xlsx');
|
||||||
|
// Decode the UTF-8 BOM before passing a JS string to SheetJS (Excel handles it).
|
||||||
|
const book = read(csv.replace(/^\ufeff/, ''), { type: 'string', raw: true, FS: ';' });
|
||||||
|
const rows = utils.sheet_to_json(book.Sheets[book.SheetNames[0]], { header: 1, raw: true });
|
||||||
|
const expectedCsvRows = exports.workViewCsvRows(report);
|
||||||
|
assert.equal(rows.length, expectedCsvRows.length, 'CSV row count');
|
||||||
|
const differences = [];
|
||||||
|
rows.forEach((row, index) => row.forEach((cell, col) => {
|
||||||
|
const expected = expectedCsvRows[index][col];
|
||||||
|
if (cell !== expected) {
|
||||||
|
let first = 0;
|
||||||
|
while (first < Math.min(cell.length, expected.length) && cell[first] === expected[first]) first++;
|
||||||
|
differences.push({ row: index, column: col, actualLength: cell.length, expectedLength: expected.length,
|
||||||
|
firstDifference: first, actualChar: cell.charCodeAt(first), expectedChar: expected.charCodeAt(first) });
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
if (differences.length) { console.log(JSON.stringify({ csvDifferences: differences.slice(0, 5) })); throw new Error('CSV content round-trip differs'); }
|
||||||
|
assert.ok(rows.every(row => row.length === 2), 'Not exactly two CSV columns');
|
||||||
|
assert.equal((html.match(/<article class="finding">/g) ?? []).length, records.length);
|
||||||
|
assert.equal((md.match(/^\| /gm) ?? []).length, records.length + 2);
|
||||||
|
counts.push({ statusFilter, records: records.length, csvContinuationRows: rows.length - records.length - 1, resultRows: report.entries.reduce((sum, item) => sum + item.resultRowCount, 0),
|
||||||
|
checkedResultCells: cells, resultSections: sections, gpoObservations: sources,
|
||||||
|
missingResultWarnings: report.entries.filter(item => item.observed.some(block => /nicht geladen/.test(block.value))).length,
|
||||||
|
oversizedCsvCells: rows.flat().filter(cell => String(cell).length > 32767).length,
|
||||||
|
bytes: { csv: Buffer.byteLength(csv), html: Buffer.byteLength(html), md: Buffer.byteLength(md) } });
|
||||||
|
}
|
||||||
|
console.log(JSON.stringify({ verification: 'passed', counts }, null, 2));
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "assessor",
|
"name": "assessor",
|
||||||
"version": "0.1.0",
|
"version": "2.25.6",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "assessor",
|
"name": "assessor",
|
||||||
"version": "0.1.0",
|
"version": "2.25.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/inter": "^5.3.0",
|
"@fontsource-variable/inter": "^5.3.0",
|
||||||
"jsdom": "^29.1.1",
|
"jsdom": "^29.1.1",
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
"@csstools/css-tokenizer": "^4.0.0"
|
"@csstools/css-tokenizer": "^4.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.2 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@asamuzakjp/dom-selector": {
|
"node_modules/@asamuzakjp/dom-selector": {
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
"is-potential-custom-element-name": "^1.0.1"
|
"is-potential-custom-element-name": "^1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.2 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@asamuzakjp/generational-cache": {
|
"node_modules/@asamuzakjp/generational-cache": {
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
"integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
|
"integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.2 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@asamuzakjp/nwsapi": {
|
"node_modules/@asamuzakjp/nwsapi": {
|
||||||
|
|
@ -951,7 +951,7 @@
|
||||||
"integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==",
|
"integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.2 || >=24.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@noble/hashes": "^1.8.0 || ^2.0.0"
|
"@noble/hashes": "^1.8.0 || ^2.0.0"
|
||||||
|
|
@ -1571,7 +1571,7 @@
|
||||||
"react-refresh": "^0.18.0"
|
"react-refresh": "^0.18.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||||
|
|
@ -1896,7 +1896,7 @@
|
||||||
"whatwg-url": "^16.0.0"
|
"whatwg-url": "^16.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.2 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
|
|
@ -2092,7 +2092,7 @@
|
||||||
"@exodus/bytes": "^1.6.0"
|
"@exodus/bytes": "^1.6.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.2 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-potential-custom-element-name": {
|
"node_modules/is-potential-custom-element-name": {
|
||||||
|
|
@ -2137,7 +2137,7 @@
|
||||||
"xml-name-validator": "^5.0.0"
|
"xml-name-validator": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.13.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.13.5 || >=24.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"canvas": "^3.0.0"
|
"canvas": "^3.0.0"
|
||||||
|
|
@ -2839,7 +2839,7 @@
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.2"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
|
@ -2848,7 +2848,7 @@
|
||||||
"fsevents": "~2.3.3"
|
"fsevents": "~2.3.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/node": "^20.19.0 || >=22.12.0",
|
"@types/node": "^20.19.0 || >=22.12.2",
|
||||||
"jiti": ">=1.21.0",
|
"jiti": ">=1.21.0",
|
||||||
"less": "^4.0.0",
|
"less": "^4.0.0",
|
||||||
"lightningcss": "^1.21.0",
|
"lightningcss": "^1.21.0",
|
||||||
|
|
@ -3033,7 +3033,7 @@
|
||||||
"webidl-conversions": "^8.0.1"
|
"webidl-conversions": "^8.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.2 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/why-is-node-running": {
|
"node_modules/why-is-node-running": {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "assessor",
|
"name": "assessor",
|
||||||
"version": "0.1.0",
|
"version": "2.25.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node scripts/dev-local.mjs",
|
"dev": "node scripts/dev-local.mjs",
|
||||||
"dev:client": "vite --config vite.client.config.ts --host 127.0.0.1",
|
"dev:client": "vite --config vite.client.config.ts --host 127.0.0.1",
|
||||||
|
"prebuild": "npm run test:imports",
|
||||||
"build": "npm run build:client && npm run build:server",
|
"build": "npm run build:client && npm run build:server",
|
||||||
"build:client": "tsc -p tsconfig.app.json --noEmit && vite build --config vite.client.config.ts",
|
"build:client": "tsc -p tsconfig.app.json --noEmit && vite build --config vite.client.config.ts",
|
||||||
"build:server": "tsc -p tsconfig.server.json --noEmit && vite build --config vite.server.config.ts",
|
"build:server": "tsc -p tsconfig.server.json --noEmit && vite build --config vite.server.config.ts",
|
||||||
|
|
@ -15,7 +16,8 @@
|
||||||
"db:down": "docker compose --env-file .env.postgres -f docker-compose.postgres.yml down",
|
"db:down": "docker compose --env-file .env.postgres -f docker-compose.postgres.yml down",
|
||||||
"db:logs": "docker compose --env-file .env.postgres -f docker-compose.postgres.yml logs -f postgres",
|
"db:logs": "docker compose --env-file .env.postgres -f docker-compose.postgres.yml logs -f postgres",
|
||||||
"test:postgres": "vitest run --config vitest.config.ts tests/postgres/postgres-repository.integration.test.ts && vitest run --config vitest.config.ts tests/postgres/gpo-backup-management.integration.test.ts tests/postgres/policy-analysis.integration.test.ts",
|
"test:postgres": "vitest run --config vitest.config.ts tests/postgres/postgres-repository.integration.test.ts && vitest run --config vitest.config.ts tests/postgres/gpo-backup-management.integration.test.ts tests/postgres/policy-analysis.integration.test.ts",
|
||||||
"test": "vitest run --config vitest.config.ts && node tests/local-only-check.mjs && node tests/design-language-check.mjs",
|
"test:imports": "node scripts/check-import-contracts.mjs",
|
||||||
|
"test": "npm run test:imports && vitest run --config vitest.config.ts && node tests/local-only-check.mjs && node tests/design-language-check.mjs",
|
||||||
"test:security": "node tests/local-only-check.mjs",
|
"test:security": "node tests/local-only-check.mjs",
|
||||||
"test:design": "node tests/design-language-check.mjs",
|
"test:design": "node tests/design-language-check.mjs",
|
||||||
"test:watch": "vitest --config vitest.config.ts"
|
"test:watch": "vitest --config vitest.config.ts"
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,120 @@
|
||||||
|
{
|
||||||
|
"surface": "report",
|
||||||
|
"manifest": {
|
||||||
|
"version": 1,
|
||||||
|
"surface": "report",
|
||||||
|
"title": "ASSESSOR – Ist-Zustand",
|
||||||
|
"description": "High-Level-Ist-Zustand des aktuellen ASSESSOR aus Quellcode und Projektdokumentation.",
|
||||||
|
"generatedAt": "2026-09-09T12:00:00+02:00",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "# ASSESSOR – Ist-Zustand"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "executive-summary",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Executive Summary\n\n- ASSESSOR ist aktuell eine lokale Browseranwendung mit Node-Backend und PostgreSQL; Oberfläche und API laufen same-origin.\n- Standardberichte werden aus fünf Formatfamilien verarbeitet: CSV/Text, HTML, Excel, JSON und XML.\n- Eigene Profile bestehen für Purple Knight, PingCastle, CIS-CAT Pro und Maester; unbekannte strukturierte Quellen laufen über das generische Profil.\n- GPO-Backups und Microsoft Security Baselines werden über einen separaten spezialisierten Analysepfad verarbeitet.\n- Sichtbares Ergebnis sind vereinheitlichte Findings, Evidenzen, Umgebungsdaten und Policy-Vergleiche in Client Info, Work, Result DB und Pathfinder."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inputs",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Unterstützte Eingaben\n\n- **CSV/Text:** .csv und .txt; Komma, Semikolon, Tabulator und Pipe.\n- **HTML:** .html und .htm; Tabellen und erkannte statische Berichtsdaten.\n- **Excel:** .xlsx und .xls; Arbeitsblätter werden übernommen, Formeln nicht ausgeführt.\n- **JSON:** .json; Objekte und Arrays werden tabellarisch strukturiert.\n- **XML:** .xml; hierarchische Strukturen ohne aktive externe XML-Konstrukte.\n- **Scannerprofile:** Purple Knight, PingCastle, CIS-CAT Pro, Maester und Generic.\n- **GPO/Baseline:** GPO-Backup als ZIP oder unterstützte Ordnerauswahl; Microsoft Security Baseline als ZIP.\n- **GPO-Artefakte:** Registry.pol, GptTmpl.inf, audit.csv sowie ausgewählte Group Policy Preferences für Registry, Services, Laufwerkszuordnungen und lokale Benutzer/Gruppen."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scanner-specifics",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Scannerbezogene Besonderheiten\n\n- **Purple Knight:** HTML und tabellarische Exporte; Findings, Detailtabellen, Zusammenfassungen und ausgewählte Umgebungskennzahlen.\n- **PingCastle:** HTML liefert Risk Rules und Details; XML liefert eigenständige strukturierte Berichtsdaten und Kontext, aber nicht die vollständige HTML-Findingansicht.\n- **CIS-CAT Pro:** HTML mit Benchmark-Ergebnissen, Regelstatus, Evidenztabellen, Summary-Abgleich und System-/Benchmarkkontext.\n- **Maester:** HTML mit sicher lesbarem statischem Ergebnisobjekt sowie tabellarische/Excel-Ausgaben; Tests, Status, Ergebnistext und Berichtskontext.\n- **Generic:** strukturierte Tabellen/Objekte; fachliche Feldzuordnung nur bei ausreichender Erkennung oder Nutzerbestätigung."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flow",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Datenstrecke: sichtbarer Start- bis Endpunkt\n\n1. **Start:** Berichtdateien im Data Hub auswählen oder GPO-/Baseline-Import starten.\n2. **Eingangskontrolle:** Format, Signatur, Ressourcenlimits und unsichere Inhalte lokal prüfen.\n3. **Strukturierung:** Formatadapter erzeugt ein gemeinsames Zwischenmodell.\n4. **Erkennung:** Tool, Ergebnistabelle und fachliche Felder mit Konfidenz bestimmen; unsichere Zuordnungen bestätigen lassen.\n5. **Normalisierung:** Status, Severity, Titel, Kategorien, Evidenz, Referenzen und Kontext in das kanonische Modell überführen; Originalwerte und Provenienz erhalten.\n6. **Persistenz:** Bestätigten Import atomar als Projekt, Assessment, Scanlauf, Findings, Evidenz, Diagnosen und Historie in PostgreSQL speichern.\n7. **Endpunkt:** Gewählten Client/Timeslot in Client Info, Work, Result DB und Pathfinder anzeigen."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "capabilities",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Weitere Fähigkeiten\n\n- **Datahub:** Clients/Projekte, Mehrdatei-Vorschau, Tool-/Formatprüfung, Importdiagnosen, Dublettenhinweise, Reimport/Ersatzläufe, Historie und Timeslots.\n- **Client Info:** Umgebungsfakten, Overall Score, Security Domains, deviation-basierte Priority Findings, Assessment Gaps, Statuskontext und Source Overview.\n- **Work:** Reports als Standardansicht sowie Matrix, Grid und Raw Data; Suche, Sortierung und dynamische Filter.\n- **Finding-Bearbeitung:** Bookmark, Reviewstatus, Priorität, Owner/Team, Fälligkeit, Voraussetzungen, Rollen, Next Step, Evidenzkriterien, Kommentar und Reportmarkierungen.\n- **Vergleich:** Neue, fortbestehende, behobene und wiederkehrende Befunde über Scanläufe/Timeslots.\n- **Result DB:** Vereinheitlichte Scanner-, Kontext- und Policy-Ergebnisse mit Facetten, Details und JSONL-Export.\n- **Pathfinder:** Deviation-basierter Graph mit Kategorie-, Asset-, MITRE ATT&CK- und D3FEND-Beziehungen.\n- **GPO-Analyse:** Baselinevergleich mit Misconfigured, Match/Passed, Not configured, Conflicts, Duplicates und Review required.\n- **Exporte:** Sichere CSV, ASSESSOR-Snapshot als JSON, Result DB als JSONL und HTML-basierter Aktions-/Dashboardexport.\n- **Betrieb:** Backend-Health, DB-Strukturansicht, versionierte Migrationen und technische Verarbeitungsdiagnosen."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "security",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Sicherheit und Betriebsgrenzen\n\n- Lokale Verarbeitung ohne externe Uploads, Telemetrie, CDN-Abhängigkeiten oder automatische externe APIs.\n- Importiertes HTML wird nicht ausgeführt; aktive Inhalte werden entfernt.\n- XML mit DTD/ENTITY/SYSTEM/PUBLIC, Excel mit VBA oder verschlüsselten Archiven und nicht statisch lesbare Maester-Ausdrücke werden abgelehnt.\n- Standardgrenzen: 100 MiB je Datei, 140 MiB je Request und 50 Dateien je Batch; zusätzlich Zeit-, Speicher-, Mengen- und Parallelitätsgrenzen.\n- PostgreSQL ist die kanonische lokale Datenhaltung; kein stiller Fallback bei konfigurierter, aber nicht erreichbarer Datenbank.\n- Noch kein freigegebener Mehrbenutzer-Firmenbetrieb: HTTPS, SSO, Rollen und Autorisierung gehören zu Phase 14."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "maturity",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Reifegrad des Umsetzungsplans\n\n- **15 Phasen/Teilphasen abgeschlossen:** Phase 0 bis 13A.\n- **2 Teilphasen in Arbeit:** 13B GPO/Baseline-Analyse und 13C Scanner Context/Environment Inventory.\n- **1 Phase offen:** Phase 14 Mehrbenutzer- und Firmenbetrieb."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "phase-chart-block",
|
||||||
|
"type": "chart",
|
||||||
|
"chartId": "phase-status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "next-steps",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Empfohlene nächste Schritte\n\n- Phase 13B gegen die definierten GPO-/Baseline-Akzeptanzkriterien abschließen.\n- Phase 13C für alle unterstützten Scannerprofile vervollständigen.\n- Vor Firmenbetrieb Phase 14 mit HTTPS, SSO, Rollen, Autorisierung, Betriebsüberwachung sowie Backup/Restore umsetzen.\n- Format- und Profilkompatibilität weiter mit synthetischen Referenzberichten und Vollständigkeitsabgleichen absichern."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "questions",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Offene Fragen\n\n- Welche Scanner oder Reportvarianten sollen als Nächstes ein eigenes Profil erhalten?\n- Welche zusätzlichen GPO-Artefaktarten sind fachlich zwingend?\n- Wird zusätzlich eine Capability-Matrix „Format × Tool × sichtbare Felder“ benötigt?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "caveats",
|
||||||
|
"type": "markdown",
|
||||||
|
"body": "## Caveats\n\n- Stand: aktueller Workspace am 09.09.2026; Basis sind Quellcode, Sicherheitsdokumentation und Umsetzungsplan, keine Kundendaten.\n- „Parsebar“ heißt strukturell lesbar; vollständige Findings setzen ein passendes Profil oder bestätigtes Mapping voraus.\n- GPO/Baseline und Environment sind nutzbar, die zugehörigen Phasen 13B und 13C bleiben aber in Arbeit.\n- High-Level-Ist-Zustand, kein Feld-für-Feld- oder Parser-Testnachweis."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"charts": [
|
||||||
|
{
|
||||||
|
"id": "phase-status",
|
||||||
|
"title": "Umsetzungsstatus nach Phasen",
|
||||||
|
"type": "bar",
|
||||||
|
"dataset": "phase_status",
|
||||||
|
"encodings": {
|
||||||
|
"x": { "field": "status", "type": "nominal", "title": "Status" },
|
||||||
|
"y": { "field": "anzahl", "type": "quantitative", "title": "Anzahl Phasen/Teilphasen" }
|
||||||
|
},
|
||||||
|
"options": { "orientation": "vertical", "grouping": "grouped", "showLegend": false },
|
||||||
|
"source": {
|
||||||
|
"id": "phase-status-values",
|
||||||
|
"label": "Aus dem ASSESSOR-Umsetzungsplan abgeleitete Statuszählung",
|
||||||
|
"query": {
|
||||||
|
"engine": "static-sql",
|
||||||
|
"description": "Zählt die dokumentierten Phasen und Teilphasen nach Status; Grundlage: docs/IMPLEMENTATION_PLAN.md.",
|
||||||
|
"sql": "SELECT status, anzahl, umfang FROM (VALUES ('Abgeschlossen', 15, 'Phase 0–13A'), ('In Arbeit', 2, 'Phase 13B und 13C'), ('Offen', 1, 'Phase 14')) AS phase_status(status, anzahl, umfang)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
{ "id": "implementation-plan", "label": "ASSESSOR Umsetzungsplan", "path": "docs/IMPLEMENTATION_PLAN.md" },
|
||||||
|
{ "id": "import-code", "label": "Importengine und Toolprofile", "path": "src/importers; src/profiles" },
|
||||||
|
{ "id": "app-code", "label": "Anwendungsbereiche und sichtbare Datenprojektionen", "path": "src/app; src/features" },
|
||||||
|
{ "id": "backend-code", "label": "Backend, GPO-Analyse und Persistenz", "path": "server; src/persistence; src/api" },
|
||||||
|
{ "id": "security-docs", "label": "Sicherheitsmodell und Importgrenzen", "path": "SECURITY.md; docs/IMPORT_SECURITY_LIMITS.md" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"snapshot": {
|
||||||
|
"version": 1,
|
||||||
|
"generatedAt": "2026-09-09T12:00:00+02:00",
|
||||||
|
"status": "ready",
|
||||||
|
"datasets": {
|
||||||
|
"phase_status": [
|
||||||
|
{ "status": "Abgeschlossen", "anzahl": 15, "umfang": "Phase 0–13A" },
|
||||||
|
{ "status": "In Arbeit", "anzahl": 2, "umfang": "Phase 13B und 13C" },
|
||||||
|
{ "status": "Offen", "anzahl": 1, "umfang": "Phase 14" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{ "id": "implementation-plan", "label": "ASSESSOR Umsetzungsplan", "path": "docs/IMPLEMENTATION_PLAN.md" },
|
||||||
|
{ "id": "import-code", "label": "Importengine und Toolprofile", "path": "src/importers; src/profiles" },
|
||||||
|
{ "id": "app-code", "label": "Anwendungsbereiche und sichtbare Datenprojektionen", "path": "src/app; src/features" },
|
||||||
|
{ "id": "backend-code", "label": "Backend, GPO-Analyse und Persistenz", "path": "server; src/persistence; src/api" },
|
||||||
|
{ "id": "security-docs", "label": "Sicherheitsmodell und Importgrenzen", "path": "SECURITY.md; docs/IMPORT_SECURITY_LIMITS.md" }
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
|
|
@ -0,0 +1,194 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>ASSESSOR · Management Report · Dummy · 29.09.2026</title>
|
||||||
|
<style>
|
||||||
|
@font-face {
|
||||||
|
font-family: Inter;
|
||||||
|
src: url("assets/inter-latin.woff2") format("woff2");
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--ink: #17171c;
|
||||||
|
--muted: #5c6069;
|
||||||
|
--line: #cfcfd8;
|
||||||
|
--paper: #fff;
|
||||||
|
--surface: #f5f5f7;
|
||||||
|
--navy: #123047;
|
||||||
|
--amber: #e9a63c;
|
||||||
|
--orange: #df752d;
|
||||||
|
--red: #b82446;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: #e7e7eb; color: var(--ink); font: 400 14px/1.46 Inter, Arial, sans-serif; }
|
||||||
|
.page { width: 210mm; min-height: 297mm; margin: 16px auto; padding: 12mm 16mm 11mm; background: var(--paper); display: flex; flex-direction: column; }
|
||||||
|
.mast { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding-bottom: 12px; border-bottom: 2px solid var(--navy); }
|
||||||
|
.brand { display: flex; align-items: center; gap: 9px; color: var(--navy); font-size: 19px; font-weight: 700; letter-spacing: .06em; }
|
||||||
|
.brand img { width: 31px; height: 31px; object-fit: contain; }
|
||||||
|
.mast-note { color: var(--navy); text-align: right; font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; }
|
||||||
|
.kicker { margin: 20px 0 7px; color: var(--orange); font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
h1 { margin: 0; max-width: 590px; color: var(--navy); font-size: 31px; line-height: 1.12; font-weight: 600; }
|
||||||
|
h2 { margin: 0 0 10px; color: var(--navy); font-size: 18px; line-height: 1.25; font-weight: 600; }
|
||||||
|
h3 { margin: 0 0 7px; color: var(--navy); font-size: 16px; line-height: 1.25; font-weight: 600; }
|
||||||
|
p { margin: 0 0 8px; }
|
||||||
|
.subtitle { margin: 10px 0 15px; color: var(--muted); }
|
||||||
|
.report-meta { display: flex; gap: 18px; padding: 8px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); color: var(--muted); font-size: 12px; }
|
||||||
|
.report-meta strong { color: var(--ink); font-weight: 600; }
|
||||||
|
.statement { margin: 19px 0 22px; padding: 15px 18px; border-left: 5px solid var(--red); background: var(--surface); }
|
||||||
|
.statement .label { display: block; margin-bottom: 6px; color: var(--red); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
.statement p { margin: 0; font-size: 17px; line-height: 1.4; }
|
||||||
|
.section { margin-top: 3px; }
|
||||||
|
.risk-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
||||||
|
.risk-table th { padding: 8px 9px; background: var(--navy); color: #fff; font-size: 11px; font-weight: 600; text-align: left; }
|
||||||
|
.risk-table td { padding: 10px 9px; vertical-align: top; border-bottom: 1px solid var(--line); font-size: 13px; }
|
||||||
|
.risk-table td:first-child { color: var(--navy); font-weight: 600; }
|
||||||
|
.risk-table td:last-child { color: var(--muted); }
|
||||||
|
.evidence-visuals { margin-top: 23px; }
|
||||||
|
figure { min-width: 0; margin: 0; padding-top: 12px; border-top: 2px solid var(--navy); }
|
||||||
|
figcaption { color: var(--navy); font-size: 14px; font-weight: 600; }
|
||||||
|
.figure-lead { margin-top: 7px; min-height: 37px; color: var(--muted); font-size: 12px; }
|
||||||
|
.snapshot { display: grid; grid-template-columns: 1fr 1fr; gap: 0; margin: 15px 0 8px; border-top: 5px solid var(--orange); background: var(--surface); }
|
||||||
|
.snapshot div { padding: 11px 9px; min-width: 0; }
|
||||||
|
.snapshot div + div { border-left: 1px solid var(--line); }
|
||||||
|
.snapshot strong { display: block; color: var(--navy); font-size: 19px; font-weight: 600; line-height: 1.25; }
|
||||||
|
.snapshot span { display: block; margin-top: 3px; color: var(--muted); font-size: 11px; }
|
||||||
|
.figure-note { margin-top: 12px; color: var(--muted); font-size: 11px; }
|
||||||
|
.context { margin-top: 21px; padding: 12px 15px; border-left: 3px solid var(--amber); background: var(--surface); }
|
||||||
|
.context h2 { margin-bottom: 5px; font-size: 15px; }
|
||||||
|
.context p { margin: 0; }
|
||||||
|
.foot { margin-top: auto; padding-top: 10px; border-top: 1px solid var(--navy); display: flex; justify-content: space-between; gap: 12px; color: var(--muted); font-size: 10px; }
|
||||||
|
.foot strong { color: var(--navy); font-weight: 600; }
|
||||||
|
.page-two .subtitle { margin-bottom: 24px; }
|
||||||
|
.assessment { display: grid; grid-template-columns: 41mm 1fr; gap: 13px; padding: 16px 0; border-top: 1px solid var(--line); }
|
||||||
|
.assessment:first-of-type { border-top: 2px solid var(--navy); }
|
||||||
|
.assessment .kind { color: var(--orange); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
.assessment h2 { margin-top: 4px; }
|
||||||
|
.assessment p:last-child { margin-bottom: 0; }
|
||||||
|
.assessment .limit { color: var(--muted); }
|
||||||
|
.open-points { margin-top: 20px; padding: 15px 17px; background: var(--surface); border-top: 2px solid var(--navy); }
|
||||||
|
.open-points h2 { margin-bottom: 8px; }
|
||||||
|
.open-points ul { margin: 0; padding-left: 18px; }
|
||||||
|
.open-points li { margin: 0 0 7px; }
|
||||||
|
.open-points li:last-child { margin-bottom: 0; }
|
||||||
|
.scope { margin-top: 20px; padding-top: 12px; border-top: 1px solid var(--line); color: var(--muted); font-size: 12px; }
|
||||||
|
.scope strong { color: var(--ink); font-weight: 600; }
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body { background: var(--paper); }
|
||||||
|
.page { width: auto; min-height: 0; margin: 0; padding: 18px 16px 26px; }
|
||||||
|
.page + .page { border-top: 9px solid var(--surface); }
|
||||||
|
.assessment { grid-template-columns: 1fr; gap: 3px; }
|
||||||
|
.report-meta { flex-wrap: wrap; }
|
||||||
|
h1 { font-size: 27px; }
|
||||||
|
}
|
||||||
|
@page { size: A4; margin: 0; }
|
||||||
|
@media print {
|
||||||
|
body { background: #fff; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
|
||||||
|
.page { margin: 0; break-after: page; }
|
||||||
|
.page:last-child { break-after: auto; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<section class="page" aria-labelledby="report-title">
|
||||||
|
<header class="mast">
|
||||||
|
<div class="brand"><img src="assets/assessor-icon-light-128.png" alt="">ASSESSOR</div>
|
||||||
|
<div class="mast-note">Management Report<br>Vertraulich</div>
|
||||||
|
</header>
|
||||||
|
<p class="kicker">Identität und Wiederanlauf</p>
|
||||||
|
<h1 id="report-title">Sicherheitslage der Identitätsinfrastruktur</h1>
|
||||||
|
<p class="subtitle">Verdichtete Einordnung der im ASSESSOR gespeicherten Ergebnisse für den Dummy-Mandanten.</p>
|
||||||
|
<div class="report-meta">
|
||||||
|
<span><strong>Umgebung:</strong> sevenkingdoms.local</span>
|
||||||
|
<span><strong>Berichtsstand:</strong> 29. September 2026</span>
|
||||||
|
<span><strong>Vergleich:</strong> Erstbewertung</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="statement">
|
||||||
|
<span class="label">Gesamteinordnung</span>
|
||||||
|
<p>Die vorliegenden Ergebnisse zeigen erhebliche Risiken für privilegierte Identitäten und den Wiederanlauf des Active Directory. Eine aktive Kompromittierung ist damit nicht nachgewiesen.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="section" aria-labelledby="risk-title">
|
||||||
|
<h2 id="risk-title">Betriebliche Risikobereiche</h2>
|
||||||
|
<table class="risk-table">
|
||||||
|
<colgroup><col style="width:25%"><col style="width:43%"><col style="width:32%"></colgroup>
|
||||||
|
<thead><tr><th scope="col">Bereich</th><th scope="col">Beobachtung</th><th scope="col">Mögliche Folge</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>Privilegierte Identitäten</td><td>Geänderte Rechte an einem besonders geschützten AD-Objekt, auffällige Admin-Aktivität und Schwächen in der Zertifikatsinfrastruktur.</td><td>Unbefugte Ausweitung von Rechten mit Wirkung auf AD-abhängige Dienste.</td></tr>
|
||||||
|
<tr><td>Wiederanlauf</td><td>Ein Domänencontroller erfasst; als letztes AD-Backup wurde bei der Erhebung der 27. Mai 2026 gemeldet. Ein Wiederherstellungstest ist in den Scandaten nicht belegt.</td><td>Längere Unterbrechung von Anmeldung und Berechtigungsprüfung bei einem AD-Ausfall.</td></tr>
|
||||||
|
<tr><td>Cloud-Identität</td><td>Abweichungen beim Schutz privilegierter Anmeldungen und bei risikobasierten Zugriffsregeln.</td><td>Erhöhte Exposition administrativer Cloud-Zugänge; Umfang nur eingeschränkt beurteilbar.</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="evidence-visuals">
|
||||||
|
<figure aria-labelledby="backup-chart">
|
||||||
|
<figcaption id="backup-chart">AD-Wiederanlauf: erfasster Zustand</figcaption>
|
||||||
|
<p class="figure-lead">Bestandsangaben zum Zeitpunkt der jeweiligen Erhebung.</p>
|
||||||
|
<div class="snapshot" role="img" aria-label="Ein Domänencontroller erfasst; zuletzt gemeldetes AD-Backup vom 27. Mai 2026">
|
||||||
|
<div><strong>1</strong><span>Domänencontroller erfasst</span></div>
|
||||||
|
<div><strong>27.05.2026</strong><span>zuletzt gemeldetes AD-Backup</span></div>
|
||||||
|
</div>
|
||||||
|
<p class="figure-note">Aus diesen Angaben folgt nicht, ob nach der Erhebung weitere Sicherungen erstellt wurden. Ein erfolgreicher Restore ist nicht belegt.</p>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="context" aria-labelledby="context-title">
|
||||||
|
<h2 id="context-title">Betriebliche Einordnung</h2>
|
||||||
|
<p>AD-Anmeldung und Berechtigungen sind unmittelbar relevant. Welche geschäftskritischen Dienste sowie OT-Prozesse davon abhängen, ist in den vorliegenden Daten nicht erfasst.</p>
|
||||||
|
</aside>
|
||||||
|
<footer class="foot"><span><strong>ASSESSOR 2.9.0</strong> · Dummy-Datenbestand · vertraulicher Entwurf</span><span>1 / 2</span></footer>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="page page-two" aria-labelledby="analysis-title">
|
||||||
|
<header class="mast">
|
||||||
|
<div class="brand"><img src="assets/assessor-icon-light-128.png" alt="">ASSESSOR</div>
|
||||||
|
<div class="mast-note">Management Report<br>Vertraulich</div>
|
||||||
|
</header>
|
||||||
|
<p class="kicker">Begründung und Aussagegrenzen</p>
|
||||||
|
<h1 id="analysis-title">Einordnung der wesentlichen Risiken</h1>
|
||||||
|
<p class="subtitle">Die Szenarien beschreiben mögliche betriebliche Folgen. Die technische Ausnutzung und konkrete Dienstauswirkungen wurden nicht nachgewiesen.</p>
|
||||||
|
|
||||||
|
<article class="assessment">
|
||||||
|
<div><span class="kind">Integrität</span><h2>Privilegierte AD-Rechte</h2></div>
|
||||||
|
<div>
|
||||||
|
<p>Eine Änderung an den Rechten eines besonders geschützten AD-Objekts und auffällige administrative Aktivität sind dokumentiert. Weitere Befunde betreffen Zertifikatsvorlagen, Web Enrollment und Delegationsrechte. Zusammen ergibt sich ein plausibles Risiko unbefugter Rechteausweitung.</p>
|
||||||
|
<p class="limit">Ob die Änderung autorisiert war, ob die Beobachtungen zusammenhängen und ob Rechte tatsächlich missbraucht wurden, ist offen. Eine Kompromittierung darf daraus nicht abgeleitet werden.</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="assessment">
|
||||||
|
<div><span class="kind">Verfügbarkeit</span><h2>AD-Wiederanlauf</h2></div>
|
||||||
|
<div>
|
||||||
|
<p>Im erfassten Zustand besteht keine Redundanz des Domänencontrollers. Als letztes AD-Backup wurde bei der Erhebung der 27.05.2026 gemeldet. Ein Ausfall könnte die Anmeldung und Berechtigungsprüfung der abhängigen Dienste erheblich beeinträchtigen.</p>
|
||||||
|
<p class="limit">Spätere Sicherungen und eine praktisch geprüfte Wiederherstellung gehen aus dem Datenstand nicht hervor. Die tatsächliche Wiederanlaufzeit kann nicht beziffert werden.</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="assessment">
|
||||||
|
<div><span class="kind">Cloud-Zugang</span><h2>Administrative Konten</h2></div>
|
||||||
|
<div>
|
||||||
|
<p>Die Ergebnisse weisen auf Lücken bei phishingresistenter Mehrfaktor-Authentifizierung und risikobasierten Zugriffsregeln hin. Damit ist die Absicherung privilegierter Cloud-Anmeldungen nicht hinreichend belegt.</p>
|
||||||
|
<p class="limit">169 von 318 importierten Cloud-Prüfungen wurden zum Scanzeitpunkt nicht ausgeführt; zwei weitere blieben ohne Bewertung. Vorhandene Gegenkontrollen und die Reichweite der Abweichungen sind daher nicht vollständig beurteilt.</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<section class="open-points" aria-labelledby="open-title">
|
||||||
|
<h2 id="open-title">Für die Managementbewertung noch offen</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Einordnung der AD-Rechteänderung und der auffälligen Admin-Aktivität anhand von Change- und Ereignisnachweisen.</li>
|
||||||
|
<li>Nachweis eines aktuellen, nutzbaren AD-Backups und einer getesteten Wiederherstellung.</li>
|
||||||
|
<li>Zuordnung geschäftskritischer Dienste und möglicher OT-Abhängigkeiten zur Identitätsinfrastruktur.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<p class="scope"><strong>Datenbasis:</strong> Gespeicherte ASSESSOR-Ergebnisse des Dummy-Mandanten bis 29.09.2026. Die Erhebung ist kein synchroner Rescan und erlaubt keinen belastbaren Vorher-nachher-Vergleich. Technische Einzelbelege und Originalwerte sind in ASSESSOR Work und Results verfügbar.</p>
|
||||||
|
<footer class="foot"><span><strong>ASSESSOR 2.9.0</strong> · Dummy-Datenbestand · vertraulicher Entwurf</span><span>2 / 2</span></footer>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Management Report · Dummy</title>
|
||||||
|
<style>
|
||||||
|
@font-face{font-family:"Inter";src:url("assets/inter-latin.woff2") format("woff2");font-weight:100 900;font-style:normal}
|
||||||
|
:root{
|
||||||
|
--surface-app:#F1F1F4;--paper:#FFFFFF;--tile-surface:#F5F5F7;--tile-border:#DCDDE3;--tile-divider:#E2E2E8;
|
||||||
|
--text-primary:#17171C;--text-secondary:#55555F;--text-muted:#74747F;--marking-color:#E9A63C;
|
||||||
|
--outcome-fail:#C60F3E;--outcome-pass:#187447;--neutral:#B9BEC7;--donut-track:#B9BEC7;--donut-value:#1C70C9;
|
||||||
|
--panel-inset:20px;--marker-width:3px;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme:dark){:root{
|
||||||
|
--surface-app:#242426;--paper:#2A2A2E;--tile-surface:#323238;--tile-border:transparent;--tile-divider:rgba(255,255,255,.14);
|
||||||
|
--text-primary:#E2DFE6;--text-secondary:#B7BAC0;--text-muted:#9A9DA4;
|
||||||
|
--outcome-fail:#F2607F;--outcome-pass:#63D69A;--neutral:#555B65;--donut-track:#555B65;--donut-value:#348CF2;}}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
html,body{background:var(--surface-app)}
|
||||||
|
body{margin:0;font-family:"Inter",system-ui,"Segoe UI",sans-serif;font-size:16px;line-height:1.55;color:var(--text-primary)}
|
||||||
|
.doc{max-width:960px;margin:32px auto;background:var(--paper);padding:48px 56px}
|
||||||
|
h1{font-size:32px;font-weight:600;line-height:1.2;margin:0 0 4px}
|
||||||
|
h2{font-size:24px;font-weight:600;margin:0 0 16px}
|
||||||
|
h3{font-size:18px;font-weight:600;margin:0}
|
||||||
|
p{margin:0 0 12px}
|
||||||
|
section{margin-top:48px}
|
||||||
|
.brand{display:flex;align-items:center;gap:12px;font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary)}
|
||||||
|
.brand img{width:28px;height:28px} .brand .sep{flex:1}
|
||||||
|
.badge{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;padding:2px 8px;border:1px solid var(--tile-border);background:var(--tile-surface)}
|
||||||
|
.badge--draft{border-color:var(--marking-color)}
|
||||||
|
.titleblock{margin:36px 0 24px} .titleblock p{font-size:18px;color:var(--text-secondary);margin:0}
|
||||||
|
.facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border:1px solid var(--tile-border);background:var(--tile-surface);margin:0}
|
||||||
|
.facts div{padding:12px 16px;border-right:1px solid var(--tile-divider);border-bottom:1px solid var(--tile-divider)}
|
||||||
|
.facts div:nth-child(3n){border-right:0} .facts div:nth-last-child(-n+3){border-bottom:0}
|
||||||
|
.facts dt{font-size:14px;color:var(--text-secondary)} .facts dd{margin:2px 0 0}
|
||||||
|
.kpis{display:grid;grid-template-columns:auto repeat(3,minmax(0,1fr));gap:16px;margin:0 0 24px}
|
||||||
|
.kpi{background:var(--tile-surface);border:1px solid var(--tile-border);padding:var(--panel-inset);display:flex;flex-direction:column;justify-content:center}
|
||||||
|
.kpi .v{font-size:32px;font-weight:600;line-height:1.1;font-variant-numeric:tabular-nums}
|
||||||
|
.kpi .l{font-size:14px;color:var(--text-secondary);margin-top:6px}
|
||||||
|
.donut{position:relative;width:132px;height:132px;margin:0}
|
||||||
|
.donut svg{width:100%;height:100%;display:block}
|
||||||
|
.donut circle{fill:none;stroke-width:12px;stroke-linecap:butt}
|
||||||
|
.donut .track{stroke:var(--donut-track)} .donut .value{stroke:var(--donut-value)}
|
||||||
|
.donut figcaption{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
|
||||||
|
.donut .num{font-size:28px;font-weight:600;line-height:1} .donut .num span{font-size:14px;font-weight:400;color:var(--text-secondary)}
|
||||||
|
.donut .cap{font-size:12px;color:var(--text-secondary);margin-top:4px}
|
||||||
|
.chart{margin:0 0 20px;background:var(--tile-surface);border:1px solid var(--tile-border);padding:var(--panel-inset)}
|
||||||
|
.chart svg{width:100%;height:auto;display:block}
|
||||||
|
.chart figcaption{font-size:14px;color:var(--text-secondary);margin-top:14px}
|
||||||
|
.chart figcaption b{color:var(--text-primary);font-weight:600}
|
||||||
|
svg text{font-family:inherit;fill:var(--text-primary);font-size:14px;white-space:pre}
|
||||||
|
svg .ax{font-size:12px;fill:var(--text-secondary)} svg .val{fill:var(--text-secondary)} svg .strong{fill:var(--text-primary);font-weight:600}
|
||||||
|
svg .grid{stroke:var(--paper);stroke-width:1.5}
|
||||||
|
svg .pass{fill:var(--outcome-pass)} svg .fail{fill:var(--outcome-fail)} svg .na{fill:var(--neutral)}
|
||||||
|
.focus{list-style:none;padding:0;margin:0}
|
||||||
|
.focus li{padding:4px 0 4px 14px;border-left:var(--marker-width) solid var(--tile-border);margin:0 0 6px}
|
||||||
|
.finding{border:1px solid var(--tile-border);background:var(--tile-surface);padding:var(--panel-inset);margin:0 0 16px;break-inside:avoid}
|
||||||
|
.finding header{display:flex;justify-content:space-between;align-items:baseline;gap:16px;margin-bottom:12px}
|
||||||
|
.finding .domain{font-size:12px;color:var(--text-secondary);text-align:right}
|
||||||
|
.block{padding-left:14px;border-left:var(--marker-width) solid var(--tile-border);margin:0 0 12px}
|
||||||
|
.block:last-child{margin-bottom:0} .block p:last-child{margin:0}
|
||||||
|
.block--fact{border-left-color:var(--outcome-fail)}
|
||||||
|
.label{font-size:14px;font-weight:600;color:var(--text-secondary);margin:0 0 2px}
|
||||||
|
table{width:100%;border-collapse:collapse;font-size:14px}
|
||||||
|
td{padding:10px;border-bottom:1px solid var(--tile-divider);vertical-align:top}
|
||||||
|
td b{font-weight:600}
|
||||||
|
footer{margin-top:48px;padding-top:12px;border-top:1px solid var(--tile-divider);font-size:12px;color:var(--text-muted);display:flex;justify-content:space-between;gap:16px}
|
||||||
|
@media (max-width:760px){
|
||||||
|
.doc{margin:0;padding:24px 16px}
|
||||||
|
.facts{grid-template-columns:1fr 1fr} .facts div{border-right:1px solid var(--tile-divider)!important;border-bottom:1px solid var(--tile-divider)!important}
|
||||||
|
.kpis{grid-template-columns:1fr 1fr}
|
||||||
|
.finding header{flex-direction:column;gap:4px} .finding .domain{text-align:left}
|
||||||
|
}
|
||||||
|
@page{size:A4;margin:18mm 16mm}
|
||||||
|
@media print{html,body{background:#fff} .doc{margin:0;padding:0;max-width:none} section.pb{break-before:page} h2,h3{break-after:avoid} .chart,.kpi{break-inside:avoid}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="doc">
|
||||||
|
<div class="brand"><img src="assets/assessor-icon-light-128.png" alt=""><span>ASSESSOR · Management Report</span><span class="sep"></span><span class="badge badge--draft">Entwurf</span><span class="badge">Vertraulich</span></div>
|
||||||
|
|
||||||
|
<div class="titleblock"><h1>Security Assessment Dummy</h1><p>Active Directory, Entra ID und Gruppenrichtlinien</p></div>
|
||||||
|
|
||||||
|
<dl class="facts">
|
||||||
|
<div><dt>Auftraggeber</dt><dd>Dummy</dd></div>
|
||||||
|
<div><dt>Domäne</dt><dd>sevenkingdoms.local</dd></div>
|
||||||
|
<div><dt>Domain Controller</dt><dd>1</dd></div>
|
||||||
|
<div><dt>Erhebungszeitraum</dt><dd>—</dd></div>
|
||||||
|
<div><dt>Berichtsdatum</dt><dd>29.09.2026</dd></div>
|
||||||
|
<div><dt>Version</dt><dd>Entwurf 0.3</dd></div>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>1 Zusammenfassung</h2>
|
||||||
|
<div class="kpis">
|
||||||
|
<div class="kpi" style="align-items:center"><figure class="donut" aria-label="Gesamtbewertung 37 von 100">
|
||||||
|
<svg viewBox="0 0 128 128" aria-hidden="true"><circle class="track" cx="64" cy="64" r="52"/><circle class="value" cx="64" cy="64" r="52" stroke-dasharray="120.89 326.73" transform="rotate(-90 64 64)"/></svg>
|
||||||
|
<figcaption><span class="num">37<span>/100</span></span></figcaption></figure><span class="l">Gesamtbewertung</span></div>
|
||||||
|
<div class="kpi"><span class="v">998</span><span class="l">auswertbare Prüfungen</span></div>
|
||||||
|
<div class="kpi"><span class="v">579</span><span class="l">Abweichungen (58 %)</span></div>
|
||||||
|
<div class="kpi"><span class="v">7 von 8</span><span class="l">Security Domains mit Pass Rate unter 50 %</span></div>
|
||||||
|
</div>
|
||||||
|
<p>Die Gesamtbewertung der Umgebung beträgt 37 von 100 Punkten. Von 998 auswertbaren Prüfungen weichen 579 von der jeweiligen Vorgabe ab. Die Abweichungen konzentrieren sich auf folgende Bereiche:</p>
|
||||||
|
<ul class="focus">
|
||||||
|
<li>Privilegierte Konten und Berechtigungen</li>
|
||||||
|
<li>Zertifikatsdienste</li>
|
||||||
|
<li>Veraltete Authentisierungsverfahren</li>
|
||||||
|
<li>Anmeldeschutz der Cloud-Identitäten</li>
|
||||||
|
<li>Härtung des Domain Controllers</li>
|
||||||
|
<li>Verfügbarkeit, Sicherung und Protokollierung</li>
|
||||||
|
</ul>
|
||||||
|
<p style="margin-top:16px">In den Bereichen privilegierte Konten und Zertifikatsdienste bestehen Konfigurationen, über die administrative Rechte in der Domäne erlangt werden können. Für einen Teil der festgestellten Berechtigungsänderungen ist offen, ob sie autorisiert sind.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="pb">
|
||||||
|
<h2>2 Gesamtbewertung</h2>
|
||||||
|
<figure class="chart"><svg viewBox="0 0 800 56" role="img" aria-labelledby="f1t"><title id="f1t">Ergebnis der auswertbaren Prüfungen</title><rect class="pass" x="0.0" y="0" width="335.9" height="18"/><rect class="fail" x="335.9" y="0" width="464.1" height="18"/><rect class="pass" x="0" y="37" width="12" height="12"/><text class="val" x="18" y="48"><tspan class="strong">419</tspan> Bestanden (42 %)</text><rect class="fail" x="300" y="37" width="12" height="12"/><text class="val" x="318" y="48"><tspan class="strong">579</tspan> Abweichung (58 %)</text></svg>
|
||||||
|
<figcaption><b>Abbildung 1</b> · Ergebnis der 998 auswertbaren Prüfungen. 175 weitere Prüfungen wurden nicht ausgeführt, waren nicht anwendbar oder fehlerhaft und gehen nicht in die Bewertung ein.</figcaption></figure>
|
||||||
|
<figure class="chart"><svg viewBox="0 0 800 358" role="img" aria-labelledby="f2t"><title id="f2t">Pass Rate je Security Domain</title><text class="ax" x="250" y="14">0 %</text><text class="ax" x="450.0" y="14" text-anchor="middle">50 %</text><text class="ax" x="650" y="14" text-anchor="end">100 %</text><text class="lbl" x="0" y="43">Identity & Account Hygiene</text><rect class="pass" x="250" y="32" width="192.0" height="14"/><rect class="fail" x="442.0" y="32" width="208.0" height="14"/><text class="val" x="664" y="43"><tspan class="strong">48 %</tspan> · 221 von 425</text><text class="lbl" x="0" y="77">Privilege & Attack Paths</text><rect class="pass" x="250" y="66" width="240.0" height="14"/><rect class="fail" x="490.0" y="66" width="160.0" height="14"/><text class="val" x="664" y="77"><tspan class="strong">60 %</tspan> · 10 von 25</text><text class="lbl" x="0" y="111">Authentication & Access</text><rect class="pass" x="250" y="100" width="104.8" height="14"/><rect class="fail" x="354.8" y="100" width="295.2" height="14"/><text class="val" x="664" y="111"><tspan class="strong">26 %</tspan> · 31 von 42</text><text class="lbl" x="0" y="145">AD Infrastructure & PKI</text><rect class="pass" x="250" y="134" width="137.5" height="14"/><rect class="fail" x="387.5" y="134" width="262.5" height="14"/><text class="val" x="664" y="145"><tspan class="strong">34 %</tspan> · 21 von 32</text><text class="lbl" x="0" y="179">System Hardening</text><rect class="pass" x="250" y="168" width="12.7" height="14"/><rect class="fail" x="262.7" y="168" width="387.3" height="14"/><text class="val" x="664" y="179"><tspan class="strong">3 %</tspan> · 61 von 63</text><text class="lbl" x="0" y="213">GPO & Baseline</text><rect class="pass" x="250" y="202" width="173.0" height="14"/><rect class="fail" x="423.0" y="202" width="227.0" height="14"/><text class="val" x="664" y="213"><tspan class="strong">43 %</tspan> · 223 von 393</text><text class="lbl" x="0" y="247">Cloud & Applications</text><rect class="pass" x="250" y="236" width="44.4" height="14"/><rect class="fail" x="294.4" y="236" width="355.6" height="14"/><text class="val" x="664" y="247"><tspan class="strong">11 %</tspan> · 8 von 9</text><text class="lbl" x="0" y="281">Monitoring & Recovery</text><rect class="pass" x="250" y="270" width="80.0" height="14"/><rect class="fail" x="330.0" y="270" width="320.0" height="14"/><text class="val" x="664" y="281"><tspan class="strong">20 %</tspan> · 4 von 5</text><text class="lbl" x="0" y="315">Nicht zugeordnet</text><rect class="pass" x="250" y="304" width="400.0" height="14"/><text class="val" x="664" y="315"><tspan class="strong">100 %</tspan> · 0 von 4</text><line class="grid" x1="450.0" y1="24" x2="450.0" y2="328"/><rect class="pass" x="250" y="341" width="12" height="12"/><text class="val" x="268" y="352">Bestanden</text><rect class="fail" x="370" y="341" width="12" height="12"/><text class="val" x="388" y="352">Abweichung</text></svg>
|
||||||
|
<figcaption><b>Abbildung 2</b> · Pass Rate je Security Domain; Anzahl der Abweichungen von den auswertbaren Prüfungen.</figcaption></figure>
|
||||||
|
<figure class="chart"><svg viewBox="0 0 800 272" role="img" aria-labelledby="f3t"><title id="f3t">Abweichungen je Security Domain</title><text class="lbl" x="0" y="15">Identity & Account Hygiene</text><rect class="fail" x="250" y="4" width="396.4" height="14"/><text class="val strong" x="656.4" y="15">221</text><text class="lbl" x="0" y="49">Privilege & Attack Paths</text><rect class="fail" x="250" y="38" width="17.9" height="14"/><text class="val strong" x="277.9" y="49">10</text><text class="lbl" x="0" y="83">Authentication & Access</text><rect class="fail" x="250" y="72" width="55.6" height="14"/><text class="val strong" x="315.6" y="83">31</text><text class="lbl" x="0" y="117">AD Infrastructure & PKI</text><rect class="fail" x="250" y="106" width="37.7" height="14"/><text class="val strong" x="297.7" y="117">21</text><text class="lbl" x="0" y="151">System Hardening</text><rect class="fail" x="250" y="140" width="109.4" height="14"/><text class="val strong" x="369.4" y="151">61</text><text class="lbl" x="0" y="185">GPO & Baseline</text><rect class="fail" x="250" y="174" width="400.0" height="14"/><text class="val strong" x="660.0" y="185">223</text><text class="lbl" x="0" y="219">Cloud & Applications</text><rect class="fail" x="250" y="208" width="14.3" height="14"/><text class="val strong" x="274.3" y="219">8</text><text class="lbl" x="0" y="253">Monitoring & Recovery</text><rect class="fail" x="250" y="242" width="7.2" height="14"/><text class="val strong" x="267.2" y="253">4</text></svg>
|
||||||
|
<figcaption><b>Abbildung 3</b> · Anzahl der Abweichungen je Security Domain.</figcaption></figure>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="pb">
|
||||||
|
<h2>3 Wesentliche Feststellungen</h2>
|
||||||
|
<article class="finding"><header><h3>3.1 Privilegierte Konten und Berechtigungen</h3><span class="domain">Privilege & Attack Paths · Identity & Account Hygiene</span></header>
|
||||||
|
<div class="block block--fact"><p class="label">Sachverhalt</p><p>Die Berechtigungen des AdminSDHolder-Objekts, das die Zugriffsrechte aller administrativen Konten vorgibt, wurden verändert. Auf dem Domain Controller und dem Kerberos-Dienstkonto krbtgt bestehen Schreibrechte für ressourcenbasierte Delegation; an privilegierten Objekten sind zusätzliche Anmeldeschlüssel (Shadow Credentials) hinterlegt. Vier administrative Konten sind nicht gegen Delegation geschützt, drei Mitglieder der Gruppe Domain Admins besitzen Kennwörter ohne Ablaufdatum. Das integrierte Administratorkonto wird aktiv verwendet; für ein Konto wurden auffällige administrative Aktivitäten erfasst.</p></div>
|
||||||
|
<div class="block"><p class="label">Einordnung</p><p>Diese Konstellation erlaubt es, ausgehend von nicht privilegierten Konten administrative Rechte in der Domäne zu erlangen. Ob die veränderten Berechtigungen und die erfassten Aktivitäten autorisiert sind, lässt sich aus den erhobenen Daten nicht bestimmen.</p></div></article>
|
||||||
|
<article class="finding"><header><h3>3.2 Zertifikatsdienste</h3><span class="domain">AD Infrastructure & PKI · Authentication & Access</span></header>
|
||||||
|
<div class="block block--fact"><p class="label">Sachverhalt</p><p>Mehrere Zertifikatsvorlagen erlauben dem Antragsteller, die im Zertifikat bestätigte Identität selbst anzugeben. Eine Vorlage weist mindestens drei unsichere Einstellungen auf. Die Web-Registrierung der Zertifizierungsstelle ist nicht gegen die Weiterleitung von Anmeldungen (NTLM Relay) geschützt.</p></div>
|
||||||
|
<div class="block"><p class="label">Einordnung</p><p>Über diese Konfiguration können Zertifikate für fremde Identitäten, einschließlich administrativer Konten, ausgestellt und zur Anmeldung genutzt werden.</p></div></article>
|
||||||
|
<article class="finding"><header><h3>3.3 Authentisierungsverfahren</h3><span class="domain">Authentication & Access</span></header>
|
||||||
|
<div class="block block--fact"><p class="label">Sachverhalt</p><p>Die Domain Controller akzeptieren NTLMv1 und LM, unterstützen für Kerberos die Verschlüsselungsverfahren RC4 und DES und bieten TLS 1.0 und 1.1 an. Die Namensauflösung per LLMNR ist nicht deaktiviert.</p></div>
|
||||||
|
<div class="block"><p class="label">Einordnung</p><p>Die Verfahren gelten als veraltet und weisen bekannte Schwächen auf. Sie erleichtern das Abgreifen und Entschlüsseln von Anmeldedaten im internen Netz.</p></div></article>
|
||||||
|
<article class="finding"><header><h3>3.4 Cloud-Identitäten</h3><span class="domain">Identity & Account Hygiene · Cloud & Applications</span></header>
|
||||||
|
<div class="block block--fact"><p class="label">Sachverhalt</p><p>In Entra ID ist phishing-resistente Mehrfaktor-Authentisierung nicht durchgesetzt, und nicht alle Benutzer sind für Mehrfaktor-Authentisierung registriert. Risikobasierte Anmelderichtlinien bestehen nicht; Anmeldungen mit hohem Risiko werden nicht blockiert. Administrative Rollen sind teilweise synchronisierten Konten zugewiesen, ein externer Benutzer hält eine dauerhafte privilegierte Rolle, und die Zahl der globalen Administratoren liegt außerhalb des empfohlenen Bereichs.</p></div>
|
||||||
|
<div class="block"><p class="label">Einordnung</p><p>Der Anmeldeschutz stützt sich überwiegend auf Kennwörter. Über synchronisierte administrative Konten wirkt eine Kompromittierung des lokalen Verzeichnisses auf den Cloud-Tenant fort.</p></div></article>
|
||||||
|
<article class="finding"><header><h3>3.5 Härtung des Domain Controllers</h3><span class="domain">System Hardening · AD Infrastructure & PKI</span></header>
|
||||||
|
<div class="block block--fact"><p class="label">Sachverhalt</p><p>Auf dem Domain Controller ist die Druckwarteschlange aktiv und aus dem Netz erreichbar. Die Windows-Firewall ist nicht wie vorgesehen aktiviert und protokolliert nicht. Eine Verwaltung lokaler Administratorkennwörter (LAPS) wurde nicht festgestellt. Einstellungen der Benutzerkontensteuerung weichen von der Vorgabe ab. Von 63 Prüfungen zur Systemhärtung weichen 61 ab.</p></div>
|
||||||
|
<div class="block"><p class="label">Einordnung</p><p>Die Angriffsfläche des zentralen Systems der Umgebung ist größer als erforderlich.</p></div></article>
|
||||||
|
<article class="finding"><header><h3>3.6 Verfügbarkeit, Sicherung und Protokollierung</h3><span class="domain">Monitoring & Recovery · AD Infrastructure & PKI</span></header>
|
||||||
|
<div class="block block--fact"><p class="label">Sachverhalt</p><p>Die Domäne wird von einem einzelnen Domain Controller getragen. Die letzte erfasste Sicherung des Verzeichnisses datiert vom 27.05.2026 und liegt am Berichtsdatum 125 Tage zurück. Die Überwachungsrichtlinie der Domain Controller erfasst zentrale Ereignisse nicht; die PowerShell-Protokollierung ist unvollständig.</p></div>
|
||||||
|
<div class="block"><p class="label">Einordnung</p><p>Für Anmeldung und Namensauflösung besteht kein Ausfallschutz. Wiederherstellbarkeit und Nachvollziehbarkeit sicherheitsrelevanter Vorgänge sind eingeschränkt.</p></div></article>
|
||||||
|
<article class="finding"><header><h3>3.7 Gruppenrichtlinien</h3><span class="domain">GPO & Baseline</span></header>
|
||||||
|
<div class="block block--fact"><p class="label">Sachverhalt</p><p>In den zwei analysierten GPO-Backups weichen 25 Policy Settings (100 Vergleiche) von der Microsoft Security Baseline ab; 100 Vergleiche stimmen überein. Betroffen sind unter anderem Kennwortrichtlinie und Benutzerrechte. Vier Einstellungen sind mehrfach konfiguriert (Duplicates); widersprüchliche Konfigurationen (Conflicts) bestehen nicht.</p></div>
|
||||||
|
<div class="block"><p class="label">Einordnung</p><p>Die Abweichungen betreffen dieselben Schutzziele wie 3.1 und 3.5. Die Aussage beschränkt sich auf die konfigurierten Einstellungen der analysierten Backups.</p></div></article>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="pb">
|
||||||
|
<h2>4 Bewertungsgrundlage und Aussagebereich</h2>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr><td style="width:30%"><b>Gegenstand</b></td><td>Active Directory der Domäne sevenkingdoms.local, Konfiguration des Domain Controllers, Entra-ID-Tenant sowie zwei GPO-Backups im Abgleich mit der Microsoft Security Baseline.</td></tr>
|
||||||
|
<tr><td><b>Erhebung</b></td><td>Automatisierte Prüfung aus fünf Datenquellen mit insgesamt 4.757 Einzelergebnissen, davon 998 auswertbar. Maßgeblich ist der jeweilige Erhebungszeitpunkt.</td></tr>
|
||||||
|
<tr><td><b>Gesamtbewertung</b></td><td>Mixed-Method-Index auf einer Skala von 0 bis 100. Je Datenquelle geht deren eigener Gesamtwert ein, andernfalls die Pass Rate der auswertbaren Prüfungen. Die Werte der Security Domains sind Pass Rates und keine Zerlegung dieses Index.</td></tr>
|
||||||
|
<tr><td><b>Gruppenrichtlinien</b></td><td>Bewertet wird die Konformität der konfigurierten, vergleichbaren Einstellungen im Scope. Die Bewertung bezieht sich ausschließlich auf die analysierten GPO-Backups, nicht auf die gesamte Domäne oder den wirksamen Zustand der Systeme. 1.305 Vergleiche erfordern eine fachliche Prüfung und sind nicht in der Bewertung enthalten; 3.516 nicht konfigurierte Baseline-Empfehlungen sind bewertungsneutral.</td></tr>
|
||||||
|
<tr><td><b>Cloud-Identitäten</b></td><td>147 von 318 vorgesehenen Prüfungen waren auswertbar; 169 wurden nicht ausgeführt.</td></tr>
|
||||||
|
<tr><td><b>Nicht Gegenstand</b></td><td>Geschäftsprozesse, Schutzbedarf der Daten, organisatorische Regelungen und bestehende kompensierende Maßnahmen.</td></tr>
|
||||||
|
<tr><td><b>Risikoentscheidungen</b></td><td>Eine Abweichung ist als Accepted Risk dokumentiert.</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer><span>ASSESSOR · Management Report · Dummy · Entwurf 0.3 · 29.09.2026</span><span>Vertraulich</span></footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,187 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>ASSESSOR · Faktenbericht · Dummy</title>
|
||||||
|
<style>
|
||||||
|
@font-face {
|
||||||
|
font-family: Inter;
|
||||||
|
src: url("assets/inter-latin.woff2") format("woff2");
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--ink: #17171c;
|
||||||
|
--muted: #5c6069;
|
||||||
|
--navy: #123047;
|
||||||
|
--line: #cfd1d7;
|
||||||
|
--paper: #fff;
|
||||||
|
--surface: #f5f5f7;
|
||||||
|
--orange: #df752d;
|
||||||
|
--heat-0: #e7e8ec;
|
||||||
|
--heat-1: #f8dfcb;
|
||||||
|
--heat-2: #edb68d;
|
||||||
|
--heat-3: #e68f51;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: #e7e7eb; color: var(--ink); font: 400 14px/1.45 Inter, Arial, sans-serif; }
|
||||||
|
.sheet { width: 210mm; min-height: 297mm; margin: 16px auto; padding: 12mm 16mm 11mm; background: var(--paper); display: flex; flex-direction: column; }
|
||||||
|
.mast { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-bottom: 12px; border-bottom: 2px solid var(--navy); }
|
||||||
|
.brand { display: flex; align-items: center; gap: 9px; color: var(--navy); font-size: 19px; font-weight: 700; letter-spacing: .06em; }
|
||||||
|
.brand img { width: 31px; height: 31px; object-fit: contain; }
|
||||||
|
.mast-note { color: var(--navy); text-align: right; font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; }
|
||||||
|
.kicker { margin: 20px 0 7px; color: var(--orange); font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
h1 { margin: 0; color: var(--navy); font-size: 30px; line-height: 1.13; font-weight: 600; }
|
||||||
|
h2 { margin: 0; color: var(--navy); font-size: 18px; font-weight: 600; }
|
||||||
|
p { margin: 0 0 8px; }
|
||||||
|
.subtitle { margin: 8px 0 14px; color: var(--muted); font-size: 14px; }
|
||||||
|
.meta { display: flex; flex-wrap: wrap; gap: 6px 18px; padding: 8px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); color: var(--muted); font-size: 12px; }
|
||||||
|
.meta strong { color: var(--ink); font-weight: 600; }
|
||||||
|
.facts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; margin: 21px 0 26px; border-top: 2px solid var(--navy); border-bottom: 1px solid var(--line); }
|
||||||
|
.fact { padding: 13px 15px; border-right: 1px solid var(--line); }
|
||||||
|
.fact:last-child { border-right: 0; }
|
||||||
|
.fact strong { display: block; color: var(--navy); font-size: 28px; line-height: 1.12; font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||||
|
.fact span { display: block; margin-top: 5px; font-size: 14px; }
|
||||||
|
.section-head { display: flex; align-items: end; justify-content: space-between; gap: 15px; margin-bottom: 11px; }
|
||||||
|
.section-head p { max-width: 260px; margin: 0; color: var(--muted); font-size: 12px; text-align: right; }
|
||||||
|
.heatmap { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; margin: 0; padding: 0; list-style: none; }
|
||||||
|
.heatmap li { min-height: 104px; padding: 11px 12px 10px; display: flex; flex-direction: column; border: 1px solid rgba(18,48,71,.17); break-inside: avoid; }
|
||||||
|
.heatmap .h0 { background: var(--heat-0); }
|
||||||
|
.heatmap .h1 { background: var(--heat-1); }
|
||||||
|
.heatmap .h2 { background: var(--heat-2); }
|
||||||
|
.heatmap .h3 { background: var(--heat-3); }
|
||||||
|
.heatmap .area { color: var(--navy); font-size: 14px; line-height: 1.25; font-weight: 600; }
|
||||||
|
.heatmap .value { display: block; margin-top: auto; color: var(--navy); font-size: 28px; line-height: 1.08; font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||||
|
.heatmap .count { display: block; margin-top: 3px; color: var(--ink); font-size: 14px; }
|
||||||
|
.legend { display: flex; align-items: center; flex-wrap: wrap; gap: 5px 12px; margin: 12px 0 0; color: var(--muted); font-size: 12px; }
|
||||||
|
.legend .key { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
|
||||||
|
.legend i { display: inline-block; width: 14px; height: 14px; border: 1px solid rgba(18,48,71,.17); }
|
||||||
|
.legend .l0 { background: var(--heat-0); } .legend .l1 { background: var(--heat-1); }
|
||||||
|
.legend .l2 { background: var(--heat-2); } .legend .l3 { background: var(--heat-3); }
|
||||||
|
.comparison { margin-top: 26px; padding: 15px 17px; background: var(--surface); border: 1px solid var(--line); break-inside: avoid; }
|
||||||
|
.comparison h2 { margin-bottom: 13px; }
|
||||||
|
.comparison-scale, .comparison-row { display: grid; grid-template-columns: 240px minmax(180px, 1fr) 135px; gap: 12px; }
|
||||||
|
.comparison-scale { margin-bottom: 5px; color: var(--muted); font-size: 12px; }
|
||||||
|
.comparison-scale div { grid-column: 2; display: flex; justify-content: space-between; }
|
||||||
|
.comparison-list { list-style: none; margin: 0; padding: 0; }
|
||||||
|
.comparison-row { align-items: center; min-height: 34px; font-size: 14px; }
|
||||||
|
.comparison-bar { display: flex; height: 14px; background: #e3e3e8; }
|
||||||
|
.bar-pass { background: #167048; } .bar-fail { background: #c60b41; }
|
||||||
|
.comparison-value { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
|
||||||
|
.comparison-value span { color: var(--muted); font-weight: 400; }
|
||||||
|
.comparison-key { display: flex; gap: 24px; margin: 13px 0 0 252px; font-size: 14px; color: var(--muted); }
|
||||||
|
.comparison-key i { display: inline-block; width: 12px; height: 12px; margin-right: 5px; vertical-align: -1px; }
|
||||||
|
.comparison-key .pass { background: #167048; } .comparison-key .fail { background: #c60b41; }
|
||||||
|
.notes { margin-top: 23px; padding: 13px 15px; background: var(--surface); border-top: 2px solid var(--navy); }
|
||||||
|
.notes h2 { font-size: 15px; margin-bottom: 6px; }
|
||||||
|
.notes p { margin: 0 0 5px; font-size: 14px; }
|
||||||
|
.notes p:last-child { margin-bottom: 0; }
|
||||||
|
.foot { margin-top: auto; padding-top: 10px; border-top: 1px solid var(--navy); display: flex; justify-content: space-between; gap: 12px; color: var(--muted); font-size: 10px; }
|
||||||
|
.foot strong { color: var(--navy); font-weight: 600; }
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body { background: #fff; }
|
||||||
|
.sheet { width: auto; min-height: 0; margin: 0; padding: 18px 16px 26px; }
|
||||||
|
.heatmap { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.section-head { display: block; }
|
||||||
|
.section-head p { max-width: none; margin-top: 4px; text-align: left; }
|
||||||
|
h1 { font-size: 26px; }
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.heatmap, .facts { grid-template-columns: 1fr; }
|
||||||
|
.fact { border-right: 0; border-bottom: 1px solid var(--line); }
|
||||||
|
.fact:last-child { border-bottom: 0; }
|
||||||
|
}
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.comparison-scale { display: none; }
|
||||||
|
.comparison-row { grid-template-columns: 1fr auto; gap: 3px 10px; margin-bottom: 12px; }
|
||||||
|
.comparison-label { grid-column: 1 / -1; }
|
||||||
|
.comparison-bar { grid-column: 1; }
|
||||||
|
.comparison-value { grid-column: 2; }
|
||||||
|
.comparison-key { margin-left: 0; }
|
||||||
|
}
|
||||||
|
@page { size: A4; margin: 0; }
|
||||||
|
@media print {
|
||||||
|
body { background: #fff; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
|
||||||
|
.sheet { margin: 0; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="sheet">
|
||||||
|
<header class="mast">
|
||||||
|
<div class="brand"><img src="assets/assessor-icon-light-128.png" alt="">ASSESSOR</div>
|
||||||
|
<div class="mast-note">Management Report<br>Vertraulich</div>
|
||||||
|
</header>
|
||||||
|
<p class="kicker">Gespeicherter Assessment-Stand</p>
|
||||||
|
<h1>Technische Ergebnisse im Überblick</h1>
|
||||||
|
<p class="subtitle">Dummy · Fakten aus der aktuellen ASSESSOR-Reportprojektion, ohne Risikoscore oder Szenariobewertung</p>
|
||||||
|
<div class="meta">
|
||||||
|
<span><strong>Domäne:</strong> sevenkingdoms.local</span>
|
||||||
|
<span><strong>Berichtsdatum:</strong> 29.09.2026</span>
|
||||||
|
<span><strong>Vergleich:</strong> nicht enthalten</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="facts" aria-label="Erfasste Kennzahlen">
|
||||||
|
<div class="fact"><strong>1</strong><span>Domänencontroller erfasst</span></div>
|
||||||
|
<div class="fact"><strong>998</strong><span>Ergebnisse bewertet</span></div>
|
||||||
|
<div class="fact"><strong>579</strong><span>Abweichende Ergebnisse</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section aria-labelledby="heatmap-title">
|
||||||
|
<div class="section-head">
|
||||||
|
<h2 id="heatmap-title">Abweichungsanteil je Sicherheitsbereich</h2>
|
||||||
|
<p>Abweichende von bewerteten Ergebnissen innerhalb des jeweiligen Bereichs.</p>
|
||||||
|
</div>
|
||||||
|
<ol class="heatmap" aria-label="Heatmap der bewerteten Ergebnisse">
|
||||||
|
<li class="h2" data-deviations="221" data-assessed="425"><span class="area">Konten & Identitäten</span><strong class="value">52 %</strong><span class="count">221 von 425</span></li>
|
||||||
|
<li class="h1" data-deviations="10" data-assessed="25"><span class="area">Privilegien & Angriffspfade</span><strong class="value">40 %</strong><span class="count">10 von 25</span></li>
|
||||||
|
<li class="h2" data-deviations="31" data-assessed="42"><span class="area">Anmeldung & Zugriff</span><strong class="value">74 %</strong><span class="count">31 von 42</span></li>
|
||||||
|
<li class="h2" data-deviations="21" data-assessed="32"><span class="area">AD-Infrastruktur & PKI</span><strong class="value">66 %</strong><span class="count">21 von 32</span></li>
|
||||||
|
<li class="h3" data-deviations="61" data-assessed="63"><span class="area">Systemhärtung</span><strong class="value">97 %</strong><span class="count">61 von 63</span></li>
|
||||||
|
<li class="h2" data-deviations="223" data-assessed="393"><span class="area">GPO & Baseline*</span><strong class="value">57 %</strong><span class="count">223 von 393</span></li>
|
||||||
|
<li class="h3" data-deviations="8" data-assessed="9"><span class="area">Cloud & Anwendungen</span><strong class="value">89 %</strong><span class="count">8 von 9</span></li>
|
||||||
|
<li class="h3" data-deviations="4" data-assessed="5"><span class="area">Überwachung & Wiederanlauf</span><strong class="value">80 %</strong><span class="count">4 von 5</span></li>
|
||||||
|
<li class="h0" data-deviations="0" data-assessed="4"><span class="area">Nicht zugeordnet</span><strong class="value">0 %</strong><span class="count">0 von 4</span></li>
|
||||||
|
</ol>
|
||||||
|
<div class="legend" aria-label="Farbstufen nach Abweichungsanteil">
|
||||||
|
<span>Farbstufe:</span>
|
||||||
|
<span class="key"><i class="l0"></i>0–24 %</span>
|
||||||
|
<span class="key"><i class="l1"></i>25–49 %</span>
|
||||||
|
<span class="key"><i class="l2"></i>50–74 %</span>
|
||||||
|
<span class="key"><i class="l3"></i>75–100 %</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="comparison" aria-labelledby="comparison-title">
|
||||||
|
<h2 id="comparison-title">Bestandene und abweichende Ergebnisse</h2>
|
||||||
|
<div class="comparison-scale" aria-hidden="true"><div><span>0 %</span><span>50 %</span><span>100 %</span></div></div>
|
||||||
|
<ol class="comparison-list">
|
||||||
|
<li class="comparison-row"><span class="comparison-label">Identity & Account Hygiene</span><span class="comparison-bar"><span class="bar-pass" style="width:48%"></span><span class="bar-fail" style="width:52%"></span></span><span class="comparison-value">48 % <span>· 221 von 425</span></span></li>
|
||||||
|
<li class="comparison-row"><span class="comparison-label">Privilege & Attack Paths</span><span class="comparison-bar"><span class="bar-pass" style="width:60%"></span><span class="bar-fail" style="width:40%"></span></span><span class="comparison-value">60 % <span>· 10 von 25</span></span></li>
|
||||||
|
<li class="comparison-row"><span class="comparison-label">Authentication & Access</span><span class="comparison-bar"><span class="bar-pass" style="width:26%"></span><span class="bar-fail" style="width:74%"></span></span><span class="comparison-value">26 % <span>· 31 von 42</span></span></li>
|
||||||
|
<li class="comparison-row"><span class="comparison-label">AD Infrastructure & PKI</span><span class="comparison-bar"><span class="bar-pass" style="width:34%"></span><span class="bar-fail" style="width:66%"></span></span><span class="comparison-value">34 % <span>· 21 von 32</span></span></li>
|
||||||
|
<li class="comparison-row"><span class="comparison-label">System Hardening</span><span class="comparison-bar"><span class="bar-pass" style="width:3%"></span><span class="bar-fail" style="width:97%"></span></span><span class="comparison-value">3 % <span>· 61 von 63</span></span></li>
|
||||||
|
<li class="comparison-row"><span class="comparison-label">GPO & Baseline</span><span class="comparison-bar"><span class="bar-pass" style="width:43%"></span><span class="bar-fail" style="width:57%"></span></span><span class="comparison-value">43 % <span>· 223 von 393</span></span></li>
|
||||||
|
<li class="comparison-row"><span class="comparison-label">Cloud & Applications</span><span class="comparison-bar"><span class="bar-pass" style="width:11%"></span><span class="bar-fail" style="width:89%"></span></span><span class="comparison-value">11 % <span>· 8 von 9</span></span></li>
|
||||||
|
<li class="comparison-row"><span class="comparison-label">Monitoring & Recovery</span><span class="comparison-bar"><span class="bar-pass" style="width:20%"></span><span class="bar-fail" style="width:80%"></span></span><span class="comparison-value">20 % <span>· 4 von 5</span></span></li>
|
||||||
|
<li class="comparison-row"><span class="comparison-label">Nicht zugeordnet</span><span class="comparison-bar"><span class="bar-pass" style="width:100%"></span><span class="bar-fail" style="width:0%"></span></span><span class="comparison-value">100 % <span>· 0 von 4</span></span></li>
|
||||||
|
</ol>
|
||||||
|
<div class="comparison-key"><span><i class="pass"></i>Bestanden</span><span><i class="fail"></i>Abweichung</span></div>
|
||||||
|
<p>Pass Rate je Security Domain; rechts: Anzahl der Abweichungen von den bewerteten Ergebnissen.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<aside class="notes" aria-labelledby="notes-title">
|
||||||
|
<h2 id="notes-title">Lesart</h2>
|
||||||
|
<p>Die Farbe zeigt die Häufigkeit abweichender Ergebnisse innerhalb eines Bereichs. Sie zeigt weder Schweregrad noch Geschäftsrisiko. Bereiche mit wenigen Prüfungen können hohe Prozentwerte haben.</p>
|
||||||
|
<p>* Die GPO-Kachel zählt zugeordnete Ergebniszeilen. Die gesonderte GPO-Evaluation zählt 100 abweichende Vergleiche zu 25 Einstellungen; beide Zähleinheiten sind nicht gleichzusetzen.</p>
|
||||||
|
<p>Ergebnisse stammen aus unterschiedlichen Erhebungszeitpunkten. Eine Aussage zu betroffenen Geschäftsdiensten, Ausnutzung oder aktuellem Behebungsstand ist aus dieser Darstellung nicht ableitbar.</p>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<footer class="foot">
|
||||||
|
<span><strong>ASSESSOR 2.9.0</strong> · Dummy · Darstellung vorhandener Reportwerte</span>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>ASSESSOR · Management Report · verfügbarer Datenstand</title>
|
||||||
|
<style>
|
||||||
|
@font-face {
|
||||||
|
font-family: Inter;
|
||||||
|
src: url("assets/inter-latin.woff2") format("woff2");
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--ink: #17171c;
|
||||||
|
--muted: #5c6069;
|
||||||
|
--line: #cfcfd8;
|
||||||
|
--paper: #fff;
|
||||||
|
--surface: #f5f5f7;
|
||||||
|
--navy: #123047;
|
||||||
|
--orange: #df752d;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: #e7e7eb; color: var(--ink); font: 400 14px/1.46 Inter, Arial, sans-serif; }
|
||||||
|
.page { width: 210mm; min-height: 297mm; margin: 16px auto; padding: 12mm 16mm 11mm; background: var(--paper); display: flex; flex-direction: column; }
|
||||||
|
.mast { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-bottom: 12px; border-bottom: 2px solid var(--navy); }
|
||||||
|
.brand { display: flex; align-items: center; gap: 9px; color: var(--navy); font-size: 19px; font-weight: 700; letter-spacing: .06em; }
|
||||||
|
.brand img { width: 31px; height: 31px; object-fit: contain; }
|
||||||
|
.mast-note { color: var(--navy); text-align: right; font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; }
|
||||||
|
.kicker { margin: 20px 0 7px; color: var(--orange); font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
h1 { margin: 0; color: var(--navy); font-size: 31px; line-height: 1.12; font-weight: 600; }
|
||||||
|
h2 { margin: 0 0 9px; color: var(--navy); font-size: 18px; font-weight: 600; }
|
||||||
|
p { margin: 0 0 9px; }
|
||||||
|
.subtitle { margin: 9px 0 15px; color: var(--muted); }
|
||||||
|
.meta { display: flex; flex-wrap: wrap; gap: 7px 18px; padding: 8px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); color: var(--muted); font-size: 12px; }
|
||||||
|
.meta strong { color: var(--ink); font-weight: 600; }
|
||||||
|
.intro { margin: 19px 0 23px; padding: 15px 18px; border-left: 5px solid var(--orange); background: var(--surface); }
|
||||||
|
.intro p { margin: 0; font-size: 17px; line-height: 1.4; }
|
||||||
|
.facts { display: grid; grid-template-columns: 1fr 1fr 1fr; border-top: 2px solid var(--navy); border-bottom: 1px solid var(--line); }
|
||||||
|
.fact { padding: 15px 14px 14px; border-right: 1px solid var(--line); }
|
||||||
|
.fact:last-child { border-right: 0; }
|
||||||
|
.fact strong { display: block; color: var(--navy); font-size: 27px; line-height: 1.15; font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||||
|
.fact span { display: block; margin-top: 7px; color: var(--muted); font-size: 12px; }
|
||||||
|
.fact small { display: block; margin-top: 4px; color: var(--muted); font-size: 11px; }
|
||||||
|
.content { display: grid; grid-template-columns: 1fr 1fr; gap: 25px; margin-top: 28px; }
|
||||||
|
.content section { border-top: 2px solid var(--navy); padding-top: 12px; }
|
||||||
|
.content ul { margin: 0; padding-left: 18px; }
|
||||||
|
.content li { margin-bottom: 9px; }
|
||||||
|
.content li:last-child { margin-bottom: 0; }
|
||||||
|
.content .limited { color: var(--muted); }
|
||||||
|
.scope { margin-top: 30px; padding: 14px 16px; background: var(--surface); }
|
||||||
|
.scope h2 { font-size: 15px; margin-bottom: 6px; }
|
||||||
|
.scope p:last-child { margin-bottom: 0; }
|
||||||
|
.foot { margin-top: auto; padding-top: 10px; border-top: 1px solid var(--navy); display: flex; justify-content: space-between; gap: 12px; color: var(--muted); font-size: 10px; }
|
||||||
|
.foot strong { color: var(--navy); font-weight: 600; }
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body { background: #fff; }
|
||||||
|
.page { width: auto; min-height: 0; margin: 0; padding: 18px 16px 26px; }
|
||||||
|
.facts, .content { grid-template-columns: 1fr; }
|
||||||
|
.fact { border-right: 0; border-bottom: 1px solid var(--line); }
|
||||||
|
.fact:last-child { border-bottom: 0; }
|
||||||
|
h1 { font-size: 27px; }
|
||||||
|
}
|
||||||
|
@page { size: A4; margin: 0; }
|
||||||
|
@media print {
|
||||||
|
body { background: #fff; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
|
||||||
|
.page { margin: 0; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="page">
|
||||||
|
<header class="mast">
|
||||||
|
<div class="brand"><img src="assets/assessor-icon-light-128.png" alt="">ASSESSOR</div>
|
||||||
|
<div class="mast-note">Management Report<br>Vertraulich</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<p class="kicker">Verfügbarer ASSESSOR-Datenstand</p>
|
||||||
|
<h1>Ergebnisse des Security Assessments</h1>
|
||||||
|
<p class="subtitle">Dummy · Automatisch verfügbare Befunde und Bestandsangaben in einer Managementansicht.</p>
|
||||||
|
<div class="meta">
|
||||||
|
<span><strong>Domäne:</strong> sevenkingdoms.local</span>
|
||||||
|
<span><strong>Berichtsdatum:</strong> 29.09.2026</span>
|
||||||
|
<span><strong>Erhebungszeitraum:</strong> nicht einheitlich ausgewiesen</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="intro">
|
||||||
|
<p>ASSESSOR weist technische Abweichungen aus. Wie stark sie konkrete Geschäftsprozesse betreffen und welche davon vorrangig sind, lässt sich aus dem aktuellen Report-Datenstand nicht belastbar bestimmen.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section aria-labelledby="facts-title">
|
||||||
|
<h2 id="facts-title">Erfasste Werte</h2>
|
||||||
|
<div class="facts">
|
||||||
|
<div class="fact"><strong>1</strong><span>Domänencontroller erfasst</span><small>Bestandsangabe zum Erhebungszeitpunkt</small></div>
|
||||||
|
<div class="fact"><strong>998</strong><span>Einzelprüfungen bewertet</span><small>Unterschiedliche technische Prüfgegenstände</small></div>
|
||||||
|
<div class="fact"><strong>579</strong><span>Abweichende Ergebnisse</span><small>Keine Anzahl unabhängiger Geschäftsrisiken</small></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<section aria-labelledby="known-title">
|
||||||
|
<h2 id="known-title">Aus dem Datenstand ableitbar</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Technische Prüfergebnisse, deren Status und zugeordnete Sicherheitsbereiche.</li>
|
||||||
|
<li>Ein erfasster Domänencontroller. Der Report belegt keine zweite AD-Instanz.</li>
|
||||||
|
<li>Gespeicherte Reviewangaben, sofern sie vom Kunden gepflegt wurden; sie sind kein technischer Nachweis einer Behebung.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section aria-labelledby="limits-title">
|
||||||
|
<h2 id="limits-title">Nicht belastbar ableitbar</h2>
|
||||||
|
<ul class="limited">
|
||||||
|
<li>Konkrete Auswirkungen auf geschäftskritische Dienste oder OT.</li>
|
||||||
|
<li>Eine bestätigte Kompromittierung oder ein vollständig validierter Angriffspfad.</li>
|
||||||
|
<li>Aktuelle Wiederherstellbarkeit, spätere Änderungen oder ein Fortschritt seit den Scans.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="scope" aria-labelledby="scope-title">
|
||||||
|
<h2 id="scope-title">Aussagebereich</h2>
|
||||||
|
<p>Die Zahlen beschreiben gespeicherte Prüfergebnisse verschiedener Quellen und Erhebungszeitpunkte. Sie sind weder ein unternehmensweiter Risikoscore noch eine Priorisierung nach betrieblicher Wirkung. Für eine individuelle Risikoeinordnung fehlen im heutigen Reportmodell geprüfte Szenarioregeln und kundenseitige Dienstabhängigkeiten.</p>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<footer class="foot">
|
||||||
|
<span><strong>ASSESSOR 2.9.0</strong> · Dummy · Darstellungsentwurf auf Basis vorhandener Reportdaten</span>
|
||||||
|
<span>1 / 1</span>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>ASSESSOR · Management Brief · Dummy</title>
|
||||||
|
<style>
|
||||||
|
@font-face {
|
||||||
|
font-family: Inter;
|
||||||
|
src: url("assets/inter-latin.woff2") format("woff2");
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--ink: #17171c;
|
||||||
|
--muted: #5c6069;
|
||||||
|
--navy: #123047;
|
||||||
|
--line: #cfd1d7;
|
||||||
|
--paper: #fff;
|
||||||
|
--surface: #f5f5f7;
|
||||||
|
--orange: #df752d;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: #e7e7eb; color: var(--ink); font: 400 16px/1.44 Inter, Arial, sans-serif; }
|
||||||
|
.sheet { width: 210mm; min-height: 297mm; margin: 16px auto; padding: 12mm 16mm 11mm; background: var(--paper); display: flex; flex-direction: column; }
|
||||||
|
.mast { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-bottom: 12px; border-bottom: 2px solid var(--navy); }
|
||||||
|
.brand { display: flex; align-items: center; gap: 9px; color: var(--navy); font-size: 19px; font-weight: 700; letter-spacing: .06em; }
|
||||||
|
.brand img { width: 31px; height: 31px; object-fit: contain; }
|
||||||
|
.mast-note { color: var(--navy); text-align: right; font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; }
|
||||||
|
.kicker { margin: 18px 0 6px; color: var(--orange); font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
h1 { margin: 0; color: var(--navy); font-size: 30px; line-height: 1.15; font-weight: 600; }
|
||||||
|
h2 { margin: 0 0 10px; color: var(--navy); font-size: 18px; line-height: 1.25; font-weight: 600; }
|
||||||
|
p { margin: 0 0 8px; }
|
||||||
|
.subtitle { margin: 8px 0 15px; color: var(--muted); font-size: 14px; }
|
||||||
|
.meta { display: flex; flex-wrap: wrap; gap: 6px 18px; padding: 8px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); color: var(--muted); font-size: 12px; }
|
||||||
|
.meta strong { color: var(--ink); font-weight: 600; }
|
||||||
|
.summary { margin: 18px 0 22px; padding: 15px 18px; border-left: 5px solid var(--orange); background: var(--surface); }
|
||||||
|
.summary .label { display: block; margin-bottom: 6px; color: var(--navy); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
.summary p { margin: 0; font-size: 17px; line-height: 1.39; }
|
||||||
|
.matrix { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
||||||
|
.matrix thead th { padding: 9px 9px; background: var(--navy); color: #fff; font-size: 11px; font-weight: 600; letter-spacing: .02em; text-align: left; }
|
||||||
|
.matrix tbody th, .matrix tbody td { padding: 12px 9px; border-bottom: 1px solid var(--line); vertical-align: top; text-align: left; font-size: 14px; line-height: 1.42; }
|
||||||
|
.matrix tbody th { color: var(--navy); font-weight: 600; }
|
||||||
|
.matrix tbody th span { display: block; margin-top: 4px; color: var(--muted); font-size: 12px; font-weight: 400; }
|
||||||
|
.matrix tbody td:last-child { color: var(--muted); }
|
||||||
|
.matrix tbody tr { break-inside: avoid; }
|
||||||
|
.meaning { display: grid; grid-template-columns: 42mm 1fr; gap: 15px; margin-top: 21px; padding: 13px 15px; border-top: 2px solid var(--navy); background: var(--surface); }
|
||||||
|
.meaning h2 { margin: 0; font-size: 15px; }
|
||||||
|
.meaning p { margin: 0; font-size: 14px; }
|
||||||
|
.scope { margin-top: 19px; color: var(--muted); font-size: 12px; }
|
||||||
|
.scope strong { color: var(--ink); font-weight: 600; }
|
||||||
|
.foot { margin-top: auto; padding-top: 10px; border-top: 1px solid var(--navy); display: flex; justify-content: space-between; gap: 12px; color: var(--muted); font-size: 10px; }
|
||||||
|
.foot strong { color: var(--navy); font-weight: 600; }
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body { background: #fff; }
|
||||||
|
.sheet { width: auto; min-height: 0; margin: 0; padding: 18px 16px 25px; }
|
||||||
|
h1 { font-size: 26px; }
|
||||||
|
.matrix, .matrix thead, .matrix tbody, .matrix tr, .matrix th, .matrix td { display: block; width: 100%; }
|
||||||
|
.matrix thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); }
|
||||||
|
.matrix tbody tr { margin-bottom: 14px; border-top: 2px solid var(--navy); }
|
||||||
|
.matrix tbody th, .matrix tbody td { border-bottom: 0; padding: 8px 9px; }
|
||||||
|
.matrix tbody td::before { display: block; margin-bottom: 2px; color: var(--muted); font-size: 11px; font-weight: 600; text-transform: uppercase; }
|
||||||
|
.matrix tbody td:nth-child(2)::before { content: "Beobachtet"; }
|
||||||
|
.matrix tbody td:nth-child(3)::before { content: "Mögliche Betriebswirkung"; }
|
||||||
|
.matrix tbody td:nth-child(4)::before { content: "Offen"; }
|
||||||
|
.meaning { grid-template-columns: 1fr; gap: 6px; }
|
||||||
|
}
|
||||||
|
@page { size: A4; margin: 0; }
|
||||||
|
@media print {
|
||||||
|
body { background: #fff; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
|
||||||
|
.sheet { margin: 0; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="sheet">
|
||||||
|
<header class="mast">
|
||||||
|
<div class="brand"><img src="assets/assessor-icon-light-128.png" alt="">ASSESSOR</div>
|
||||||
|
<div class="mast-note">Management Report<br>Vertraulich</div>
|
||||||
|
</header>
|
||||||
|
<p class="kicker">Identitätsinfrastruktur</p>
|
||||||
|
<h1>Befunde mit möglicher Betriebswirkung</h1>
|
||||||
|
<p class="subtitle">Dummy · Fachlich verdichtete Einordnung der gespeicherten ASSESSOR-Ergebnisse</p>
|
||||||
|
<div class="meta">
|
||||||
|
<span><strong>Umgebung:</strong> sevenkingdoms.local</span>
|
||||||
|
<span><strong>Berichtsstand:</strong> 29.09.2026</span>
|
||||||
|
<span><strong>Vergleich:</strong> kein belastbarer Vorher-nachher-Stand</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="summary">
|
||||||
|
<span class="label">Kernaussage</span>
|
||||||
|
<p>Die Befunde weisen auf Möglichkeiten zur Ausweitung privilegierter AD-Rechte hin. Die Wiederherstellbarkeit des AD ist aus dem erfassten Datenstand nicht belegt. Eine aktive Kompromittierung ist nicht nachgewiesen.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section aria-labelledby="risk-title">
|
||||||
|
<h2 id="risk-title">Risikobild aus den vorliegenden Befunden</h2>
|
||||||
|
<table class="matrix">
|
||||||
|
<colgroup><col style="width:18%"><col style="width:31%"><col style="width:28%"><col style="width:23%"></colgroup>
|
||||||
|
<thead><tr><th scope="col">Risikobereich</th><th scope="col">Beobachtet</th><th scope="col">Mögliche Betriebswirkung</th><th scope="col">Offen</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">AD-Rechte und PKI<span>Integrität</span></th>
|
||||||
|
<td>Geänderte Rechte an einem geschützten AD-Objekt, auffällige Admin-Aktivität sowie Schwächen bei Zertifikatsvorlagen und der Zertifikatsregistrierung.</td>
|
||||||
|
<td>Unbefugte administrative Zugriffe könnten AD-abhängige Anmeldungen und Berechtigungen beeinflussen.</td>
|
||||||
|
<td>Autorisierung der Änderung, Zusammenhang der Befunde und tatsächliche Ausnutzbarkeit.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">AD-Wiederanlauf<span>Verfügbarkeit</span></th>
|
||||||
|
<td>Ein Domänencontroller erfasst. Als letztes AD-Backup wurde bei der Erhebung der 27.05.2026 gemeldet.</td>
|
||||||
|
<td>Ein AD-Ausfall könnte Anmeldung und Berechtigungsprüfung abhängiger Dienste unterbrechen.</td>
|
||||||
|
<td>Spätere Sicherungen, erfolgreiche Wiederherstellung und konkrete Wiederanlaufzeit.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Cloud-Admins<span>Zugriffsschutz</span></th>
|
||||||
|
<td>Abweichungen bei phishingresistenter Mehrfaktor-Authentifizierung und risikobasierten Zugriffsregeln.</td>
|
||||||
|
<td>Privilegierte Cloud-Zugänge könnten leichter missbraucht werden.</td>
|
||||||
|
<td>Weitere Gegenkontrollen und Reichweite der Abweichungen; ein Teil der Prüfungen wurde nicht ausgeführt.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<aside class="meaning" aria-labelledby="meaning-title">
|
||||||
|
<h2 id="meaning-title">Betriebliche Einordnung</h2>
|
||||||
|
<p>AD-basierte Anmeldung und Berechtigungen wären bei einem Ausfall oder Missbrauch betroffen. Welche geschäftskritischen Dienste oder OT-Prozesse davon abhängen, ist nicht erfasst; ein konkreter finanzieller oder operativer Schaden lässt sich daher nicht beziffern.</p>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<p class="scope"><strong>Aussagebasis:</strong> Gespeicherte Assessment-Ergebnisse mit unterschiedlichen Erhebungszeitpunkten. Die Tabelle trennt beobachtete Konfigurationen von möglichen Folgen und offenen Nachweisen. Ein Scannerbefund belegt weder einen Angriff noch den aktuellen Betriebszustand nach der Erhebung.</p>
|
||||||
|
|
||||||
|
<footer class="foot">
|
||||||
|
<span><strong>ASSESSOR 2.9.0</strong> · Dummy · manuell redigierter Berichtsentwurf</span>
|
||||||
|
<span>1 / 1</span>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
# PingCastle-Importprüfung
|
||||||
|
|
||||||
|
## Nachtrag: Punkte 1–3 umgesetzt und verifiziert
|
||||||
|
|
||||||
|
9. September 2026, PingCastle-Profil **1.5.0**, Extractor **2.2.0**,
|
||||||
|
HTML/XML-Formatadapter **1.1.0**. Der ursprüngliche Prüfstand darunter bleibt
|
||||||
|
als historische Diagnose erhalten.
|
||||||
|
|
||||||
|
- XML: Alle 403 Attribute außerhalb der RiskRules sind einzeln mit Originalwert
|
||||||
|
und Attributpfad vorhanden, außerdem alle 70 explizit leeren Blätter.
|
||||||
|
Zusammen mit den 664 Textwerten entstehen 1.137 Kontextbeobachtungen.
|
||||||
|
- HTML: Alle 125 Tabellen und 1.513 Kontextbeobachtungen bleiben erhalten.
|
||||||
|
Die 47 verunreinigten Kontextzellen, 3 Finding-Details und 16 verknüpften
|
||||||
|
Detailzell-Vorkommen enthalten keine technischen Attributreste mehr.
|
||||||
|
Eine unabhängige HTML-Strukturzählung bestätigt unveränderte Elementanzahlen
|
||||||
|
vor und nach dem Schutz zitierter Attribute.
|
||||||
|
- Fehlende Rule IDs scheitern explizit. Dubletten sowie ungültige oder
|
||||||
|
abweichende Kategorie-Trefferzahlen erzeugen Diagnosen; die bisherige erste
|
||||||
|
Darstellung bleibt erhalten. Originalzähler und errechnete eindeutige Treffer
|
||||||
|
stehen in einem zusätzlichen Bilanzabschnitt: insgesamt 18 statt 17 Abschnitte,
|
||||||
|
ohne die Environment- oder Findingzahl zu erhöhen.
|
||||||
|
- Beide Formate liefern weiterhin dieselben 37 Regeln mit identischem Status,
|
||||||
|
Regelpunkten und abgeleiteter Severity. Bekannte Quellgrenzen bleiben sichtbar:
|
||||||
|
ein unaufgelöster HTML-Verweis und die XML-Results-Warnung.
|
||||||
|
|
||||||
|
21 neue synthetische Regressionen; `npm test`: 1.096 bestanden, acht optionale
|
||||||
|
PostgreSQL-Tests übersprungen, Sicherheits-/Designprüfung erfolgreich.
|
||||||
|
`npm run build`: Client und Server erfolgreich, bestehender Bundlegrößenhinweis.
|
||||||
|
Der produktive Parse-only-HTTP-Pfad bestätigt Profil und Zahlen; App, Backend
|
||||||
|
und Proxy antworten mit HTTP 200. Der kanonische PostgreSQL-Datastore wurde vor
|
||||||
|
dem Backend-Neustart geprüft; die Repositoryrevision blieb während des
|
||||||
|
Parse-Abgleichs unverändert. Keine Importdaten gespeichert oder ersetzt,
|
||||||
|
keine Schemaänderung. Temporäre Dummy-Prüfdatei entfernt.
|
||||||
|
|
||||||
|
Bestehende Importe benötigen einen expliziten Reimport. Punkte 4–5
|
||||||
|
(semantisches XML-Kontextmapping und Control-Path-JSON) sind nicht Teil dieser
|
||||||
|
Freigabe und bleiben offen. Die Schutzregeln stehen in `AGENTS.md`,
|
||||||
|
`SECURITY.md` und `docs/IMPORT_SECURITY_LIMITS.md`.
|
||||||
|
|
||||||
|
## Historischer Prüfstand vor der Korrektur
|
||||||
|
|
||||||
|
Stand: 9. September 2026. Assessor-Profil 1.4.0, Extractor 2.1.0. Prüfung, keine Implementierung oder Bestandsänderung. Einordnung: Phase 13A/13C.
|
||||||
|
|
||||||
|
## Ergebnis
|
||||||
|
|
||||||
|
**Die 37 ausgelösten Regeln sind vollständig und korrekt übernommen. Der gesamte Berichtskontext ist noch nicht vollständig beziehungsweise unverfälscht nutzbar.**
|
||||||
|
|
||||||
|
HTML und XML ergeben jeweils 37 eindeutige Regel-IDs und 37 kanonische Vorkommen. Titel, Regelpunkte, Status, Domain-Risiko und zugeordneter Kategoriescore stimmen für jede Regel überein. Browser- und Server-Adapter liefern dieselben Kandidaten und Kontextbeobachtungen. Beide kanonischen Modelle bestehen die Strukturvalidierung.
|
||||||
|
|
||||||
|
Die unabhängig aus den Originalen gezählten Treffer sind Stale Objects 10, Privileged Accounts 7, Trusts 0 und Anomalies 20. Die daneben genannten 187 Katalogregeln sind **keine 187 vorhandenen Einzeltestergebnisse**. Assessor leitet aus den Regelpunkten in beiden Formaten 8 Medium, 13 Low und 16 Info ab; das ist keine native PingCastle-Severity. Native Risikowerte: Domain 100, Stale Objects 31, Privileged Accounts 85, Trusts 0 und Anomalies 100.
|
||||||
|
|
||||||
|
## Was zu korrigieren wäre
|
||||||
|
|
||||||
|
### P1: XML-Attribute und explizite Leere erhalten
|
||||||
|
|
||||||
|
Außerhalb der RiskRules enthält XML 758 Blattelemente: 664 mit nichtleerem Text, 24 ohne Text mit Attributen und 70 ohne beides. Nur die 664 Textwerte werden kanonische Kontextbeobachtungen. Die 24 attributtragenden Elemente bestehen aus 16 CertificateTemplateRights, 4 Verteilungsdatensätzen und 4 Control-Path-Analysewerten. Auch die 70 ausdrücklich leeren Elemente fehlen als Zustände.
|
||||||
|
|
||||||
|
Das betrifft die strukturierte Projektion, nicht die Existenz des ursprünglichen Rohdokuments. Attribute werden im generischen XML-Zwischenmodell noch geführt. `xmlCandidates` verwirft jedoch Kontextknoten ohne Text und übernimmt die Attribute der übrigen Knoten ebenfalls nicht in die Kontextwerte. Folge: Berechtigungs-/Verteilungsinformationen und die Unterscheidung zwischen leer und nicht erhoben sind nicht vollständig nutzbar.
|
||||||
|
|
||||||
|
Korrektur: Originalattribute mit genauen Quellpfaden erhalten, Attribute separat typisieren und leere Sammlungen ausdrücklich kennzeichnen. Sicherheit der Feststellung: hoch.
|
||||||
|
|
||||||
|
### P1: HTML-Zelltext und Finding-Details sauber extrahieren
|
||||||
|
|
||||||
|
47 Kontextzellen enthalten technische Attributreste. Zusätzlich sind 3 Finding-Details sowie 16 Zellen verknüpfter Detailtabellen betroffen; die 16 zählen Vorkommen und nicht zwingend unterschiedliche Quellzellen.
|
||||||
|
|
||||||
|
Die lexikalischen Tag-/Zellregulärausdrücke sind gegenüber spitzen Klammern innerhalb zitierter Attribute nicht robust. Eine ausschließlich präsentative Bereinigung repariert keine bereits verunreinigten strukturierten Werte.
|
||||||
|
|
||||||
|
Korrektur: Attributgrenzen zuverlässig erkennen, sichtbaren Text vom Originalmarkup trennen und dieselbe Regel für Kontext und Finding-Details verwenden. Sicherheit: hoch.
|
||||||
|
|
||||||
|
### P1: Stille Nullbefunde, falsche Zähler und Dubletten diagnostizieren
|
||||||
|
|
||||||
|
Die Originaldateien haben keine fehlenden IDs oder widersprüchlichen Trefferzahlen. Drei zusätzliche synthetische Gegenproben belegen jedoch fehlende Schutzprüfungen:
|
||||||
|
|
||||||
|
- Ein angekündigter Treffer mit leerer Rule ID: 0 Kandidaten, `complete`, keine Diagnose.
|
||||||
|
- Zwei angekündigte Treffer mit einem Regelblock: 1 Kandidat, `complete`, keine Diagnose.
|
||||||
|
- Zwei Regelblöcke mit derselben ID: still auf 1 Kandidaten reduziert, `complete`, keine Diagnose.
|
||||||
|
|
||||||
|
Korrektur: Trefferzahlen je Kategorie gegen Einzelregeln abgleichen; fehlende IDs und doppelte IDs explizit behandeln. Keine Regeln verwerfen, um eine Summe passend zu machen. Sicherheit: hoch; die Relevanz für künftige Exporte ist durch synthetische Fälle belegt, nicht als Fehler dieses Dummy-Paares behauptet.
|
||||||
|
|
||||||
|
### P2: XML auf denselben Kontextvertrag wie HTML abbilden
|
||||||
|
|
||||||
|
HTML liefert 1.513 Beobachtungen in 17 Abschnitten, XML 664 in einem Sammelabschnitt. Diese Mengen haben unterschiedliche Körnung und sind keine unmittelbar vergleichbaren Vollständigkeitsquoten.
|
||||||
|
|
||||||
|
Beim XML liegen alle Eigenschaften unter `pingcastle.xml.*`. Gemeinsame Eigenschaften wie `domain.fqdn`, `risk.domain` und `report.engine_version` fehlen. Die entsprechenden Client-Info-Selektoren können die vorhandenen Quelldaten daher nicht gleichwertig nutzen. Die kanonische Toolversion bleibt beim XML leer.
|
||||||
|
|
||||||
|
Korrektur: fachlich äquivalente HTML-/XML-Werte auf dieselben Eigenschaften abbilden und zugleich ursprüngliche Werte und Pfade behalten. Sicherheit: hoch.
|
||||||
|
|
||||||
|
### P2: Control-Path-JSON erhalten
|
||||||
|
|
||||||
|
HTML enthält 27 gültige `application/json`-Blöcke: 26 Graphen mit insgesamt 255 Knoten- und 229 Kanten-Vorkommen sowie ein Beziehungserklärungs-Wörterbuch. Diese Strukturen werden vom PingCastle-Extractor nicht eigenständig übernommen. Die Summen sind **keine deduplizierte AD-Assetanzahl**. Vorhandene Tabellen können Teile derselben Information enthalten, ersetzen aber nicht die Knoten-Kanten-Beziehungen.
|
||||||
|
|
||||||
|
Korrektur: ausschließlich statisches JSON lesen, Struktur und Referenzen validieren und mit Provenienz erhalten. Keine Berichtsskripte ausführen. Eine zusätzliche graphische Darstellung ist eine gesonderte Produktentscheidung. Sicherheit: hoch.
|
||||||
|
|
||||||
|
## Kein Assessor-Fehler: fehlendes Ankerziel
|
||||||
|
|
||||||
|
Ein HTML-Detailverweis auf `HardenedPaths` hat im Original kein entsprechendes Ziel. Assessor meldet ihn korrekt als unaufgelöst. Es darf kein vermeintlich passender Zielabschnitt erfunden werden.
|
||||||
|
|
||||||
|
Bei Textvergleichen festgestellte Absatzunterschiede wurden nicht pauschal als Inhaltsverlust ausgegeben: überwiegend handelte es sich um Whitespace oder zusätzliche Inhalte gegenüber dem ersten DOM-Absatz.
|
||||||
|
|
||||||
|
## Verifikation und Grenzen
|
||||||
|
|
||||||
|
- Unabhängige native Regelzählung und ID-Abgleich, zusätzlich XML-Vergleich sämtlicher 37 Rationale-, Points-, Category- und Model-Werte.
|
||||||
|
- Produktive Importpipeline durch Browser-/Server-Adapter und kanonische Modellvalidierung.
|
||||||
|
- Laufendes Backend über den nicht speichernden Parse-Endpunkt: beide Dateien HTTP 200, je 37 Kandidaten; HTML 1.513 Kontextbeobachtungen/17 Abschnitte, XML 664/1. Repositoryrevision vor und nach der Prüfung unverändert.
|
||||||
|
- 34 vorhandene fokussierte Tests bestanden; ein zusätzlicher temporärer Test führte die drei oben beschriebenen Gegenproben aus. Temporäre Testdateien entfernt.
|
||||||
|
- Ein erweiterter unabhängiger DOM-Auditlauf überschritt unter Last sein Zeitlimit, nachdem er die Auswertung ausgegeben hatte. Ein vorheriger Kernabgleich war erfolgreich. Keine Performance-Aussage aus diesen Auditlaufzeiten.
|
||||||
|
- Keine vollständige visuelle UI-Abnahme, keine Aussage über alle PingCastle-Versionen, keine Migration, kein Reimport und keine Produktionscodeänderung. Aus einem Berichtspaar lässt sich kein zeitlicher Trend ableiten.
|
||||||
|
|
||||||
|
## Empfohlene Reihenfolge
|
||||||
|
|
||||||
|
Zuerst XML-Werterhalt, HTML-Textbereinigung und Vollständigkeitsdiagnosen; anschließend einheitliche Kontextabbildung und Control-Path-Daten. Danach synthetische Regressionen, denselben Dummy erneut abgleichen, Verarbeitung versionieren und erst auf Freigabe bestehende Importe ersetzen. HTML und XML nicht ungeprüft als doppelte Scans importieren oder automatisch miteinander verschmelzen.
|
||||||
|
|
||||||
|
## Quellen und Reproduzierbarkeit
|
||||||
|
|
||||||
|
- [Freigegebenes HTML](<C:/Users/milu/OneDrive - Exxeta/OC_SOC - General/Projekt Assessor/Dummy-Scans/PingCastle/ad_hc_sevenkingdoms.local.html>)
|
||||||
|
- [Freigegebenes XML](<C:/Users/milu/OneDrive - Exxeta/OC_SOC - General/Projekt Assessor/Dummy-Scans/PingCastle/ad_hc_sevenkingdoms.local.xml>)
|
||||||
|
- [Native Quellextraktion](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/pingcastle-source-extractor.ts:586>)
|
||||||
|
- [XML-Kontextprojektion](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/pingcastle-source-extractor.ts:1229>)
|
||||||
|
- [Lexikalische HTML-Extraktion](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/html-extraction.ts:404>)
|
||||||
|
- [Strukturierte Prüfergebnisse und XML-Zählkern](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/reports/pingcastle-audit-2026-09-09/artifact.json>)
|
||||||
|
|
||||||
|
Der Datenqualitäts-Skill führte zur unabhängigen Quellenzählung statt einer bloßen Erfolgsprüfung. Der zusätzliche Build-report-/Visualize-data-Ausgabeweg ist blockiert: Gehostete Ausgabe ist wegen der projektspezifischen Local-only-Regel ausgeschlossen. Der lokale Portable-Builder verlangt für Diagramme tatsächlich SQL-förmige Provenienz und lehnt die hier ausgeführte JavaScript/XML-Auswertung ab. Eine SQL-Abfrage wurde nicht erfunden. Daher liegt dieses Markdown-Prüfprotokoll vor, kein gerenderter HTML-Prüfbericht. Dies betrifft ausschließlich die Darstellung dieses Prüfprotokolls, **nicht** die erfolgreich geprüfte PingCastle-HTML-Importfunktion. Es wurden keine Daten extern hochgeladen.
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"surface": "report",
|
||||||
|
"manifest": {
|
||||||
|
"version": 1,
|
||||||
|
"surface": "report",
|
||||||
|
"title": "PingCastle: Importprüfung",
|
||||||
|
"generatedAt": "2026-09-09T11:30:00Z",
|
||||||
|
"sources": [
|
||||||
|
{"id":"pair","label":"Freigegebener PingCastle-Dummy und Assessor-Import","query":{"description":"Abgleich von ad_hc_sevenkingdoms.local.html und ad_hc_sevenkingdoms.local.xml mit der unveränderten Assessor-Pipeline. Native Quellzählung, Browser-/Server-Adapter und kanonisches Modell; Prüfung am 9. September 2026. Profil 1.4.0, Extractor 2.1.0."}},
|
||||||
|
{"id":"xml","label":"ad_hc_sevenkingdoms.local.xml","query":{"language":"javascript","engine":"Node.js / JSDOM","sql":"const leafNodes = [...document.querySelectorAll('*')].filter(element => !element.children.length && !element.closest('RiskRules'));\nconst total = leafNodes.length;\nconst textLeaves = leafNodes.filter(element => element.textContent?.trim()).length;\nconst attributeOnlyLeaves = leafNodes.filter(element => !element.textContent?.trim() && element.attributes.length);\nconst attributeKinds = Object.fromEntries([...new Set(attributeOnlyLeaves.map(element => element.tagName))].map(key => [key, attributeOnlyLeaves.filter(element => element.tagName === key).length]));\nconst emptyLeaves = leafNodes.filter(element => !element.textContent?.trim() && !element.attributes.length).length;","description":"Ausgeführter Zählkern; document ist das inerte XML-DOM der Datei ad_hc_sevenkingdoms.local.xml. 758 Blattelemente außerhalb RiskRules: 664 mit nichtleerem Text, 24 ohne Text mit Attributen, 70 ohne Text und Attribute. Vergleich mit den 664 kanonischen Kontextbeobachtungen. Diagramm gruppiert HealthCheckCertificateTemplateRights separat (16), Dist und CompromiseGraphAnomalyAnalysisData gemeinsam (4 + 4). Namespace-Deklarationen der Wurzel sind nicht Teil dieser Blattzählung."}},
|
||||||
|
{"id":"html","label":"ad_hc_sevenkingdoms.local.html","query":{"description":"Unabhängige inerte DOM-Prüfung und JSON.parse ausschließlich für application/json-Blöcke. Keine Ausführung von Berichtsskripten. Vergleich mit Kandidaten, Berichtstabellen, Detailfeldern und Kontextprojektion."}},
|
||||||
|
{"id":"guards","label":"Synthetische PingCastle-Gegenproben und Quellcode","query":{"description":"Temporäre Vitest-Prüfung von leerer Rule ID, deklarierten zwei Treffern bei einer Regel und zweimal derselben Rule ID. Alle drei Fälle als complete ohne Diagnose akzeptiert. Geprüfte Funktionen: htmlCandidates, xmlCandidates, contextObservations in src/importers/pingcastle-source-extractor.ts; decodeHtmlLexically in src/importers/html-extraction.ts. Temporäre Dateien nach der Prüfung entfernt."}}
|
||||||
|
],
|
||||||
|
"blocks": [
|
||||||
|
{"id":"title","type":"markdown","body":"# PingCastle: Importprüfung"},
|
||||||
|
{"id":"summary","type":"markdown","sourceId":"pair","body":"## Befunde vollständig, Zusatzdaten nicht vollständig\n\nHTML und XML liefern jeweils **37 von 37 ausgelösten Regeln**, ohne doppelte Regel-IDs und ohne Mapping-Rückfragen. Titel, Regelpunkte, Status sowie zugeordnete Domain- und Kategoriescores stimmen für alle 37 Regeln überein. Der strukturierte Kontext ist dagegen lückenhaft beziehungsweise teilweise verunreinigt. Für die Zusage, PingCastle vollständig zu übernehmen, sind weitere Korrekturen erforderlich.\n\nGeprüft wurde die vorhandene Implementierung, nicht verändert. Bestehende Importe wurden nicht ersetzt."},
|
||||||
|
{"id":"scope","type":"markdown","sourceId":"pair","body":"## Was hier als vollständig gilt\n\nEin Befund ist eine ausgelöste RiskRule, nicht jede Regel des PingCastle-Prüfkatalogs. Das HTML nennt 10 Treffer bei Stale Objects, 7 bei Privileged Accounts, 0 bei Trusts und 20 bei Anomalies; zusammen 37. Die daneben genannten insgesamt 187 Katalogregeln dürfen nicht als 187 vorhandene Einzeltestergebnisse behandelt werden.\n\nAssessor übernimmt für beide Formate denselben Status und dieselbe abgeleitete Severity: 8 Medium, 13 Low, 16 Info. PingCastle liefert Regelpunkte, keine native Severity je Regel. Domain-Risiko 100, Stale Objects 31, Privileged Accounts 85, Trusts 0 und Anomalies 100 bleiben native Risikowerte, keine vergleichbaren ASSESSOR-Scores.\n\nKontextvollständigkeit bedeutet zusätzlich: Textwerte, Attribute, explizite Leere, Berichtsdaten und strukturierte Verknüpfungen bleiben mit Herkunft nutzbar. Die bloße Verfügbarkeit des ursprünglichen Rohdokuments erfüllt diese Bedingung nicht."},
|
||||||
|
{"id":"xml-loss","type":"markdown","sourceId":"xml","body":"## P1: XML verliert Attributwerte und explizite Leere\n\nVon 758 Kontext-Blattelementen gelangen die 664 mit nichtleerem Text als Beobachtungen in das kanonische Modell. **24 attributtragende Blattelemente ohne Text fehlen dort**: 16 CertificateTemplateRights, 4 Verteilungsdatensätze und 4 Control-Path-Analysewerte. Dazu kommen 70 ausdrücklich leere Elemente. Dadurch gehen insbesondere Berechtigungs- und Verteilungsinformationen im strukturierten Kontext verloren; eine leere Sammlung ist nicht mehr zuverlässig von einer nicht erhobenen Sammlung unterscheidbar.\n\nDie folgende Darstellung zählt XML-Blattelemente, nicht Attribute oder Findings. Alle vier Gruppen sind disjunkt und ergeben zusammen 758. Roh-XML und generische Zwischenstruktur sind hiervon zu unterscheiden: Die Lücke liegt in der kanonischen Kontextprojektion. Sicherheit der Feststellung: hoch."},
|
||||||
|
{"id":"xml-coverage","type":"chart","chartId":"xml-leaves"},
|
||||||
|
{"id":"html-loss","type":"markdown","sourceId":"html","body":"## P1: HTML-Attributreste verschmutzen Kontext und Details\n\n**47 Kontextzellen** enthalten technische Attributreste. Auch 3 Finding-Details und 16 Zellen der verknüpften Ergebnistabellen sind betroffen; letztere Zahl zählt Vorkommen, nicht eindeutige Quellzellen. Die lexikalische HTML-Verarbeitung und einfache Tag-Regulärausdrücke behandeln spitze Klammern innerhalb zitierter Attribute nicht robust genug. Eine reine Anzeigebereinigung löst die Verunreinigung gespeicherter beziehungsweise normalisierter Werte nicht.\n\nEmpfehlung: Attributgrenzen sicher berücksichtigen, sichtbaren Zelltext getrennt vom ursprünglichen Markup gewinnen und beide mit Provenienz erhalten. Dieselbe Bereinigung muss für Kontext- und Finding-Details gelten. Sicherheit: hoch."},
|
||||||
|
{"id":"graphs","type":"markdown","sourceId":"html","body":"## P2: Control-Path-Graphdaten werden nicht strukturiert übernommen\n\nDas HTML enthält 27 gültige application/json-Blöcke: **26 Graphen mit zusammen 255 Knoten- und 229 Kanten-Vorkommen** sowie ein Wörterbuch mit Beziehungserklärungen. Diese Graphstrukturen werden vom PingCastle-Extractor nicht als eigene Strukturen erhalten. Vorhandene Tabellen und Texte können Teile derselben Information enthalten, ersetzen aber keine Knoten-Kanten-Beziehungen. Die Zahlen sind keine deduplizierte Anzahl von AD-Objekten.\n\nZusätzlich bleibt ein Detailverweis auf HardenedPaths unaufgelöst. Im Quelldokument existiert kein entsprechendes Ankerziel; die vorhandene Warnung ist hier korrekt. Daraus darf kein erfundener Detailabschnitt entstehen. Sicherheit: hoch."},
|
||||||
|
{"id":"xml-mapping","type":"markdown","sourceId":"pair","body":"## P2: XML-Kontext fehlt die gemeinsame fachliche Zuordnung\n\nHTML liefert 1.513 Kontextbeobachtungen in 17 Abschnitten, XML 664 Beobachtungen in einem Sammelabschnitt. Diese Mengen haben unterschiedliche Körnung und dürfen nicht direkt als Verlustquote verglichen werden. Beim XML liegen sämtliche Werte unter generischen pingcastle.xml-Eigenschaften. Gemeinsame Eigenschaften wie domain.fqdn, risk.domain und report.engine_version fehlen. Obwohl der Export die Werte enthält, können die darauf aufbauenden Client-Info-Selektoren sie nicht gleichwertig nutzen; auch die kanonische Toolversion bleibt beim XML leer.\n\nEmpfehlung: XML und HTML auf denselben fachlichen Kontextvertrag abbilden und zugleich die unveränderten XML-Werte samt Herkunft erhalten. Fehlende Daten niemals zu Null umdeuten. Sicherheit: hoch."},
|
||||||
|
{"id":"guards","type":"markdown","sourceId":"guards","body":"## P1: Fehlende Regeln müssen als Fehler oder Teilverarbeitung auffallen\n\nDrei synthetische Gegenproben werden ohne Diagnose als vollständig akzeptiert: ein angekündigter Treffer mit leerer Rule ID wird zu 0 Kandidaten; zwei angekündigte Treffer mit nur einem Regelblock werden zu 1 Kandidaten; zwei Blöcke mit derselben ID werden still zu 1 Kandidaten. Die freigegebenen Originaldateien zeigen diese Abweichungen nicht. Die Gegenproben belegen eine fehlende Schutzprüfung für künftige oder beschädigte Exporte.\n\nEmpfehlung: deklarierte Trefferzahlen je Kategorie gegen unabhängig gezählte Einzelregeln prüfen, fehlende Identitäten explizit melden und Dubletten nicht kommentarlos verwerfen. Sicherheit: hoch."},
|
||||||
|
{"id":"verification","type":"markdown","sourceId":"pair","body":"## Prüfung und Grenzen\n\nNative Regel-IDs wurden unabhängig gezählt und mit dem Import abgeglichen. Für XML stimmen außerdem sämtliche 37 Rationale-, Points-, Category- und Model-Werte mit dem Quelltext überein. Browser- und Server-Adapter erzeugen für beide Dateien dieselben Kandidaten und Kontextbeobachtungen; beide Modelle bestehen die kanonische Strukturvalidierung. Die fokussierten vorhandenen Tests bestanden; die Gegenproben demonstrieren fehlende Guards, keine bereits implementierten Fixes.\n\nUnterschiede bei HTML-Absatztexten wurden nicht pauschal als Verlust gewertet: die geprüften Differenzen waren überwiegend Whitespace oder zusätzliche Inhalte gegenüber dem ersten DOM-Absatz. Ein tiefer DOM-Auditlauf überschritt unter Last sein Zeitlimit; daraus wird kein Performance-Benchmark abgeleitet. Keine Aussage über sämtliche PingCastle-Versionen, keine Bestandsdatenmigration, kein Reimport und keine vollständige visuelle UI-Abnahme. Es liegt ein Berichtspaar vor, daher keine Zeitreihenanalyse."},
|
||||||
|
{"id":"next","type":"markdown","body":"## Empfohlene Reihenfolge\n\n1. XML-Attribute und explizite Leere verlustfrei in den Kontext übernehmen.\n2. HTML-Zell- und Detailtext korrekt auslesen.\n3. Vollständigkeits-, Identitäts- und Dublettendiagnosen ergänzen.\n4. XML auf den gemeinsamen Kontextvertrag abbilden.\n5. Statische Control-Path-JSON-Daten samt Beziehungen erhalten.\n\nAnschließend synthetische Regressionen und denselben Dummy-Abgleich durchführen, Verarbeitung versionieren und bestehende Importe gezielt neu importieren. Vor einer Umsetzung bleibt zu entscheiden, ob Control-Path-Daten zunächst als strukturierte Evidenz genügen oder zusätzlich eine eigene Darstellung erhalten sollen. Die Parserprüfung autorisiert keine automatische Zusammenführung von HTML und XML zu einem Scan."}
|
||||||
|
],
|
||||||
|
"charts": [{"id":"xml-leaves","title":"XML-Kontext nach Quellstruktur","dataset":"xmlLeaves","sourceId":"xml","type":"bar","encodings":{"x":{"field":"group"},"y":{"field":"count"}},"options":{"orientation":"horizontal","showValueLabels":true,"showLegend":false}}]
|
||||||
|
},
|
||||||
|
"snapshot": {"version":1,"status":"ready","generatedAt":"2026-09-09T11:30:00Z","datasets":{"xmlLeaves":[
|
||||||
|
{"group":"Textwerte übernommen","count":664,"state":"retained","totalContextLeaves":758},
|
||||||
|
{"group":"Explizit leere Elemente fehlen","count":70,"state":"missing","totalContextLeaves":758},
|
||||||
|
{"group":"Berechtigungsattribute fehlen","count":16,"state":"missing","totalContextLeaves":758},
|
||||||
|
{"group":"Verteilungs-/Graphattribute fehlen","count":8,"state":"missing","totalContextLeaves":758}
|
||||||
|
]}},
|
||||||
|
"sources": []
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
# PurpleKnight in ASSESSOR – Importprüfung
|
||||||
|
|
||||||
|
## Verifizierte Umsetzung vom 09.09.2026
|
||||||
|
|
||||||
|
Die beauftragten Importfehler sind behoben. Der ursprüngliche Auditstand bleibt darunter als Historie erhalten.
|
||||||
|
|
||||||
|
- Profil **1.3.0**, HTML-Extractor **1.2.0**, Summary-Extractor **1.1.0**; bestehende Mapping-/Normalisierungsverträge bleiben kompatibel.
|
||||||
|
- XLSX liefert nun **119 kanonische Ergebnisse**, 22 Kontextbeobachtungen und alle 20 verknüpften Detailtabellen mit 46 Zeilen. Die Summary verdrängt keine Indikatorentabelle mehr.
|
||||||
|
- HTML liefert **119 Ergebnisse**, **45 Kontextbeobachtungen**, einen Reportabschnitt und alle 20 Detailtabellen mit 46 Zeilen. Bekannte Summary-Kennzahlen und unbekannte Originalmetadaten bleiben erhalten. Sämtliche 213 geprüften Detailzellen, einschließlich Sekunden, stimmen exakt mit der Quelle überein; 4.743 Originalfeldwerte wurden gegen ihre nativen Quellpfade geprüft. Abgeleitete Status-, Kategorie- und Laufzeitwerte referenzieren ihre realen Eingabeobjekte.
|
||||||
|
- Die Haupt-CSV wird anhand der Kombination aus nativer Namensstruktur und Schema ohne zusätzliche Werkzeugbestätigung erkannt. Der CSV-Gesamtsatz liefert weiterhin 119 Ergebnisse, 22 Kontextbeobachtungen sowie 19 eindeutig verknüpfte Detaildateien mit 45 Zeilen. Dateiname allein erzwingt kein Profil.
|
||||||
|
- Beschädigte Payloads, fehlende IDs, konkurrierende Zuweisungen und angekündigte, aber unlesbare Ergebnisse scheitern explizit. Wiederholte IDs, fehlende Kategorieverweise und Summen-/Detailabweichungen erhalten Diagnosen; vorhandene Ergebnisse werden nicht verworfen. Native Null-Details ohne Ergebnisse und deklarierte Leerberichte bleiben gültig.
|
||||||
|
- **25 neue synthetische Regressionen. `npm test`: 1.122 bestanden, acht optionale PostgreSQL-Tests übersprungen. Sicherheits-/Designcheck und vollständiger Client-/Serverbuild erfolgreich.** Alle 24 Dateien wurden rein lesend mit beiden Runtimes und dem lokalen Parse-Endpunkt abgeglichen; der abschließende Drei-Format-Check am finalen Backend bestätigt Profil und Ergebniszahlen. Repositoryrevision unverändert.
|
||||||
|
|
||||||
|
AGENTS.md, Sicherheitsvorgaben, Importgrenzen und Umsetzungsplan enthalten die Erkenntnisse. Die temporären Audit-Skripte wurden entfernt. Der kanonische PostgreSQL-Datastore bleibt unverändert; App und aktualisiertes Backend laufen lokal. Kein Reimport, keine Datenbankmigration. **Bereits gespeicherte PurpleKnight-Importe benötigen einen ausdrücklichen Reimport**, um den neuen Erfassungsstand zu erhalten. Die separate AD-Domänen-CSV bleibt als Rohquelle erhalten; die typisierte HTML-Domänenliste ist Teil des neuen Kontexts. Phase 13C bleibt insgesamt in Arbeit.
|
||||||
|
|
||||||
|
## Ursprünglicher Prüfstand
|
||||||
|
|
||||||
|
Stand: 09.09.2026 · ASSESSOR 2.5.5 · Prüfung, keine Implementierung
|
||||||
|
|
||||||
|
## Ergebnis
|
||||||
|
|
||||||
|
PurpleKnight wird noch nicht vollständig verarbeitet. Der schwerste Fehler betrifft das vollständige Excel-Workbook: Seine 119 Indikatorergebnisse ergeben **0 kanonische Findings**. HTML und der bestätigte CSV-Gesamtimport liefern dagegen jeweils sämtliche 119 Indikatorergebnisse und die zugehörigen Ergebnistabellen. HTML verliert jedoch Berichtskontext und Zeitstempelpräzision; außerdem fehlen Vollständigkeitsprüfungen.
|
||||||
|
|
||||||
|
„119 Ergebnisse“ umfasst bestandene und nicht ausgeführte beziehungsweise nicht relevante Prüfungen, nicht 119 Sicherheitsprobleme.
|
||||||
|
|
||||||
|
## Prüfumfang und Methode
|
||||||
|
|
||||||
|
Geprüft wurden ausschließlich die 24 vom Nutzer freigegebenen Dummy-Dateien: 22 CSV-Dateien, ein HTML-Bericht und ein XLSX-Workbook. Der Auftrag gehört zu Phase 13A/13C des Implementierungsplans: Importpipeline, Profilverarbeitung und vollständiger Umgebungskontext.
|
||||||
|
|
||||||
|
- Unabhängige Kontrolllesung mit Python-CSV-Reader und direkter OOXML-Zelllesung. Der Spreadsheet-Prüfworkflow wurde rein lesend angewendet; das Workbook blieb unverändert. Die direkte OOXML-Lesung umging eine nicht unterstützte Layout-Eigenschaft im unabhängigen Excel-Leser, nicht einen ASSESSOR-Parserfehler.
|
||||||
|
- HTML-Skriptblöcke ausschließlich als Text und eingebettetes JSON gelesen, niemals ausgeführt.
|
||||||
|
- Alle Dateien durch Browser- und Node-Importadapter, Vorschau, erforderliche Mappingbestätigung, kanonisches Modell, Modellvalidierung und Work-Dataset-Projektion geprüft.
|
||||||
|
- Zusätzlich alle 24 Dateien gegen den laufenden lokalen Backend-Parse-Endpunkt geprüft: jeweils HTTP 200 und identische Extraktion; Repository-Revision vorher/nachher unverändert.
|
||||||
|
- Synthetische Gegenproben für Workbook-Extractor-Auswahl und beschädigte beziehungsweise widersprüchliche HTML-Berichte.
|
||||||
|
- 47 vorhandene fokussierte Tests in neun Testdateien bestanden.
|
||||||
|
|
||||||
|
Kein Import-/Reimport-Abschluss, keine Datenbankmigration, kein Zugriff auf fremde Kundendaten und keine Produktionscodeänderung. Bestehende persistierte PurpleKnight-Bestände und eine befüllte Oberfläche wurden nicht inhaltlich untersucht. Vollständiger Build und Gesamttests waren nicht Gegenstand dieser reinen Prüfung.
|
||||||
|
|
||||||
|
## Abgleich nach Format
|
||||||
|
|
||||||
|
| Quelle | Native Indikatorergebnisse | Kanonische Ergebnisse | Detailverknüpfungen | Berichtskontext |
|
||||||
|
| --- | ---: | ---: | --- | --- |
|
||||||
|
| CSV-Gesamtsatz, 08:23 | 119 | 119 nach Bestätigung | 19/19 eindeutig, 45 Detailzeilen | 22 Beobachtungen, ein Summary-Abschnitt |
|
||||||
|
| XLSX, 09:14 | 119 | **0** | Keine Work-Projektion aus diesem Einzelimport | 22 Beobachtungen, ein Summary-Abschnitt |
|
||||||
|
| HTML, 09:14 | 119 | 119 | 20/20 eindeutig, 46 Detailzeilen | **0 Beobachtungen, 0 Berichtabschnitte** |
|
||||||
|
|
||||||
|
Die CSV-Dateien stammen aus einem früheren Lauf als HTML/XLSX. Ihre Abweichung ist kein Importfehler:
|
||||||
|
|
||||||
|
| Lauf | Bestanden | IOE gefunden | Nicht relevant | Nicht ausgewählt | Nativer Posture Score |
|
||||||
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||||
|
| CSV 08:23 | 96 | 19 | 3 | 1 | 65 |
|
||||||
|
| HTML/XLSX 09:14 | 95 | 20 | 4 | 0 | 59 |
|
||||||
|
|
||||||
|
Alle 119 nativen Indikator-IDs stimmen zwischen den Läufen überein; zwei Statuswerte haben sich geändert. Die CSV-Hauptdatei erhält sämtliche 26 Spaltenwerte für alle 119 Zeilen. Die 19 CSV-Detaildateien erhalten sämtliche geprüften Zellwerte.
|
||||||
|
|
||||||
|
## Priorisierte Befunde
|
||||||
|
|
||||||
|
### 1. P1 – Summary-Extractor verdrängt Excel-Indikatoren
|
||||||
|
|
||||||
|
Das Workbook enthält 23 Blätter, darunter „Assessment summary“, „Indicators results“ mit 119 Indikatoren sowie 20 Detailblätter. Die Erkennung wählt die Indikatorentabelle korrekt aus. Anschließend gewinnt jedoch der Summary-Extractor für das gesamte Dokument und liefert absichtlich keine Finding-Kandidaten. Die kanonische Validierung akzeptiert das Ergebnis ohne Warnung. Quelltabellen bleiben erhalten, aber die 119 Indikatorergebnisse werden nicht als Findings aufgebaut; die Work-Projektion des alleinigen Workbooks bleibt leer.
|
||||||
|
|
||||||
|
Ursache: [Summary-Erkennung und Extraktion](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/purple-knight-summary-source-extractor.ts:170>) in Verbindung mit der [Auswahl genau eines Extractors](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/source-extractor-registry.ts:5>).
|
||||||
|
|
||||||
|
Fix: Summary-Kontext, Indikatorentabelle und Detailblätter gemeinsam über die generische Pipeline verarbeiten. Den Summary-Extractor nicht einfach abschalten, sonst geht der bereits verfügbare Kontext verloren. Synthetisch mit einem Summary- und einem Indikatorenblatt reproduziert.
|
||||||
|
|
||||||
|
Abnahme: 119 Ergebnisse, 20 eindeutig verknüpfte Detailtabellen mit 46 Zeilen und weiterhin 22 Summary-Beobachtungen aus diesem Workbook; keine Summary-Zeile als Finding.
|
||||||
|
|
||||||
|
### 2. P1 – HTML-Berichtskontext wird nicht übernommen
|
||||||
|
|
||||||
|
Im eingebetteten HTML-Modell stehen unter anderem Posture Score 59, Gesamtzahl 119, 95 bestandene Prüfungen, 20 IOEs, vier nicht relevante Prüfungen, sechs bewertete Kategorien, Werkzeugversion, Erstellungszeit und Umgebungskontext. Der Extractor übernimmt lediglich Indikator-Kandidaten und Detailtabellen. Das kanonische Modell enthält weder Umgebungsbeobachtungen noch Berichtabschnitte.
|
||||||
|
|
||||||
|
Ursache: [HTML-Extraktion](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/purple-knight-html-source-extractor.ts:191>).
|
||||||
|
|
||||||
|
Fix: Native HTML-Summary und Kategorien auf dieselben Kontextverträge wie CSV/XLSX abbilden. Native Scores separat und mit Herkunft führen, nicht mit vergleichbaren ASSESSOR-Scores verwechseln. Fehlende Werte bleiben unbekannt, nicht 0 beziehungsweise 0 %.
|
||||||
|
|
||||||
|
Abnahme: Inhaltlich äquivalente HTML-/XLSX-Summary liefert dieselben normalisierten Kennzahlen bei erhaltener formatabhängiger Provenienz.
|
||||||
|
|
||||||
|
### 3. P1 – HTML-Teilimporte werden nicht als unvollständig erkannt
|
||||||
|
|
||||||
|
Synthetische Gegenproben durch Extraktion und kanonisches Modell:
|
||||||
|
|
||||||
|
| Gegenprobe | Beobachtetes Verhalten |
|
||||||
|
| --- | --- |
|
||||||
|
| Deklarierte Gesamtzahl 99, tatsächlich zwei Indikatoren | Zwei Ergebnisse; Extraktion „complete“; keine Abweichungsdiagnose |
|
||||||
|
| Ein fehlender `ResIndicator`-Block | Nur ein statt zwei Ergebnissen; „complete“ |
|
||||||
|
| Ein beschädigter JSON-Indikator | Nur ein statt zwei Ergebnissen; „complete“ |
|
||||||
|
| Zwei Indikatoren mit gleicher `ShortName` | Zwei Vorkommen; keine Identitätskonfliktdiagnose |
|
||||||
|
|
||||||
|
Die generische Warnung über entfernten aktiven HTML-Inhalt ist vorhanden, erklärt aber keinen dieser Vollständigkeitsfehler. Bei den freigegebenen 119 HTML-Indikatoren wurde kein solcher Ausfall festgestellt; hier geht es um eine nachgewiesene Schutzlücke.
|
||||||
|
|
||||||
|
Ursache: [Payload-Lesung](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/purple-knight-html-source-extractor.ts:28>) überspringt nicht lesbare Objekte und gleicht das Ergebnis nicht mit der nativen Berichtsbilanz ab.
|
||||||
|
|
||||||
|
Fix: Deklarierte, gelesene und kanonisch übernommene Ergebnisse sowie Detailanzahlen abgleichen; beschädigte Objekte, fehlende IDs und Identitätskonflikte explizit melden. Widersprüchliche Identitäten nicht pauschal zusammenlegen. Fehler je nach Ausmaß als partiellen oder fehlgeschlagenen Import ausweisen.
|
||||||
|
|
||||||
|
### 4. P2 – HTML-Originaltreue und Feldherkunft unvollständig
|
||||||
|
|
||||||
|
- Von 213 geprüften Detailzellen wurden 20 Zeitstempel verändert: Ihre vorhandenen, nicht nullwertigen Sekunden verschwinden durch die Normalisierung auf Minuten. Alle 46 Zeilen bleiben erhalten, aber diese Werte sind nicht mehr exakt. Die [Datumsumwandlung](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/purple-knight-html-source-extractor.ts:116>) muss den Originalwert neben einem separat normalisierten Wert erhalten.
|
||||||
|
- Bei allen 119 Indikatoren zeigen die Feldpfade für die sechs geprüften Felder `ShortName`, `Name`, `Description`, `Severity`, `Score` und `Remediation` auf die falsche Objektebene. Beispielsweise fehlt `ResIndicator` beziehungsweise `ExecutionResult` im Pfad. Die Werte selbst wurden gelesen; ihre [Feldprovenienz](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/purple-knight-html-source-extractor.ts:57>) ist jedoch unzutreffend. Abgeleitete Werte brauchen ebenfalls explizite Ableitungsherkunft.
|
||||||
|
|
||||||
|
Die Originaldatei wird im kanonischen Import [nicht separat aufbewahrt](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/importers/canonical-import.ts:167>). Deshalb ist eine spätere Wiederherstellung der verlorenen Zeitpräzision aus dem kanonischen Modell allein nicht gesichert.
|
||||||
|
|
||||||
|
Textabweichungen wurden nicht pauschal als Verlust gezählt: 95 Unterschiede in „Likelihood of compromise“ und sechs in „Description“ betreffen die Darstellung. Sieben Remediation-Werte unterscheiden sich durch Whitespace oder HTML-zu-Klartext-Umwandlung; zwei enthalten Links, deren Ziele im Klartext fehlen. Die ursprünglichen Remediation-HTML-Werte bleiben jedoch als Originalfelder erhalten. Dies ist kein nachgewiesener vollständiger Verlust dieser Maßnahmen oder Links aus dem kanonischen Modell.
|
||||||
|
|
||||||
|
### 5. P2 – CSV-Erkennung benötigt zusätzliche Bestätigung
|
||||||
|
|
||||||
|
Die Haupt-CSV wird als PurpleKnight vorgeschlagen, erreicht aber nur 0,54 Konfidenz und verwendet bis zur Bestätigung das generische Profil. Nach der regulären Bestätigung ist das Profil korrekt `purple-knight`; es handelt sich nicht um einen dauerhaft generischen oder fehlerhaften Import.
|
||||||
|
|
||||||
|
Fixoption: Die Kombination aus typischer CSV-Namensstruktur und charakteristischen Spalten besser erkennen, ohne Dateinamen allein zu vertrauen. Die Sicherheitsgrenze für unbekannte oder mehrdeutige Dateien beibehalten. [Profilregeln](<C:/Users/milu/OneDrive - Exxeta/Dokumente/VS CODE/ASSESSOR-2-5/src/profiles/purple-knight/purple-knight-definition.ts:28>).
|
||||||
|
|
||||||
|
Die separate AD-Domänen-Datei bleibt als Quelltabelle erhalten, erzeugt aber keine typisierten Domänenbeobachtungen. Ob ihre Einzelangaben zusätzlich als Umfeldkontext benötigt werden, sollte mit der Kontextabbildung aus Punkt 2 vereinheitlicht werden; dies ist kein Verlust ihrer Rohdaten.
|
||||||
|
|
||||||
|
## Empfohlene Umsetzung
|
||||||
|
|
||||||
|
Zuerst Punkte 1–3 beheben, anschließend Originaltreue und Provenienz aus Punkt 4 absichern. Die CSV-Erkennungsverbesserung ist nachrangig.
|
||||||
|
|
||||||
|
Verbindliche Regressionen sollten vollständige Multi-Sheet-Workbooks, semantische Formatparität innerhalb desselben Scan-Laufs, vollständige Ergebnistabellen-Verknüpfung, exakte Originalwerte, auflösbare Feldpfade und explizite Teilimportdiagnosen abdecken. Prüfungen gegen unabhängige native Summen dürfen nicht nur die Anzahl der bereits extrahierten Kandidaten bestätigen. CSV- und HTML/XLSX-Läufe verschiedener Zeitpunkte müssen getrennt bleiben.
|
||||||
|
|
||||||
|
Dieser Bericht dokumentiert einen Prüfstand. Die genannten Fehler sind noch nicht behoben; keine Phase oder Abnahme wurde als abgeschlossen markiert. Temporäre Audit-Skripte wurden nach der Prüfung entfernt.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { spawnSync } from 'node:child_process';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
// CI mode makes missing snapshots fail. Never forward --update or automatically
|
||||||
|
// accept new expectations: a failing established import blocks the release.
|
||||||
|
const root = fileURLToPath(new URL('../', import.meta.url));
|
||||||
|
const result = spawnSync(process.execPath, [
|
||||||
|
fileURLToPath(new URL('../node_modules/vitest/vitest.mjs', import.meta.url)),
|
||||||
|
'run', '--config', 'vitest.config.ts',
|
||||||
|
'tests/importers', 'tests/scoring',
|
||||||
|
'tests/server/node-import-engine.test.ts',
|
||||||
|
'tests/server/import-worker-client.test.ts',
|
||||||
|
'tests/server/import-base64.test.ts',
|
||||||
|
'tests/server/import-batch-http.test.ts',
|
||||||
|
'tests/server/assessor-http-server.test.ts',
|
||||||
|
'tests/server/gpo-', 'tests/server/gpp-',
|
||||||
|
'tests/server/registry-policy-parser.test.ts',
|
||||||
|
'tests/server/security-template-parser.test.ts',
|
||||||
|
'tests/server/group-policy-preference-parser.test.ts',
|
||||||
|
'tests/server/advanced-audit-parser.test.ts',
|
||||||
|
'tests/server/admx-policy-catalog.test.ts',
|
||||||
|
'tests/domain/gpo-ingestion.test.ts',
|
||||||
|
], { cwd: root, env: { ...process.env, CI: 'true' }, stdio: 'inherit' });
|
||||||
|
if (result.error) console.error(result.error.message);
|
||||||
|
process.exit(result.status ?? 1);
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
/**
|
||||||
|
* GPO target-role impact measurement — prints NUMBERS ONLY.
|
||||||
|
*
|
||||||
|
* Question: how many current GPO Fail findings would change if every GPO were
|
||||||
|
* compared only with the Microsoft baseline templates of its target role, and
|
||||||
|
* how many GPOs are not linked at all?
|
||||||
|
*
|
||||||
|
* Privacy contract (read before running):
|
||||||
|
* - Reads only the one project whose ID you pass. No project list is printed
|
||||||
|
* unless you run `--list` yourself.
|
||||||
|
* - Everything stays in memory. No file is written.
|
||||||
|
* - Output is a fixed set of counters. No GPO names, GUIDs, paths, setting
|
||||||
|
* names, values, SIDs, OU names or tenant identifiers are printed.
|
||||||
|
* - Any error is reported only as its error type, never with its message.
|
||||||
|
* - Read-only: it calls GET endpoints of the local backend and runs one SELECT
|
||||||
|
* against the canonical local PostgreSQL container. It changes nothing.
|
||||||
|
*
|
||||||
|
* Usage (from the repository root, backend running on 127.0.0.1:4173):
|
||||||
|
* npx vite-node scripts/gpo-role-impact.ts --list
|
||||||
|
* npx vite-node scripts/gpo-role-impact.ts project-xxxxxxxx
|
||||||
|
*/
|
||||||
|
import { execFileSync } from 'node:child_process';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { policyComparisonDisposition } from '../src/domain/policy-comparison-disposition';
|
||||||
|
import { baselineTemplateContext } from '../src/evaluation/baseline-template-context';
|
||||||
|
import { policyWorkVerdictPriority } from '../src/evaluation/policy-work-results';
|
||||||
|
import { readVerifiedZipFiles, inventoryZipArchive } from '../server/gpo/zip-inventory';
|
||||||
|
|
||||||
|
const API = 'http://127.0.0.1:4173/api/v1';
|
||||||
|
const CONTAINER = 'assessor-2_0-postgres-1';
|
||||||
|
const DEFAULT_DC_POLICY = '{6AC1786C-016F-11D2-945F-00C04FB984F9}';
|
||||||
|
|
||||||
|
type Role = 'Domain Controller' | 'all' | 'unlinked' | 'unknown';
|
||||||
|
|
||||||
|
async function get<T>(path: string): Promise<T> {
|
||||||
|
const response = await fetch(API + path);
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
return response.json() as Promise<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sql(query: string): string[] {
|
||||||
|
return execFileSync('docker', ['exec', CONTAINER, 'psql', '-U', 'assessor', '-d', 'assessor', '-At', '-c', query],
|
||||||
|
{ encoding: 'utf8', maxBuffer: 512 * 1024 * 1024 }).split('\n').filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function artifactRoot(): string {
|
||||||
|
const line = readFileSync('.env.postgres', 'utf8').split(/\r?\n/).find(item => item.startsWith('ASSESSOR_ARTIFACT_ROOT='));
|
||||||
|
if (!line) throw new Error('artifact root not configured');
|
||||||
|
return line.slice('ASSESSOR_ARTIFACT_ROOT='.length).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode(bytes: Uint8Array): string {
|
||||||
|
if (bytes[0] === 0xff && bytes[1] === 0xfe) return new TextDecoder('utf-16le').decode(bytes);
|
||||||
|
if (bytes[0] === 0xfe && bytes[1] === 0xff) return new TextDecoder('utf-16be').decode(bytes);
|
||||||
|
return new TextDecoder('utf-8').decode(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Role only from unambiguous evidence in GPReport.xml; everything else stays unknown. */
|
||||||
|
function roleFromReport(report: string): { role: Role; wmiFilter: boolean } {
|
||||||
|
const text = report.replace(/<(\/?)[A-Za-z0-9]+:/g, '<$1');
|
||||||
|
const guid = /<Identifier[^>]*>\s*<Identifier[^>]*>([^<]+)</.exec(text)?.[1] ?? /<Identifier>([^<]+)<\/Identifier>/.exec(text)?.[1] ?? '';
|
||||||
|
const links = [...text.matchAll(/<LinksTo>([\s\S]*?)<\/LinksTo>/g)].map(match => ({
|
||||||
|
path: /<SOMPath>([^<]*)<\/SOMPath>/.exec(match[1])?.[1] ?? '',
|
||||||
|
enabled: !/<Enabled>\s*false\s*<\/Enabled>/i.test(match[1])
|
||||||
|
})).filter(link => link.enabled);
|
||||||
|
const wmiFilter = /<FilterName>[^<]+<\/FilterName>/.test(text);
|
||||||
|
if (guid.trim().toUpperCase() === DEFAULT_DC_POLICY) return { role: 'Domain Controller', wmiFilter };
|
||||||
|
if (!links.length) return { role: 'unlinked', wmiFilter };
|
||||||
|
if (links.every(link => /\/Domain Controllers$/i.test(link.path.trim()))) return { role: 'Domain Controller', wmiFilter };
|
||||||
|
// A link to the domain itself (no OU segment) applies to every role.
|
||||||
|
if (links.some(link => !link.path.includes('/'))) return { role: 'all', wmiFilter };
|
||||||
|
return { role: 'unknown', wmiFilter };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function list(): Promise<void> {
|
||||||
|
// Shown only on your own screen; do not paste this list.
|
||||||
|
const projects = await get<Array<{ id: string; name: string }>>('/projects');
|
||||||
|
for (const project of projects) console.log(project.id, ' ', project.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function measure(projectId: string): Promise<void> {
|
||||||
|
if (!/^project-[a-z0-9]+$/.test(projectId)) throw new Error('invalid project id');
|
||||||
|
const overview = await get<{ current: { evaluationRunIds: string[] } }>(`/projects/${projectId}/policy-analysis-overview`);
|
||||||
|
const runIds = new Set(overview.current.evaluationRunIds);
|
||||||
|
const evaluations = (await get<{ records: any[] }>(`/projects/${projectId}/policy-evaluations`)).records.filter(item => runIds.has(item.run.id));
|
||||||
|
const aggregations = (await get<{ records: any[] } | any[]>(`/projects/${projectId}/policy-snapshot-aggregations`));
|
||||||
|
const aggregationRecords: any[] = Array.isArray(aggregations) ? aggregations : aggregations.records;
|
||||||
|
|
||||||
|
// Backup instance -> backup folder, backup folder -> role from its GPReport.xml.
|
||||||
|
const instanceFolder = new Map<string, string>();
|
||||||
|
for (const line of sql(`select json_build_object('id', instance_id, 'data', data)::text from assessor_gpo_backup_instances where project_id = '${projectId}'`)) {
|
||||||
|
const item = JSON.parse(line);
|
||||||
|
const folder = JSON.stringify(item.data).match(/\{[0-9A-Fa-f-]{36}\}/)?.[0];
|
||||||
|
if (folder) instanceFolder.set(item.id, folder.toUpperCase());
|
||||||
|
}
|
||||||
|
const folderRole = new Map<string, { role: Role; wmiFilter: boolean }>();
|
||||||
|
const root = artifactRoot();
|
||||||
|
for (const line of sql(`select storage_key || '|' || coalesce(relative_path, '') from assessor_import_artifacts where project_id = '${projectId}' and storage_kind = 'backend-controlled'`)) {
|
||||||
|
const [key, relativePath] = line.split('|');
|
||||||
|
if (!/^[0-9a-f-]{36}$/i.test(key)) continue;
|
||||||
|
let bytes: Uint8Array;
|
||||||
|
try { bytes = readFileSync(join(root, 'objects', `${key}.bin`)); } catch { continue; }
|
||||||
|
const reports: Array<{ path: string; bytes: Uint8Array }> = [];
|
||||||
|
if (bytes[0] === 0x50 && bytes[1] === 0x4b) {
|
||||||
|
const paths = inventoryZipArchive(bytes).entries.filter(entry => entry.kind === 'file' && /(^|\/)gpreport\.xml$/i.test(entry.relativePath)).map(entry => entry.relativePath);
|
||||||
|
if (paths.length) for (const file of readVerifiedZipFiles(bytes, paths, undefined, 256 * 1024 * 1024)) reports.push({ path: file.entry.relativePath, bytes: file.bytes });
|
||||||
|
} else if (/(^|[\\/])gpreport\.xml$/i.test(relativePath)) reports.push({ path: relativePath, bytes });
|
||||||
|
for (const report of reports) {
|
||||||
|
const folder = report.path.match(/\{[0-9A-Fa-f-]{36}\}/)?.[0];
|
||||||
|
if (folder) folderRole.set(folder.toUpperCase(), roleFromReport(decode(report.bytes)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateRoles = new Map<string, string | undefined>();
|
||||||
|
for (const baselineId of new Set(evaluations.flatMap(item => item.results.map((result: any) => result.baselineId)))) {
|
||||||
|
const response = await get<{ records?: any[] }>(`/reference-data/microsoft-security-baselines/${encodeURIComponent(String(baselineId))}/analysis`);
|
||||||
|
for (const record of response.records ?? [response]) for (const setting of (record.analysis ?? record).settings ?? []) {
|
||||||
|
templateRoles.set(setting.id, baselineTemplateContext([...new Set<string>((setting.template?.names ?? []).map((name: any) => name.value))])?.role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusOf = (comparison: any) => policyComparisonDisposition(comparison) === 'not-configured' ? 'Not configured'
|
||||||
|
: policyComparisonDisposition(comparison) === 'misconfigured' ? 'Fail'
|
||||||
|
: comparison.baselineVariantAssessment?.decision === 'all-pass' && comparison.classification === 'configured_match' ? 'Pass' : 'Review required';
|
||||||
|
const rank: Record<string, number> = { ...policyWorkVerdictPriority, 'No effect': -1, 'Not applicable': -2 };
|
||||||
|
const top = (items: string[]) => items.reduce((left, right) => (rank[right] ?? 0) > (rank[left] ?? 0) ? right : left);
|
||||||
|
|
||||||
|
// Per setting: comparison relations grouped by recognized template role (upper bound of role dependence).
|
||||||
|
const relationsByTemplateRole = new Map<string, Map<string, Set<string>>>();
|
||||||
|
const settings = new Map<string, { roles: Set<Role>; current: string[]; simulated: string[] }>();
|
||||||
|
let variants = 0, variantsWithoutRole = 0;
|
||||||
|
for (const evaluation of evaluations) {
|
||||||
|
const aggregation = aggregationRecords.find(item => item.run.id === evaluation.run.aggregationRunId);
|
||||||
|
const observationRoles = new Map<string, Set<Role>>();
|
||||||
|
for (const state of aggregation?.states ?? []) for (const observation of state.observationIds) {
|
||||||
|
const roles = observationRoles.get(observation) ?? new Set<Role>();
|
||||||
|
for (const instance of state.backupInstanceIds) roles.add(folderRole.get(instanceFolder.get(instance) ?? '')?.role ?? 'unknown');
|
||||||
|
observationRoles.set(observation, roles);
|
||||||
|
}
|
||||||
|
for (const comparison of evaluation.results) {
|
||||||
|
const key = JSON.stringify([evaluation.run.aggregationRunId, comparison.identity.scope, comparison.identity.id]);
|
||||||
|
const roles = new Set<Role>(comparison.observationIds.flatMap((id: string) => [...(observationRoles.get(id) ?? [])]));
|
||||||
|
const row = settings.get(key) ?? { roles: new Set<Role>(), current: [], simulated: [] };
|
||||||
|
roles.forEach(role => row.roles.add(role));
|
||||||
|
const current = statusOf(comparison);
|
||||||
|
let simulated = current;
|
||||||
|
const byRole = relationsByTemplateRole.get(key) ?? new Map<string, Set<string>>();
|
||||||
|
for (const variant of comparison.baselineVariantAssessment?.variants ?? []) {
|
||||||
|
variants++;
|
||||||
|
const templateRole = templateRoles.get(variant.settingId);
|
||||||
|
if (!templateRole) variantsWithoutRole++;
|
||||||
|
const relations = byRole.get(templateRole ?? 'unrecognized') ?? new Set<string>();
|
||||||
|
relations.add(variant.comparator.relation);
|
||||||
|
byRole.set(templateRole ?? 'unrecognized', relations);
|
||||||
|
}
|
||||||
|
relationsByTemplateRole.set(key, byRole);
|
||||||
|
if (current !== 'Not configured' && roles.size === 1 && roles.has('unlinked')) simulated = 'No effect';
|
||||||
|
else if (current !== 'Not configured' && roles.size === 1 && roles.has('Domain Controller') && comparison.baselineVariantAssessment) {
|
||||||
|
// Unknown template roles are kept: an unrecognized template could still apply.
|
||||||
|
const kept = comparison.baselineVariantAssessment.variants.filter((variant: any) => {
|
||||||
|
const role = templateRoles.get(variant.settingId);
|
||||||
|
return role === undefined || role === 'Domain Controller';
|
||||||
|
});
|
||||||
|
simulated = !kept.length ? 'Not applicable'
|
||||||
|
: kept.every((variant: any) => variant.comparator.relation === 'match') ? 'Pass'
|
||||||
|
: kept.every((variant: any) => variant.comparator.relation === 'different') ? 'Fail' : 'Review required';
|
||||||
|
}
|
||||||
|
row.current.push(current);
|
||||||
|
row.simulated.push(simulated);
|
||||||
|
settings.set(key, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = [...settings.entries()].map(([key, row]) => {
|
||||||
|
const real = row.simulated.filter(status => status !== 'Not applicable');
|
||||||
|
// A Fail is role-dependent when every template of at least one role matches (or it has
|
||||||
|
// matching templates of an unrecognized role): the verdict could change with the target role.
|
||||||
|
const byRole = relationsByTemplateRole.get(key) ?? new Map<string, Set<string>>();
|
||||||
|
const roleMatches = [...byRole.entries()].filter(([role, relations]) => role !== 'unrecognized'
|
||||||
|
&& relations.size === 1 && relations.has('match')).length;
|
||||||
|
const unrecognizedMatch = byRole.get('unrecognized')?.has('match') ?? false;
|
||||||
|
const dependence = roleMatches ? 'role-dependent (a recognized role matches)'
|
||||||
|
: unrecognizedMatch ? 'possibly role-dependent (only unrecognized templates match)'
|
||||||
|
: 'role-independent (no template matches)';
|
||||||
|
return { roles: row.roles, current: top(row.current), simulated: real.length ? top(real) : 'Not applicable', dependence };
|
||||||
|
});
|
||||||
|
const count = (values: string[]) => values.reduce<Record<string, number>>((result, value) => (result[value] = (result[value] ?? 0) + 1, result), {});
|
||||||
|
const changes = count(rows.filter(row => row.current !== row.simulated).map(row => `${row.current} -> ${row.simulated}`));
|
||||||
|
const gpoRoles = count([...new Set(instanceFolder.values())].map(folder => folderRole.get(folder)?.role ?? 'no GPReport.xml'));
|
||||||
|
const failRoles = count(rows.filter(row => row.current === 'Fail').map(row => [...row.roles].sort().join('+') || 'no customer source'));
|
||||||
|
|
||||||
|
console.log('=== GPO role impact (numbers only) ===');
|
||||||
|
console.log('GPO backups by derived role ', JSON.stringify(gpoRoles));
|
||||||
|
console.log('GPO backups with a WMI filter ', [...folderRole.values()].filter(item => item.wmiFilter).length);
|
||||||
|
console.log('Settings (rolled up) ', rows.length);
|
||||||
|
console.log('Current verdicts ', JSON.stringify(count(rows.map(row => row.current))));
|
||||||
|
console.log('Verdicts with target role ', JSON.stringify(count(rows.map(row => row.simulated))));
|
||||||
|
console.log('Changed settings ', JSON.stringify(changes));
|
||||||
|
console.log('Current Fail by contributing roles ', JSON.stringify(failRoles));
|
||||||
|
console.log('Baseline variants without template role', `${variantsWithoutRole} of ${variants}`);
|
||||||
|
console.log('Current Fail by role dependence ', JSON.stringify(count(rows.filter(row => row.current === 'Fail').map(row => row.dependence))));
|
||||||
|
}
|
||||||
|
|
||||||
|
const argument = process.argv[2];
|
||||||
|
if (!argument) console.log('Usage: npx vite-node scripts/gpo-role-impact.ts --list | <project-id>');
|
||||||
|
(argument === '--list' ? list() : argument ? measure(argument) : Promise.resolve())
|
||||||
|
.catch((error: unknown) => {
|
||||||
|
// Never print an error message: it could contain source content.
|
||||||
|
console.log('Stopped:', error instanceof Error ? error.constructor.name : typeof error);
|
||||||
|
process.exitCode = 1;
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
import { convertEnterpriseStix } from '../src/features/mitre-graph/mitre-stix.ts';
|
||||||
|
|
||||||
|
const input = process.argv[2];
|
||||||
|
if (!input) throw new Error('Usage: node --experimental-strip-types scripts/update-mitre-asset.mts <official Enterprise ATT&CK STIX JSON>');
|
||||||
|
const graph = convertEnterpriseStix(JSON.parse(await readFile(input, 'utf8')));
|
||||||
|
const output = resolve(`src/assets/data/mitre-enterprise-v${graph.version}.json`);
|
||||||
|
await writeFile(output, JSON.stringify(graph) + '\n');
|
||||||
|
console.log(`${output}: ${graph.tacticOrder.length} tactics, ${graph.techniques.length} techniques, ${graph.entities.length} entities`);
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
|
import { createHash } from 'node:crypto';
|
||||||
|
import { attackReferenceCatalog, d3fendReferenceCatalog } from '../src/references/catalog-converters.ts';
|
||||||
|
const [attackPath, d3fendPath] = process.argv.slice(2);
|
||||||
|
if (!attackPath || !d3fendPath) throw new Error('Usage: node --experimental-strip-types scripts/update-reference-assets.mts <Enterprise STIX> <D3FEND JSON-LD>');
|
||||||
|
for (const [path, convert, sourceUrl, name] of [
|
||||||
|
[attackPath, attackReferenceCatalog, 'https://raw.githubusercontent.com/mitre-attack/attack-stix-data/master/enterprise-attack/enterprise-attack-19.2.json', 'attack-reference'],
|
||||||
|
[d3fendPath, d3fendReferenceCatalog, 'https://d3fend.mitre.org/ontologies/d3fend/1.6.0/d3fend.json', 'd3fend-reference']
|
||||||
|
] as const) {
|
||||||
|
const bytes = await readFile(path);
|
||||||
|
const catalog = convert(JSON.parse(bytes.toString('utf8')), sourceUrl, createHash('sha256').update(bytes).digest('hex'));
|
||||||
|
const output = `src/assets/data/${name}-v${catalog.version}.json`;
|
||||||
|
await writeFile(output, JSON.stringify(catalog) + '\n');
|
||||||
|
console.log(`${output}: ${catalog.entries.length} entries`);
|
||||||
|
}
|
||||||
|
|
@ -40,6 +40,10 @@ export interface ArtifactStoreReconciliationResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BackendArtifactStore {
|
export interface BackendArtifactStore {
|
||||||
|
/** Cheap live accessibility check; integrity is still verified when reading. */
|
||||||
|
availability(storageKey: string): Promise<'available' | 'missing' | 'unavailable'>;
|
||||||
|
/** Restore only an absent object, with the original identity and checksum. */
|
||||||
|
restore(request: ReadArtifactRequest & { bytes: Uint8Array }): Promise<void>;
|
||||||
put(request: StoreArtifactRequest): Promise<StoredArtifactReceipt>;
|
put(request: StoreArtifactRequest): Promise<StoredArtifactReceipt>;
|
||||||
read(request: ReadArtifactRequest): Promise<Uint8Array>;
|
read(request: ReadArtifactRequest): Promise<Uint8Array>;
|
||||||
remove(storageKey: string): Promise<boolean>;
|
remove(storageKey: string): Promise<boolean>;
|
||||||
|
|
@ -110,6 +114,41 @@ export class FileSystemBackendArtifactStore implements BackendArtifactStore {
|
||||||
this.objectRoot = join(this.root, 'objects');
|
this.objectRoot = join(this.root, 'objects');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async availability(storageKey: string): Promise<'available' | 'missing' | 'unavailable'> {
|
||||||
|
try {
|
||||||
|
const path = this.objectPath(storageKey);
|
||||||
|
for (const directory of [this.root, this.objectRoot]) {
|
||||||
|
const information = await lstat(directory);
|
||||||
|
if (!information.isDirectory() || information.isSymbolicLink()) return 'unavailable';
|
||||||
|
}
|
||||||
|
const information = await lstat(path);
|
||||||
|
if (!information.isFile() || information.isSymbolicLink()) return 'unavailable';
|
||||||
|
const handle = await open(path, 'r');
|
||||||
|
await handle.close();
|
||||||
|
return 'available';
|
||||||
|
} catch (error) {
|
||||||
|
return (error as NodeJS.ErrnoException)?.code === 'ENOENT' ? 'missing' : 'unavailable';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async restore(request: ReadArtifactRequest & { bytes: Uint8Array }): Promise<void> {
|
||||||
|
const path = this.objectPath(request.storageKey);
|
||||||
|
// The complete store may have disappeared since a previous initialization.
|
||||||
|
this.initialization = undefined;
|
||||||
|
// put verifies the uploaded bytes before publication. link never overwrites
|
||||||
|
// an existing object, including one concurrently restored by another request.
|
||||||
|
const receipt = await this.put(request);
|
||||||
|
try {
|
||||||
|
try { await link(this.objectPath(receipt.storageKey), path); }
|
||||||
|
catch (error) {
|
||||||
|
if ((error as NodeJS.ErrnoException)?.code !== 'EEXIST') throw error;
|
||||||
|
}
|
||||||
|
await this.read(request);
|
||||||
|
} finally {
|
||||||
|
await this.remove(receipt.storageKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async put(request: StoreArtifactRequest): Promise<StoredArtifactReceipt> {
|
async put(request: StoreArtifactRequest): Promise<StoredArtifactReceipt> {
|
||||||
validateMaximumBytes(request.maximumBytes);
|
validateMaximumBytes(request.maximumBytes);
|
||||||
if (request.bytes.byteLength > request.maximumBytes) {
|
if (request.bytes.byteLength > request.maximumBytes) {
|
||||||
|
|
@ -156,8 +195,11 @@ export class FileSystemBackendArtifactStore implements BackendArtifactStore {
|
||||||
let information;
|
let information;
|
||||||
try {
|
try {
|
||||||
information = await lstat(path);
|
information = await lstat(path);
|
||||||
} catch {
|
} catch (error) {
|
||||||
throw storageError('artifact-store-object-missing', 'Das gespeicherte Artefakt wurde nicht gefunden.');
|
if ((error as NodeJS.ErrnoException)?.code === 'ENOENT') {
|
||||||
|
throw storageError('artifact-store-object-missing', 'Stored source is missing. Upload the unchanged original file again.');
|
||||||
|
}
|
||||||
|
throw storageError('artifact-store-read-failed', 'The artifact store cannot be accessed. Check the backend storage connection and permissions.');
|
||||||
}
|
}
|
||||||
if (!information.isFile() || information.isSymbolicLink()) throw storageError('artifact-store-object-rejected', 'Das gespeicherte Objekt ist keine reguläre Datei.');
|
if (!information.isFile() || information.isSymbolicLink()) throw storageError('artifact-store-object-rejected', 'Das gespeicherte Objekt ist keine reguläre Datei.');
|
||||||
if (information.size > request.maximumBytes) {
|
if (information.size > request.maximumBytes) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { RiskAcceptanceValidationError, type RiskAcceptanceCommand } from '../src/domain/risk-acceptance';
|
||||||
import { createHash } from 'node:crypto';
|
import { createHash } from 'node:crypto';
|
||||||
import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';
|
import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';
|
||||||
import { readFile, stat } from 'node:fs/promises';
|
import { readFile, stat } from 'node:fs/promises';
|
||||||
|
|
@ -13,6 +14,8 @@ import type {
|
||||||
} from '../src/persistence/assessor-repository';
|
} from '../src/persistence/assessor-repository';
|
||||||
import type { UnifiedResultQuery } from '../src/domain/unified-result';
|
import type { UnifiedResultQuery } from '../src/domain/unified-result';
|
||||||
import type { EnvironmentSnapshotQuery } from '../src/domain/environment-context';
|
import type { EnvironmentSnapshotQuery } from '../src/domain/environment-context';
|
||||||
|
import type { ClientInformationQuery } from '../src/domain/client-information';
|
||||||
|
import { clientInformationReadPortFrom } from '../src/persistence/client-information-read-port';
|
||||||
import type { Assessment, AssessmentProject, FindingReview, ImportFileRecord } from '../src/domain/canonical-model';
|
import type { Assessment, AssessmentProject, FindingReview, ImportFileRecord } from '../src/domain/canonical-model';
|
||||||
import type { LocalComparisonRule } from '../src/domain/comparison';
|
import type { LocalComparisonRule } from '../src/domain/comparison';
|
||||||
import { ImportError } from '../src/shared/errors';
|
import { ImportError } from '../src/shared/errors';
|
||||||
|
|
@ -25,11 +28,32 @@ import type { GpoPolicySnapshotService } from './gpo/gpo-policy-snapshot-service
|
||||||
import type { GpoPolicyEvaluationService } from './gpo/gpo-policy-evaluation-service';
|
import type { GpoPolicyEvaluationService } from './gpo/gpo-policy-evaluation-service';
|
||||||
import type { GpoPolicyFindingService } from './gpo/gpo-policy-finding-service';
|
import type { GpoPolicyFindingService } from './gpo/gpo-policy-finding-service';
|
||||||
import type { GpoPolicyAnalysisOverviewService } from './gpo/gpo-policy-analysis-overview-service';
|
import type { GpoPolicyAnalysisOverviewService } from './gpo/gpo-policy-analysis-overview-service';
|
||||||
|
import type { GpoAutomaticAnalysisService } from './gpo/gpo-automatic-analysis-service';
|
||||||
import type { MicrosoftSecurityBaselineSelectionContext } from '../src/domain/microsoft-security-baseline';
|
import type { MicrosoftSecurityBaselineSelectionContext } from '../src/domain/microsoft-security-baseline';
|
||||||
import { listProjectGpoBackupStatus } from './gpo/gpo-backup-status-service';
|
import { listProjectGpoBackupStatus } from './gpo/gpo-backup-status-service';
|
||||||
|
import type { BackendArtifactStore } from './artifacts/backend-artifact-store';
|
||||||
|
import { MitreCatalogError, type MitreCatalogService } from './reference-data/mitre-catalog-service';
|
||||||
|
import { ReferenceRecognitionService, deriveReferenceRun } from './reference-data/reference-recognition-service';
|
||||||
import { directImportExecutor, type ImportExecutor } from './import-executor';
|
import { directImportExecutor, type ImportExecutor } from './import-executor';
|
||||||
import { createCanonicalImportBatch } from '../src/importers/canonical-import';
|
import { createCanonicalImportBatch } from '../src/importers/canonical-import';
|
||||||
import { processingScopeIssues } from '../src/domain/processing-compatibility';
|
import { processingScopeIssues } from '../src/domain/processing-compatibility';
|
||||||
|
import { ASSESSOR_PRODUCT_VERSION } from '../src/domain/assessor-release';
|
||||||
|
import { assembleConsistentReportInput, createReportRun, ReportStateChangedError } from '../src/reporting/report-run';
|
||||||
|
import { reportReadPortFrom, type ReportReadPort } from '../src/reporting/report-read-port';
|
||||||
|
import type { FileSystemReportArchive } from './reporting/report-archive';
|
||||||
|
import { ContextStateChangedError, createAssessmentContext } from '../src/assessment-context/context-run';
|
||||||
|
import { publishContextReport } from '../src/assessment-context/context-report-publication';
|
||||||
|
import { ReportContextRequiredError } from '../src/reporting/report-context';
|
||||||
|
import type { AssessmentContext } from '../src/assessment-context/context-engine';
|
||||||
|
import { ASSESSMENT_CONTEXT_ENGINE } from '../src/assessment-context/context-engine';
|
||||||
|
import { ASSESSMENT_CONTEXT_CATALOG } from '../src/assessment-context/context-catalog';
|
||||||
|
import { buildWorkPackages, policyFindingKey, scannerFindingKey, REMEDIATION_ENGINE_VERSION, REMEDIATION_CATALOG_VERSION, type RemediationFinding } from '../src/remediation/remediation-engine';
|
||||||
|
import { remediationReadPortFrom, type RemediationReadPort } from '../src/remediation/remediation-read-port';
|
||||||
|
import { MemoryRemediationStore, type RemediationStore } from './remediation/remediation-store';
|
||||||
|
import { loadCanonicalProjectWorkspace } from '../src/features/project-import/load-canonical-project-workspace';
|
||||||
|
import { summarizeWorkspaceResults, type DatahubTimeslotResultMetrics } from '../src/features/project-import/datahub-scan-analysis';
|
||||||
|
import { profileForSheet } from '../src/features/project-import/workspace-profile';
|
||||||
|
import { buildReportProjection } from '../src/reporting/report-projection';
|
||||||
import { compareReimportSourceHashes } from '../src/domain/reimport-source';
|
import { compareReimportSourceHashes } from '../src/domain/reimport-source';
|
||||||
import {
|
import {
|
||||||
findPossibleDuplicateImportFile,
|
findPossibleDuplicateImportFile,
|
||||||
|
|
@ -63,7 +87,9 @@ import {
|
||||||
type PolicySnapshotAggregationCreateRequest,
|
type PolicySnapshotAggregationCreateRequest,
|
||||||
type PolicyEvaluationCreateRequest,
|
type PolicyEvaluationCreateRequest,
|
||||||
type PolicyFindingRunCreateRequest,
|
type PolicyFindingRunCreateRequest,
|
||||||
type ImportSourceValidationResponse
|
type ImportSourceValidationResponse,
|
||||||
|
type ReportRunRequest,
|
||||||
|
type ReportRunResponse
|
||||||
} from '../src/api/assessor-api-contract';
|
} from '../src/api/assessor-api-contract';
|
||||||
|
|
||||||
const DEFAULT_MAXIMUM_BODY_BYTES = 140 * 1024 * 1024;
|
const DEFAULT_MAXIMUM_BODY_BYTES = 140 * 1024 * 1024;
|
||||||
|
|
@ -114,6 +140,10 @@ export interface AuditEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AssessorHttpServerOptions {
|
export interface AssessorHttpServerOptions {
|
||||||
|
remediationStore?: RemediationStore | null;
|
||||||
|
remediationPort?: RemediationReadPort | null;
|
||||||
|
artifactStore?: BackendArtifactStore;
|
||||||
|
mitreCatalogService?: MitreCatalogService;
|
||||||
repository: AssessorRepository;
|
repository: AssessorRepository;
|
||||||
staticRoot: string;
|
staticRoot: string;
|
||||||
releaseId?: string;
|
releaseId?: string;
|
||||||
|
|
@ -129,7 +159,15 @@ export interface AssessorHttpServerOptions {
|
||||||
gpoPolicyEvaluationService?: GpoPolicyEvaluationService;
|
gpoPolicyEvaluationService?: GpoPolicyEvaluationService;
|
||||||
gpoPolicyFindingService?: GpoPolicyFindingService;
|
gpoPolicyFindingService?: GpoPolicyFindingService;
|
||||||
gpoPolicyAnalysisOverviewService?: GpoPolicyAnalysisOverviewService;
|
gpoPolicyAnalysisOverviewService?: GpoPolicyAnalysisOverviewService;
|
||||||
|
automaticGpoAnalysis?: GpoAutomaticAnalysisService;
|
||||||
microsoftSecurityBaselineService?: MicrosoftSecurityBaselineService;
|
microsoftSecurityBaselineService?: MicrosoftSecurityBaselineService;
|
||||||
|
/**
|
||||||
|
* Read-only port of the separate Report Engine. `undefined` narrows `repository` to its read methods
|
||||||
|
* (in-memory operation); `null` means the Report Engine is unavailable and ASSESSOR keeps running without it.
|
||||||
|
*/
|
||||||
|
reportPort?: ReportReadPort | null;
|
||||||
|
/** Write-once report archive outside the database; report runs are refused without it. */
|
||||||
|
reportArchive?: FileSystemReportArchive;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HttpError extends Error {
|
class HttpError extends Error {
|
||||||
|
|
@ -230,8 +268,10 @@ function unifiedQueryFrom(url: URL): UnifiedResultQuery {
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
audience: url.searchParams.get('audience') === 'internal-analysis' ? 'internal-analysis' : 'result-db',
|
||||||
projectId: url.searchParams.get('projectId') ?? undefined,
|
projectId: url.searchParams.get('projectId') ?? undefined,
|
||||||
assessmentId: url.searchParams.get('assessmentId') ?? undefined,
|
assessmentId: url.searchParams.get('assessmentId') ?? undefined,
|
||||||
|
assessmentIds: many('assessmentIds'),
|
||||||
scanRunId: url.searchParams.get('scanRunId') ?? undefined,
|
scanRunId: url.searchParams.get('scanRunId') ?? undefined,
|
||||||
scanRunIds: many('scanRunIds'),
|
scanRunIds: many('scanRunIds'),
|
||||||
evaluationRunId: url.searchParams.get('evaluationRunId') ?? undefined,
|
evaluationRunId: url.searchParams.get('evaluationRunId') ?? undefined,
|
||||||
|
|
@ -241,6 +281,7 @@ function unifiedQueryFrom(url: URL): UnifiedResultQuery {
|
||||||
outcome: many('outcome') as UnifiedResultQuery['outcome'],
|
outcome: many('outcome') as UnifiedResultQuery['outcome'],
|
||||||
technicalExecutionState: many('technicalExecutionState') as UnifiedResultQuery['technicalExecutionState'],
|
technicalExecutionState: many('technicalExecutionState') as UnifiedResultQuery['technicalExecutionState'],
|
||||||
resultType: many('resultType') as UnifiedResultQuery['resultType'],
|
resultType: many('resultType') as UnifiedResultQuery['resultType'],
|
||||||
|
policyClassification: many('policyClassification') as UnifiedResultQuery['policyClassification'],
|
||||||
isIoe: booleanValue('isIoe'),
|
isIoe: booleanValue('isIoe'),
|
||||||
deviation: booleanValue('deviation'),
|
deviation: booleanValue('deviation'),
|
||||||
mappingRuleVersion: many('mappingRuleVersion'),
|
mappingRuleVersion: many('mappingRuleVersion'),
|
||||||
|
|
@ -329,10 +370,15 @@ function importFailureMessage(error: unknown): string {
|
||||||
}
|
}
|
||||||
function statusForError(error: unknown): { status: number; code: string; message: string } {
|
function statusForError(error: unknown): { status: number; code: string; message: string } {
|
||||||
if (error instanceof HttpError) return error;
|
if (error instanceof HttpError) return error;
|
||||||
|
if (error instanceof MitreCatalogError) return { status: 502, code: error.code, message: error.message };
|
||||||
if (error instanceof ImportError) return { status: 422, code: error.diagnostics[0]?.code ?? 'import_rejected', message: error.message };
|
if (error instanceof ImportError) return { status: 422, code: error.diagnostics[0]?.code ?? 'import_rejected', message: error.message };
|
||||||
if (error instanceof RepositoryNotFoundError) return { status: 404, code: 'repository_not_found', message: error.message };
|
if (error instanceof RepositoryNotFoundError) return { status: 404, code: 'repository_not_found', message: error.message };
|
||||||
|
if (error instanceof ReportStateChangedError) return { status: 409, code: 'report_state_changed', message: error.message };
|
||||||
|
if (error instanceof ReportContextRequiredError) return { status: 409, code: 'report_context_required', message: error.message };
|
||||||
|
if (error instanceof ContextStateChangedError) return { status: 409, code: 'context_state_changed', message: error.message };
|
||||||
if (error instanceof RepositoryConflictError || error instanceof ImportSessionStateError) return { status: 409, code: 'repository_conflict', message: error.message };
|
if (error instanceof RepositoryConflictError || error instanceof ImportSessionStateError) return { status: 409, code: 'repository_conflict', message: error.message };
|
||||||
if (error instanceof RepositoryValidationError) return { status: 400, code: 'repository_validation', message: error.message };
|
if (error instanceof RepositoryValidationError) return { status: 400, code: 'repository_validation', message: error.message };
|
||||||
|
if (error instanceof RiskAcceptanceValidationError) return { status: 400, code: 'risk_acceptance_validation', message: error.message };
|
||||||
return { status: 500, code: 'internal_error', message: 'Die Anfrage konnte nicht verarbeitet werden.' };
|
return { status: 500, code: 'internal_error', message: 'Die Anfrage konnte nicht verarbeitet werden.' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,12 +407,28 @@ async function serveStatic(request: IncomingMessage, response: ServerResponse, s
|
||||||
|
|
||||||
export function createAssessorHttpServer(options: AssessorHttpServerOptions): Server {
|
export function createAssessorHttpServer(options: AssessorHttpServerOptions): Server {
|
||||||
const repository = options.repository;
|
const repository = options.repository;
|
||||||
|
const remediationStore = options.remediationStore === undefined ? new MemoryRemediationStore() : options.remediationStore;
|
||||||
|
const remediationPort = options.remediationPort === undefined ? remediationReadPortFrom(repository, async () => repository.revision) : options.remediationPort;
|
||||||
|
const clientInformation = clientInformationReadPortFrom(repository,
|
||||||
|
() => repository.getCommittedRevision?.() ?? Promise.resolve(repository.revision));
|
||||||
|
const reportPort = options.reportPort === undefined
|
||||||
|
? reportReadPortFrom(repository, async () => repository.revision) : options.reportPort;
|
||||||
|
const requireReportPort = async (projectId: string): Promise<ReportReadPort> => {
|
||||||
|
if (!reportPort) throw new HttpError(503, 'report_engine_unavailable', 'Die Report Engine ist nicht verfügbar.');
|
||||||
|
if (!await reportPort.getProject(projectId)) throw new RepositoryNotFoundError(`Projekt ${projectId} ist nicht vorhanden.`);
|
||||||
|
return reportPort;
|
||||||
|
};
|
||||||
|
const referenceRecognition = new ReferenceRecognitionService(repository, options.mitreCatalogService
|
||||||
|
? () => options.mitreCatalogService!.recognitionCatalogs() : undefined);
|
||||||
const maximumBodyBytes = options.maximumBodyBytes ?? DEFAULT_MAXIMUM_BODY_BYTES;
|
const maximumBodyBytes = options.maximumBodyBytes ?? DEFAULT_MAXIMUM_BODY_BYTES;
|
||||||
const now = options.now ?? (() => new Date());
|
const now = options.now ?? (() => new Date());
|
||||||
const importExecutor = options.importExecutor ?? directImportExecutor;
|
const importExecutor = options.importExecutor ?? directImportExecutor;
|
||||||
const previewTtlMs = options.previewTtlMs ?? DEFAULT_PREVIEW_TTL_MS;
|
const previewTtlMs = options.previewTtlMs ?? DEFAULT_PREVIEW_TTL_MS;
|
||||||
const importSessions = new Map<string, ImportSession>();
|
const importSessions = new Map<string, ImportSession>();
|
||||||
const importBatchPreviews = new Map<string, CachedImportBatchPreview>();
|
const importBatchPreviews = new Map<string, CachedImportBatchPreview>();
|
||||||
|
const timeslotMetricsCache = new Map<string, Promise<DatahubTimeslotResultMetrics | null>>();
|
||||||
|
// Context results per committed revision and exact scope; bounded, and a new revision never reuses an entry.
|
||||||
|
const assessmentContextCache = new Map<string, AssessmentContext>();
|
||||||
const auditEvents: AuditEvent[] = [];
|
const auditEvents: AuditEvent[] = [];
|
||||||
let requestSequence = 0;
|
let requestSequence = 0;
|
||||||
|
|
||||||
|
|
@ -388,13 +450,42 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
|
|
||||||
if (method === 'GET' && segments.length === 1 && segments[0] === 'health') {
|
if (method === 'GET' && segments.length === 1 && segments[0] === 'health') {
|
||||||
const result: AssessorHealthResponse = {
|
const result: AssessorHealthResponse = {
|
||||||
status: 'ok', application: 'ASSESSOR', apiVersion: 1,
|
status: 'ok', application: 'ASSESSOR', productVersion: ASSESSOR_PRODUCT_VERSION, apiVersion: 1,
|
||||||
releaseId: options.releaseId,
|
releaseId: options.releaseId,
|
||||||
developmentInstanceId: options.developmentInstanceId,
|
developmentInstanceId: options.developmentInstanceId,
|
||||||
repositoryRevision: repository.revision, persistence: options.persistence ?? 'memory'
|
repositoryRevision: repository.revision, persistence: options.persistence ?? 'memory'
|
||||||
};
|
};
|
||||||
status = 200; sendJson(response, status, result, requestId); return;
|
status = 200; sendJson(response, status, result, requestId); return;
|
||||||
}
|
}
|
||||||
|
if (segments.join('/') === 'reference-data/mitre-attack' && method === 'GET') {
|
||||||
|
if (!options.mitreCatalogService) throw new HttpError(503, 'mitre_catalog_unavailable', 'Der MITRE-Katalog ist nicht konfiguriert.');
|
||||||
|
status = 200; sendJson(response, status, await options.mitreCatalogService.current(), requestId); return;
|
||||||
|
}
|
||||||
|
if (segments[0] === 'projects' && segments[1] && segments[2] === 'references' && segments.length === 3 && method === 'GET') {
|
||||||
|
status = 200; sendJson(response, status, await referenceRecognition.status(segments[1]), requestId); return;
|
||||||
|
}
|
||||||
|
if (segments[0] === 'projects' && segments[1] && segments[2] === 'references' && segments[3] === 'recognize' && segments.length === 4 && method === 'POST') {
|
||||||
|
await readJson(request, maximumBodyBytes);
|
||||||
|
status = 200; sendJson(response, status, await referenceRecognition.reprocess(segments[1]), requestId); return;
|
||||||
|
}
|
||||||
|
if (segments.join('/') === 'reference-data/mitre-attack/refresh' && method === 'POST') {
|
||||||
|
if (!options.mitreCatalogService) throw new HttpError(503, 'mitre_catalog_unavailable', 'Der MITRE-Katalog ist nicht konfiguriert.');
|
||||||
|
await readJson<Record<string, never>>(request, maximumBodyBytes);
|
||||||
|
status = 200; sendJson(response, status, await options.mitreCatalogService.refresh(), requestId); return;
|
||||||
|
}
|
||||||
|
if (segments.join('/') === 'reference-data/mitre-attack/source' && method === 'GET') {
|
||||||
|
if (!options.mitreCatalogService) throw new HttpError(503, 'mitre_catalog_unavailable', 'Der MITRE-Katalog ist nicht konfiguriert.');
|
||||||
|
const source = await options.mitreCatalogService.officialSource();
|
||||||
|
status = 200;
|
||||||
|
response.statusCode = status;
|
||||||
|
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||||
|
response.setHeader('Content-Disposition', `attachment; filename="enterprise-attack-v${source.catalog.version}.json"`);
|
||||||
|
response.setHeader('Content-Length', source.bytes.byteLength);
|
||||||
|
response.setHeader('Cache-Control', 'no-store');
|
||||||
|
response.setHeader('X-Assessor-Request-Id', requestId);
|
||||||
|
response.end(source.bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (method === 'GET' && segments.length === 1 && segments[0] === 'audit-events') {
|
if (method === 'GET' && segments.length === 1 && segments[0] === 'audit-events') {
|
||||||
status = 200; sendJson(response, status, auditEvents.slice().reverse(), requestId); return;
|
status = 200; sendJson(response, status, auditEvents.slice().reverse(), requestId); return;
|
||||||
}
|
}
|
||||||
|
|
@ -589,7 +680,9 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
if (!body.replacesScanRunId && processingScopeIssues([...processingFiles, ...canonical.model.importFiles]).length) {
|
if (!body.replacesScanRunId && processingScopeIssues([...processingFiles, ...canonical.model.importFiles]).length) {
|
||||||
throw new HttpError(409, 'incompatible_processing_versions', 'Der gewählte Timeslot enthält unbekannte oder semantisch inkompatible Verarbeitungsstände. Bitte alle betroffenen Dateien mit dem aktuellen Profil in einen neuen Timeslot reimportieren.');
|
throw new HttpError(409, 'incompatible_processing_versions', 'Der gewählte Timeslot enthält unbekannte oder semantisch inkompatible Verarbeitungsstände. Bitte alle betroffenen Dateien mit dem aktuellen Profil in einen neuen Timeslot reimportieren.');
|
||||||
}
|
}
|
||||||
const committed = await repository.commitCanonicalImport({ ...canonical, replacesScanRunId: body.replacesScanRunId });
|
const referenceCatalogs = await referenceRecognition.catalogs();
|
||||||
|
canonical.model.referenceRuns = canonical.model.scanRuns.map(scan => deriveReferenceRun(canonical.model, scan.id, referenceCatalogs, importedAt));
|
||||||
|
const committed = await repository.commitCanonicalImport({ ...canonical, referenceCatalogs, replacesScanRunId: body.replacesScanRunId });
|
||||||
const fingerprints = await repository.findImportFingerprints(body.entries.map(entry => entry.sha256));
|
const fingerprints = await repository.findImportFingerprints(body.entries.map(entry => entry.sha256));
|
||||||
importBatchPreviews.delete(body.batchId);
|
importBatchPreviews.delete(body.batchId);
|
||||||
const result: ImportBatchCompleteResponse = {
|
const result: ImportBatchCompleteResponse = {
|
||||||
|
|
@ -601,7 +694,8 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
scanRunId: committed.scanRunId,
|
scanRunId: committed.scanRunId,
|
||||||
replacedScanRunId: body.replacesScanRunId,
|
replacedScanRunId: body.replacesScanRunId,
|
||||||
importedFiles: committed.importedFiles,
|
importedFiles: committed.importedFiles,
|
||||||
importedOccurrences: committed.importedOccurrences
|
importedOccurrences: committed.importedOccurrences,
|
||||||
|
reimportReviewTransfer: committed.reimportReviewTransfer
|
||||||
};
|
};
|
||||||
status = 200; sendJson(response, status, result, requestId); return;
|
status = 200; sendJson(response, status, result, requestId); return;
|
||||||
}
|
}
|
||||||
|
|
@ -739,6 +833,16 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
status = 200; sendJson(response, status, { version: 1, record }, requestId); return;
|
status = 200; sendJson(response, status, { version: 1, record }, requestId); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (segments[0] === 'projects' && segments[2] === 'gpo-backups' && segments.length === 5 && segments[4] === 'evaluate' && method === 'POST') {
|
||||||
|
if (!options.automaticGpoAnalysis) throw new HttpError(503, 'gpo_evaluation_unavailable', 'GPO evaluation unavailable.');
|
||||||
|
await readJson<Record<string, never>>(request, maximumBodyBytes);
|
||||||
|
const artifact = (await repository.listImportArtifacts(segments[1])).find(item => item.id === segments[3]);
|
||||||
|
if (!artifact) throw new RepositoryNotFoundError('GPO source missing.');
|
||||||
|
const runs = (await repository.listProcessingRuns(artifact.id)).filter(item => item.projectId === segments[1])
|
||||||
|
.sort((a, b) => b.completedAt.localeCompare(a.completedAt) || b.id.localeCompare(a.id));
|
||||||
|
if (!runs[0]) throw new HttpError(409, 'gpo_processing_required', 'Import processing required.');
|
||||||
|
status = 200; sendJson(response, status, await options.automaticGpoAnalysis.run(segments[1], runs[0].id), requestId); return;
|
||||||
|
}
|
||||||
if (segments[0] === 'projects' && segments[2] === 'gpo-backups' && segments.length === 5 && segments[4] === 'reprocess' && method === 'POST') {
|
if (segments[0] === 'projects' && segments[2] === 'gpo-backups' && segments.length === 5 && segments[4] === 'reprocess' && method === 'POST') {
|
||||||
if (!options.gpoBackupManagementService) throw new HttpError(503, 'gpo_management_unavailable', 'Die GPO-Verwaltung ist nicht konfiguriert.');
|
if (!options.gpoBackupManagementService) throw new HttpError(503, 'gpo_management_unavailable', 'Die GPO-Verwaltung ist nicht konfiguriert.');
|
||||||
const body = await readJson<{ expectedLatestProcessingRunId: string }>(request, maximumBodyBytes);
|
const body = await readJson<{ expectedLatestProcessingRunId: string }>(request, maximumBodyBytes);
|
||||||
|
|
@ -758,11 +862,11 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
if (!options.gpoBackupManagementService) throw new HttpError(503, 'gpo_management_unavailable', 'Die GPO-Verwaltung ist nicht konfiguriert.');
|
if (!options.gpoBackupManagementService) throw new HttpError(503, 'gpo_management_unavailable', 'Die GPO-Verwaltung ist nicht konfiguriert.');
|
||||||
const body = await readJson<{ artifactId: string; confirmDelete: true }>(request, maximumBodyBytes);
|
const body = await readJson<{ artifactId: string; confirmDelete: true }>(request, maximumBodyBytes);
|
||||||
if (!body || body.confirmDelete !== true || body.artifactId !== segments[3]) throw new HttpError(400, 'gpo_delete_confirmation_invalid', 'Die Löschung wurde nicht für das exakte GPO-Artefakt bestätigt.');
|
if (!body || body.confirmDelete !== true || body.artifactId !== segments[3]) throw new HttpError(400, 'gpo_delete_confirmation_invalid', 'Die Löschung wurde nicht für das exakte GPO-Artefakt bestätigt.');
|
||||||
const result = await options.gpoBackupManagementService.deleteFailed(segments[1], segments[3]);
|
const result = await options.gpoBackupManagementService.delete(segments[1], segments[3]);
|
||||||
status = 200; sendJson(response, status, result, requestId); return;
|
status = 200; sendJson(response, status, result, requestId); return;
|
||||||
}
|
}
|
||||||
if (segments[0] === 'projects' && segments[2] === 'gpo-backups' && segments.length === 4 && segments[3] === 'status' && method === 'GET') {
|
if (segments[0] === 'projects' && segments[2] === 'gpo-backups' && segments.length === 4 && segments[3] === 'status' && method === 'GET') {
|
||||||
const result = await listProjectGpoBackupStatus(repository, segments[1], now().toISOString());
|
const result = await listProjectGpoBackupStatus(repository, segments[1], now().toISOString(), options.artifactStore);
|
||||||
status = 200; sendJson(response, status, result, requestId); return;
|
status = 200; sendJson(response, status, result, requestId); return;
|
||||||
}
|
}
|
||||||
if (segments[0] === 'projects' && segments[2] === 'gpo-backups' && segments.length === 3 && method === 'POST') {
|
if (segments[0] === 'projects' && segments[2] === 'gpo-backups' && segments.length === 3 && method === 'POST') {
|
||||||
|
|
@ -774,12 +878,22 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
if (!isValidImportBase64(body.bytesBase64)) {
|
if (!isValidImportBase64(body.bytesBase64)) {
|
||||||
throw new HttpError(400, 'invalid_file_encoding', 'Der GPO-Container ist nicht gültig base64-kodiert.');
|
throw new HttpError(400, 'invalid_file_encoding', 'Der GPO-Container ist nicht gültig base64-kodiert.');
|
||||||
}
|
}
|
||||||
|
if (body.restoreArtifactId !== undefined && (typeof body.restoreArtifactId !== 'string' || !body.restoreArtifactId.trim())) {
|
||||||
|
throw new HttpError(400, 'invalid_gpo_restore', 'A valid GPO source identity is required.');
|
||||||
|
}
|
||||||
const bytes = Buffer.from(body.bytesBase64, 'base64');
|
const bytes = Buffer.from(body.bytesBase64, 'base64');
|
||||||
if (bytes.byteLength !== body.sizeBytes) throw new HttpError(400, 'file_size_mismatch', 'Metadaten und tatsächliche GPO-Dateigröße stimmen nicht überein.');
|
if (bytes.byteLength !== body.sizeBytes) throw new HttpError(400, 'file_size_mismatch', 'Metadaten und tatsächliche GPO-Dateigröße stimmen nicht überein.');
|
||||||
const result: GpoBackupSubmissionResponse = await options.gpoBackupSubmissionService.submit({
|
const result: GpoBackupSubmissionResponse = await options.gpoBackupSubmissionService.submit({
|
||||||
projectId: segments[1], assessmentId: body.assessmentId, fileName: body.fileName, bytes, receivedAt: now().toISOString()
|
projectId: segments[1], assessmentId: body.assessmentId, restoreArtifactId: body.restoreArtifactId, fileName: body.fileName, bytes, receivedAt: now().toISOString()
|
||||||
});
|
});
|
||||||
status = 202; sendJson(response, status, result, requestId); return;
|
const evaluation = result.deduplicated && !result.sourceRestored && !body.restoreArtifactId && options.automaticGpoAnalysis
|
||||||
|
? await (async () => {
|
||||||
|
const runs = (await repository.listProcessingRuns(result.artifactId))
|
||||||
|
.sort((left, right) => Date.parse(right.completedAt) - Date.parse(left.completedAt) || right.id.localeCompare(left.id));
|
||||||
|
return runs[0] ? options.automaticGpoAnalysis!.run(segments[1], runs[0].id) : undefined;
|
||||||
|
})()
|
||||||
|
: undefined;
|
||||||
|
status = 202; sendJson(response, status, { ...result, evaluation }, requestId); return;
|
||||||
}
|
}
|
||||||
if (segments.length === 1 && segments[0] === 'projects' && method === 'GET') {
|
if (segments.length === 1 && segments[0] === 'projects' && method === 'GET') {
|
||||||
status = 200; sendJson(response, status, await repository.listProjects(), requestId); return;
|
status = 200; sendJson(response, status, await repository.listProjects(), requestId); return;
|
||||||
|
|
@ -800,8 +914,8 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
if (!body || typeof body.replacementScanRunId !== 'string' || !body.replacementScanRunId.trim()) {
|
if (!body || typeof body.replacementScanRunId !== 'string' || !body.replacementScanRunId.trim()) {
|
||||||
throw new HttpError(400, 'replacement_scan_required', 'Der neu importierte Ersatzscan ist erforderlich.');
|
throw new HttpError(400, 'replacement_scan_required', 'Der neu importierte Ersatzscan ist erforderlich.');
|
||||||
}
|
}
|
||||||
await repository.adoptScanRunReplacement(segments[1], segments[3], body.replacementScanRunId, now().toISOString());
|
const reimportReviewTransfer = await repository.adoptScanRunReplacement(segments[1], segments[3], body.replacementScanRunId, now().toISOString());
|
||||||
status = 200; sendJson(response, status, { repositoryRevision: repository.revision }, requestId); return;
|
status = 200; sendJson(response, status, { repositoryRevision: repository.revision, reimportReviewTransfer }, requestId); return;
|
||||||
}
|
}
|
||||||
if (segments[0] === 'projects' && segments[2] === 'scan-runs' && segments.length === 4 && method === 'DELETE') {
|
if (segments[0] === 'projects' && segments[2] === 'scan-runs' && segments.length === 4 && method === 'DELETE') {
|
||||||
const confirmation = await readJson<{ projectId: string; scanRunId: string; cascade: true }>(request, maximumBodyBytes);
|
const confirmation = await readJson<{ projectId: string; scanRunId: string; cascade: true }>(request, maximumBodyBytes);
|
||||||
|
|
@ -816,6 +930,8 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
if (segments[0] === 'projects' && segments.length === 2 && method === 'DELETE') {
|
if (segments[0] === 'projects' && segments.length === 2 && method === 'DELETE') {
|
||||||
const confirmation = await readJson<{ projectId: string; cascade: true }>(request, maximumBodyBytes);
|
const confirmation = await readJson<{ projectId: string; cascade: true }>(request, maximumBodyBytes);
|
||||||
const deleted = await repository.deleteProject(segments[1], confirmation);
|
const deleted = await repository.deleteProject(segments[1], confirmation);
|
||||||
|
// Archived reports of a client are removed together with the client (idempotent on retry).
|
||||||
|
await options.reportArchive?.deleteProject(segments[1]);
|
||||||
status = 200; sendJson(response, status, { deleted, repositoryRevision: repository.revision }, requestId); return;
|
status = 200; sendJson(response, status, { deleted, repositoryRevision: repository.revision }, requestId); return;
|
||||||
}
|
}
|
||||||
if (segments[0] === 'projects' && segments[2] === 'assessment-history' && segments.length === 3 && method === 'GET') {
|
if (segments[0] === 'projects' && segments[2] === 'assessment-history' && segments.length === 3 && method === 'GET') {
|
||||||
|
|
@ -827,6 +943,29 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
scanRuns: await repository.listProjectScanRuns(segments[1])
|
scanRuns: await repository.listProjectScanRuns(segments[1])
|
||||||
}, requestId); return;
|
}, requestId); return;
|
||||||
}
|
}
|
||||||
|
if (segments[0] === 'projects' && segments[2] === 'client-information' && segments.length === 3 && method === 'GET') {
|
||||||
|
const params = url.searchParams;
|
||||||
|
const allowed = new Set(['assessmentId', 'scanRunIds', 'includeSuperseded', 'areas', 'properties', 'subjectTypes', 'subjectIds', 'toolIds', 'states', 'search', 'offset', 'limit', 'sectionOffset', 'sectionLimit']);
|
||||||
|
if ([...params.keys()].some(key => !allowed.has(key))) throw new RepositoryValidationError('Unknown Client Information query parameter.');
|
||||||
|
const scalarKeys = ['assessmentId', 'includeSuperseded', 'search', 'offset', 'limit', 'sectionOffset', 'sectionLimit'];
|
||||||
|
if (scalarKeys.some(key => params.getAll(key).length > 1)) throw new RepositoryValidationError('Repeated scalar Client Information parameter.');
|
||||||
|
const history = params.get('includeSuperseded');
|
||||||
|
if (history !== null && history !== 'true' && history !== 'false') throw new RepositoryValidationError('Invalid history selector.');
|
||||||
|
const numeric = (key: string) => {
|
||||||
|
const value = params.get(key);
|
||||||
|
if (value !== null && !/^\d+$/.test(value)) throw new RepositoryValidationError('Invalid Client Information pagination.');
|
||||||
|
return value === null ? undefined : Number(value);
|
||||||
|
};
|
||||||
|
const query: ClientInformationQuery = {
|
||||||
|
projectId: segments[1], assessmentId: params.get('assessmentId') ?? undefined,
|
||||||
|
scanRunIds: params.getAll('scanRunIds'), includeSuperseded: history === 'true',
|
||||||
|
areas: params.getAll('areas') as ClientInformationQuery['areas'], properties: params.getAll('properties'),
|
||||||
|
subjectTypes: params.getAll('subjectTypes'), subjectIds: params.getAll('subjectIds'), toolIds: params.getAll('toolIds'),
|
||||||
|
states: params.getAll('states') as ClientInformationQuery['states'], search: params.get('search') ?? undefined,
|
||||||
|
offset: numeric('offset'), limit: numeric('limit'), sectionOffset: numeric('sectionOffset'), sectionLimit: numeric('sectionLimit')
|
||||||
|
};
|
||||||
|
status = 200; sendJson(response, status, await clientInformation.queryClientInformation(query), requestId); return;
|
||||||
|
}
|
||||||
if (segments[0] === 'projects' && segments[2] === 'environment-snapshot' && segments.length === 3 && method === 'GET') {
|
if (segments[0] === 'projects' && segments[2] === 'environment-snapshot' && segments.length === 3 && method === 'GET') {
|
||||||
const snapshotQuery: EnvironmentSnapshotQuery = {
|
const snapshotQuery: EnvironmentSnapshotQuery = {
|
||||||
projectId: segments[1],
|
projectId: segments[1],
|
||||||
|
|
@ -857,6 +996,157 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
if (segments[0] === 'projects' && segments[2] === 'export' && segments.length === 3 && method === 'GET') {
|
if (segments[0] === 'projects' && segments[2] === 'export' && segments.length === 3 && method === 'GET') {
|
||||||
status = 200; sendJson(response, status, await repository.exportProject(segments[1]), requestId); return;
|
status = 200; sendJson(response, status, await repository.exportProject(segments[1]), requestId); return;
|
||||||
}
|
}
|
||||||
|
if (segments[0] === 'projects' && segments[2] === 'report-projection' && segments.length === 3 && method === 'GET') {
|
||||||
|
const assessmentIds = url.searchParams.getAll('assessmentIds');
|
||||||
|
const scanRunIds = url.searchParams.getAll('scanRunIds');
|
||||||
|
const { input } = await assembleConsistentReportInput(await requireReportPort(segments[1]), {
|
||||||
|
projectId: segments[1], generatedAt: now().toISOString(),
|
||||||
|
assessmentIds: assessmentIds.length ? assessmentIds : undefined,
|
||||||
|
scanRunIds: scanRunIds.length ? scanRunIds : undefined
|
||||||
|
});
|
||||||
|
status = 200; sendJson(response, status, buildReportProjection(input), requestId); return;
|
||||||
|
}
|
||||||
|
if (segments[0] === 'projects' && segments[1] && segments[2] === 'work-packages' && segments.length === 3 && method === 'GET') {
|
||||||
|
if (!remediationStore || !remediationPort) throw new HttpError(503, 'remediation_unavailable', 'Remediation Engine is not available.');
|
||||||
|
const projectId = segments[1];
|
||||||
|
if (!await remediationPort.getProject(projectId)) throw new RepositoryNotFoundError(`Projekt ${projectId} ist nicht vorhanden.`);
|
||||||
|
const assessmentIds = [...new Set(url.searchParams.getAll('assessmentIds'))].sort();
|
||||||
|
const scanRunIds = [...new Set(url.searchParams.getAll('scanRunIds'))].sort();
|
||||||
|
if (!assessmentIds.length) throw new HttpError(400, 'missing_assessment_scope', 'At least one assessment ID is required.');
|
||||||
|
const revision = await remediationPort.currentRevision();
|
||||||
|
const contextKey = JSON.stringify([revision, projectId, assessmentIds, scanRunIds]);
|
||||||
|
const context = assessmentContextCache.get(contextKey);
|
||||||
|
const key = JSON.stringify([contextKey, REMEDIATION_ENGINE_VERSION, REMEDIATION_CATALOG_VERSION,
|
||||||
|
ASSESSMENT_CONTEXT_ENGINE.version, ASSESSMENT_CONTEXT_CATALOG.version]);
|
||||||
|
const stored = await remediationStore.read(key);
|
||||||
|
if (stored) { status = 200; sendJson(response, status, { status: 'ready', result: stored }, requestId); return; }
|
||||||
|
if (!context) { status = 200; sendJson(response, status, { status: 'context-required' }, requestId); return; }
|
||||||
|
// Core-owned adapter: the engine receives only Work records through its declared read port.
|
||||||
|
const workspace = await loadCanonicalProjectWorkspace({
|
||||||
|
listProjectAssessments: id => remediationPort.listAssessments(id),
|
||||||
|
loadAssessmentModel: async id => {
|
||||||
|
const model = await remediationPort.loadAssessmentModel(id);
|
||||||
|
if (!model || model.project.id !== projectId) throw new RepositoryNotFoundError(`Assessment ${id} is not in this project.`);
|
||||||
|
return model;
|
||||||
|
},
|
||||||
|
queryUnifiedResults: query => remediationPort.queryUnifiedResults(query),
|
||||||
|
listPolicyEvaluations: async id => ({ records: await remediationPort.listPolicyEvaluations(id) }),
|
||||||
|
...(remediationPort.listCurrentPolicyEvaluationsForMetrics ? { listCurrentPolicyEvaluationsForMetrics: async (id: string) => ({ records: await remediationPort.listCurrentPolicyEvaluationsForMetrics!(id) }) } : {})
|
||||||
|
}, projectId, scanRunIds.length ? scanRunIds : undefined, assessmentIds, { includeHistoricalScores: false });
|
||||||
|
const findings: RemediationFinding[] = [];
|
||||||
|
const seen = new Set<string>();
|
||||||
|
for (const sheet of workspace.dataset?.sheets ?? []) {
|
||||||
|
const profile = profileForSheet(workspace.dataset!, sheet);
|
||||||
|
for (const record of sheet.analysis.records) {
|
||||||
|
if (!profile.matchesStatus(record, 'deviation')) continue;
|
||||||
|
const field = (label: string) => record.fields.find(f => f.label.trim().toLowerCase() === label)?.value.trim() ?? '';
|
||||||
|
const identityKey = field('technical identity');
|
||||||
|
const scope = field('scope');
|
||||||
|
const key = record.findingOccurrenceId ? scannerFindingKey(record.findingOccurrenceId) : identityKey && scope ? policyFindingKey(scope, identityKey) : '';
|
||||||
|
if (!key || seen.has(key)) continue;
|
||||||
|
seen.add(key);
|
||||||
|
const canonical = workspace.models.flatMap(m => m.findingOccurrences).find(o => o.id === record.findingOccurrenceId);
|
||||||
|
findings.push({ key, occurrenceId: record.findingOccurrenceId, identityKey, scope,
|
||||||
|
tool: canonical?.sourceIdentity.tool.id ?? sheet.toolId ?? '',
|
||||||
|
ruleId: canonical?.sourceIdentity.originalRuleId ?? field('source rule id'), title: record.title,
|
||||||
|
outcome: canonical?.outcome?.normalizedValue ?? 'deviation',
|
||||||
|
// Native severity as normalized by the Core; GPO settings carry none and stay without.
|
||||||
|
...(canonical?.severity?.normalizedValue ? { severity: canonical.severity.normalizedValue } : {}),
|
||||||
|
remediation: canonical ? workspace.models.flatMap(m => m.findingDefinitions).find(d => d.id === canonical.definitionId)?.recommendation
|
||||||
|
: record.policyBaselineComparisons?.map(c => c.remediation).filter(Boolean).join('\n') });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (await remediationPort.currentRevision() !== revision) throw new HttpError(409, 'remediation_state_changed', 'The data changed while preparing work packages. Reload the scope.');
|
||||||
|
const result = buildWorkPackages({ projectId, assessmentIds, scanRunIds, findings, context });
|
||||||
|
await remediationStore.save(key, result);
|
||||||
|
status = 200; sendJson(response, status, { status: 'ready', result }, requestId); return;
|
||||||
|
}
|
||||||
|
if (segments[0] === 'projects' && segments[1] && segments[2] === 'assessment-context' && segments.length === 3 && method === 'GET') {
|
||||||
|
// Context engine (Phase 13E): read-only second reading through the same read-only port as reports.
|
||||||
|
const assessmentIds = [...new Set(url.searchParams.getAll('assessmentIds'))].sort();
|
||||||
|
const scanRunIds = [...new Set(url.searchParams.getAll('scanRunIds'))].sort();
|
||||||
|
if (!assessmentIds.length) throw new HttpError(400, 'missing_assessment_scope', 'At least one assessment ID is required.');
|
||||||
|
const port = await requireReportPort(segments[1]);
|
||||||
|
const cacheKey = JSON.stringify([await port.currentRevision(), segments[1], assessmentIds, scanRunIds]);
|
||||||
|
let context = assessmentContextCache.get(cacheKey);
|
||||||
|
if (!context) {
|
||||||
|
context = await createAssessmentContext(port, { projectId: segments[1], assessmentIds, scanRunIds, generatedAt: now().toISOString() });
|
||||||
|
assessmentContextCache.set(JSON.stringify([context.repositoryRevision, segments[1], assessmentIds, scanRunIds]), context);
|
||||||
|
while (assessmentContextCache.size > 8) assessmentContextCache.delete(assessmentContextCache.keys().next().value!);
|
||||||
|
}
|
||||||
|
status = 200; sendJson(response, status, context, requestId); return;
|
||||||
|
}
|
||||||
|
if (segments[0] === 'projects' && segments[1] && segments[2] === 'report-runs' && segments.length === 3 && method === 'POST') {
|
||||||
|
const body = await readJson<ReportRunRequest>(request, maximumBodyBytes);
|
||||||
|
if (body.kind !== 'management' && body.kind !== 'technical') {
|
||||||
|
throw new HttpError(400, 'invalid_report_kind', 'kind muss management oder technical sein.');
|
||||||
|
}
|
||||||
|
const ids = (value: unknown) => Array.isArray(value) && value.every(item => typeof item === 'string') && value.length
|
||||||
|
? value as string[] : undefined;
|
||||||
|
if (!options.reportArchive) throw new HttpError(503, 'report_archive_unavailable', 'Das Report-Archiv ist nicht konfiguriert.');
|
||||||
|
const startedAt = now();
|
||||||
|
const port = await requireReportPort(segments[1]);
|
||||||
|
const run = await createReportRun(port, {
|
||||||
|
projectId: segments[1], generatedAt: startedAt.toISOString(),
|
||||||
|
assessmentIds: ids(body.assessmentIds), scanRunIds: ids(body.scanRunIds)
|
||||||
|
}, {
|
||||||
|
// Both reports read the same published Context (user decision 2026-10-08): the Management Report
|
||||||
|
// requires it, the Technical Report renders its problem fields when it is available.
|
||||||
|
requireContext: body.kind === 'management',
|
||||||
|
readContext: async scope => {
|
||||||
|
const assessmentIds = [...new Set(scope.assessmentIds)].sort();
|
||||||
|
const scanRunIds = [...new Set(scope.scanRunIds ?? [])].sort();
|
||||||
|
const key = JSON.stringify([await port.currentRevision(), scope.projectId, assessmentIds, scanRunIds]);
|
||||||
|
let context = assessmentContextCache.get(key);
|
||||||
|
if (!context) {
|
||||||
|
context = await createAssessmentContext(port, { ...scope, assessmentIds, scanRunIds });
|
||||||
|
assessmentContextCache.set(JSON.stringify([context.repositoryRevision, scope.projectId, assessmentIds, scanRunIds]), context);
|
||||||
|
while (assessmentContextCache.size > 8) assessmentContextCache.delete(assessmentContextCache.keys().next().value!);
|
||||||
|
}
|
||||||
|
return publishContextReport(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const manifest = await options.reportArchive.write(run, startedAt);
|
||||||
|
const result: ReportRunResponse = {
|
||||||
|
runId: manifest.runId, generatedAt: run.projection.generatedAt, repositoryRevision: run.revision, kind: body.kind,
|
||||||
|
fileName: `assessor-${body.kind}-report-${run.projection.generatedAt.slice(0, 10)}.html`,
|
||||||
|
html: body.kind === 'management' ? run.files.management : run.files.technical
|
||||||
|
};
|
||||||
|
status = 200; sendJson(response, status, result, requestId); return;
|
||||||
|
}
|
||||||
|
if (segments[0] === 'projects' && segments[2] === 'timeslot-result-metrics' && segments.length === 3 && method === 'GET') {
|
||||||
|
if (!await repository.getProject(segments[1])) throw new RepositoryNotFoundError(`Projekt ${segments[1]} ist nicht vorhanden.`);
|
||||||
|
const assessmentIds = url.searchParams.getAll('assessmentIds');
|
||||||
|
const scanRunIds = url.searchParams.getAll('scanRunIds');
|
||||||
|
if (!assessmentIds.length) throw new HttpError(400, 'missing_assessment_scope', 'At least one assessment ID is required.');
|
||||||
|
const cacheKey = JSON.stringify([repository.revision, segments[1], [...assessmentIds].sort(), [...scanRunIds].sort()]);
|
||||||
|
let metrics = timeslotMetricsCache.get(cacheKey);
|
||||||
|
if (!metrics) {
|
||||||
|
metrics = (async () => {
|
||||||
|
const { dataset } = await loadCanonicalProjectWorkspace({
|
||||||
|
listProjectAssessments: projectId => repository.listAssessments(projectId),
|
||||||
|
loadAssessmentModel: async assessmentId => {
|
||||||
|
const model = await repository.loadAssessmentModel(assessmentId);
|
||||||
|
if (!model) throw new RepositoryNotFoundError(`Assessment ${assessmentId} ist nicht vorhanden.`);
|
||||||
|
return model;
|
||||||
|
},
|
||||||
|
queryUnifiedResults: query => repository.queryUnifiedResults(query),
|
||||||
|
listPolicyEvaluations: async projectId => ({ records: await repository.listPolicyEvaluations(projectId) }),
|
||||||
|
listCurrentPolicyEvaluationsForMetrics: repository.listCurrentPolicyEvaluationsForMetrics
|
||||||
|
? async projectId => ({ records: await repository.listCurrentPolicyEvaluationsForMetrics!(projectId) }) : undefined,
|
||||||
|
getPolicyAnalysisOverview: options.gpoPolicyAnalysisOverviewService
|
||||||
|
? projectId => options.gpoPolicyAnalysisOverviewService!.get(projectId) : undefined
|
||||||
|
}, segments[1], scanRunIds.length ? scanRunIds : undefined, assessmentIds,
|
||||||
|
{ includeHistoricalScores: false, includeBaselineGuidance: false, summaryOnly: true });
|
||||||
|
const sheet = dataset?.sheets.find(candidate => candidate.isProjectSheet) ?? dataset?.sheets[0];
|
||||||
|
return sheet ? summarizeWorkspaceResults(sheet.analysis.records, profileForSheet(dataset, sheet)) : null;
|
||||||
|
})();
|
||||||
|
if (timeslotMetricsCache.size >= 24) timeslotMetricsCache.clear();
|
||||||
|
timeslotMetricsCache.set(cacheKey, metrics);
|
||||||
|
void metrics.catch(() => timeslotMetricsCache.delete(cacheKey));
|
||||||
|
}
|
||||||
|
status = 200; sendJson(response, status, await metrics, requestId); return;
|
||||||
|
}
|
||||||
if (segments.length === 1 && segments[0] === 'assessments' && method === 'POST') {
|
if (segments.length === 1 && segments[0] === 'assessments' && method === 'POST') {
|
||||||
const assessment = await readJson<Assessment>(request, maximumBodyBytes);
|
const assessment = await readJson<Assessment>(request, maximumBodyBytes);
|
||||||
await repository.saveAssessment(assessment);
|
await repository.saveAssessment(assessment);
|
||||||
|
|
@ -870,6 +1160,9 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
if (segments.length === 1 && segments[0] === 'results' && method === 'GET') {
|
if (segments.length === 1 && segments[0] === 'results' && method === 'GET') {
|
||||||
status = 200; sendJson(response, status, await repository.queryUnifiedResults(unifiedQueryFrom(url)), requestId); return;
|
status = 200; sendJson(response, status, await repository.queryUnifiedResults(unifiedQueryFrom(url)), requestId); return;
|
||||||
}
|
}
|
||||||
|
if (segments.length === 2 && segments[0] === 'results' && segments[1] === 'status' && method === 'GET') {
|
||||||
|
status = 200; sendJson(response, status, await repository.getResultDbStatus(), requestId); return;
|
||||||
|
}
|
||||||
if (segments.length === 2 && segments[0] === 'results' && segments[1] === 'facets' && method === 'GET') {
|
if (segments.length === 2 && segments[0] === 'results' && segments[1] === 'facets' && method === 'GET') {
|
||||||
status = 200; sendJson(response, status, await repository.queryUnifiedResultFacets(unifiedQueryFrom(url)), requestId); return;
|
status = 200; sendJson(response, status, await repository.queryUnifiedResultFacets(unifiedQueryFrom(url)), requestId); return;
|
||||||
}
|
}
|
||||||
|
|
@ -889,6 +1182,12 @@ export function createAssessorHttpServer(options: AssessorHttpServerOptions): Se
|
||||||
await repository.saveReview(body.review, body.expectedRevision);
|
await repository.saveReview(body.review, body.expectedRevision);
|
||||||
status = 200; sendJson(response, status, { review: body.review, repositoryRevision: repository.revision }, requestId); return;
|
status = 200; sendJson(response, status, { review: body.review, repositoryRevision: repository.revision }, requestId); return;
|
||||||
}
|
}
|
||||||
|
if (segments.length === 3 && segments[0] === 'projects' && segments[2] === 'risk-acceptances' && method === 'POST') {
|
||||||
|
const body = await readJson<{ occurrenceId: string; command: RiskAcceptanceCommand; expectedRevision: number }>(request, maximumBodyBytes);
|
||||||
|
if (typeof body.occurrenceId !== 'string' || !body.occurrenceId || !Number.isSafeInteger(body.expectedRevision) || body.expectedRevision < 0) throw new HttpError(400, 'invalid_risk_acceptance', 'Finding and expected review revision are required.');
|
||||||
|
const review = await repository.recordRiskAcceptance(segments[1], body.occurrenceId, body.command, body.expectedRevision);
|
||||||
|
status = 200; sendJson(response, status, { review, repositoryRevision: repository.revision }, requestId); return;
|
||||||
|
}
|
||||||
if (segments.length === 1 && segments[0] === 'profiles' && method === 'GET') {
|
if (segments.length === 1 && segments[0] === 'profiles' && method === 'GET') {
|
||||||
status = 200; sendJson(response, status, await repository.listProfileRegistrations(), requestId); return;
|
status = 200; sendJson(response, status, await repository.listProfileRegistrations(), requestId); return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue