1375 Zero-Trust Policy Simulator
Implement Zero-Trust Security
1375.1 Zero-Trust Policy Simulator
Design zero-trust access policies and test them against various scenarios. This interactive tool demonstrates how conditional access policies evaluate multiple factors before granting or denying access to resources.
NoteTool Overview
The Zero Trust security model operates on the principle of “never trust, always verify.” This simulator allows you to:
- Build policy rules with multiple conditions (user role, device health, location, etc.)
- Test access scenarios against your policies
- Visualize the decision process through an interactive decision tree
- Detect policy conflicts and gaps in coverage
- Review an audit log of access decisions
TipHow to Use This Tool
- Create policies: Add rules with conditions and access decisions
- Select a test scenario: Choose from pre-built scenarios or customize
- Run evaluation: See how your policies handle the access request
- Review the decision tree: Understand the evaluation flow
- Check the audit log: Track all access attempts and decisions
Show code
// ============================================
// Zero-Trust Policy Simulator
// Interactive Tool for IoT Security Education
// ============================================
{
// IEEE Color palette
const colors = {
navy: "#2C3E50",
teal: "#16A085",
orange: "#E67E22",
gray: "#7F8C8D",
lightGray: "#ECF0F1",
white: "#FFFFFF",
red: "#E74C3C",
green: "#27AE60",
purple: "#9B59B6",
yellow: "#F1C40F",
darkRed: "#C0392B",
darkTeal: "#1ABC9C",
lightBlue: "#3498DB",
darkBlue: "#1A5276"
};
// Access decision types
const decisions = {
allow: { label: "ALLOW", color: colors.green, icon: "check" },
deny: { label: "DENY", color: colors.red, icon: "X" },
mfa: { label: "MFA REQUIRED", color: colors.orange, icon: "key" },
limited: { label: "LIMITED ACCESS", color: colors.yellow, icon: "!" }
};
// Condition types for policy rules
const conditionTypes = {
userRole: {
label: "User Role",
values: ["admin", "employee", "contractor", "guest", "device"]
},
deviceType: {
label: "Device Type",
values: ["managed-laptop", "managed-mobile", "byod", "iot-device", "unknown"]
},
deviceHealth: {
label: "Device Health",
values: ["compliant", "non-compliant", "unknown", "compromised"]
},
location: {
label: "Location",
values: ["office", "home", "public", "foreign", "unknown"]
},
timeOfDay: {
label: "Time of Day",
values: ["business-hours", "after-hours", "weekend", "any"]
},
resourceSensitivity: {
label: "Resource Sensitivity",
values: ["public", "internal", "confidential", "restricted"]
}
};
// Pre-built test scenarios
const testScenarios = [
{
id: "legitimate",
name: "Legitimate Employee",
description: "Normal access during business hours",
context: {
userRole: "employee",
deviceType: "managed-laptop",
deviceHealth: "compliant",
location: "office",
timeOfDay: "business-hours",
resourceSensitivity: "internal"
}
},
{
id: "compromised",
name: "Compromised Device",
description: "Employee with infected laptop",
context: {
userRole: "employee",
deviceType: "managed-laptop",
deviceHealth: "compromised",
location: "office",
timeOfDay: "business-hours",
resourceSensitivity: "confidential"
}
},
{
id: "unusual-location",
name: "Unusual Location",
description: "Admin accessing from public network",
context: {
userRole: "admin",
deviceType: "managed-mobile",
deviceHealth: "compliant",
location: "public",
timeOfDay: "business-hours",
resourceSensitivity: "restricted"
}
},
{
id: "after-hours",
name: "After Hours Access",
description: "Employee working late from home",
context: {
userRole: "employee",
deviceType: "byod",
deviceHealth: "non-compliant",
location: "home",
timeOfDay: "after-hours",
resourceSensitivity: "confidential"
}
},
{
id: "guest",
name: "Guest User",
description: "Visitor trying to access internal resources",
context: {
userRole: "guest",
deviceType: "unknown",
deviceHealth: "unknown",
location: "office",
timeOfDay: "business-hours",
resourceSensitivity: "internal"
}
},
{
id: "iot-device",
name: "IoT Device Access",
description: "Sensor trying to reach cloud service",
context: {
userRole: "device",
deviceType: "iot-device",
deviceHealth: "compliant",
location: "office",
timeOfDay: "any",
resourceSensitivity: "internal"
}
}
];
// State management
let policies = [];
let currentScenario = testScenarios[0];
let customContext = { ...testScenarios[0].context };
let auditLog = [];
let lastEvaluation = null;
// Create main container
const container = d3.create("div")
.style("font-family", "system-ui, -apple-system, sans-serif")
.style("max-width", "1000px")
.style("margin", "0 auto");
// Title
container.append("div")
.style("text-align", "center")
.style("margin-bottom", "15px")
.append("h3")
.style("color", colors.navy)
.style("margin", "0")
.text("Zero-Trust Policy Simulator");
// Main layout: 2 columns
const mainLayout = container.append("div")
.style("display", "grid")
.style("grid-template-columns", "350px 1fr")
.style("gap", "15px")
.style("margin-bottom", "15px");
// === LEFT PANEL: Policy Builder ===
const leftPanel = mainLayout.append("div")
.style("display", "flex")
.style("flex-direction", "column")
.style("gap", "15px");
// Policy builder section
const policyBuilder = leftPanel.append("div")
.style("background", colors.lightGray)
.style("padding", "15px")
.style("border-radius", "8px");
policyBuilder.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "12px")
.style("font-size", "14px")
.text("Policy Rule Builder");
// Condition selectors
const conditionsForm = policyBuilder.append("div")
.style("display", "grid")
.style("grid-template-columns", "1fr 1fr")
.style("gap", "8px");
const conditionSelects = {};
Object.entries(conditionTypes).forEach(([key, config]) => {
const group = conditionsForm.append("div")
.style("display", "flex")
.style("flex-direction", "column")
.style("gap", "3px");
group.append("label")
.style("font-size", "10px")
.style("color", colors.gray)
.text(config.label);
const select = group.append("select")
.style("padding", "5px")
.style("border-radius", "4px")
.style("border", `1px solid ${colors.gray}`)
.style("font-size", "11px");
select.append("option").attr("value", "*").text("Any");
config.values.forEach(v => {
select.append("option").attr("value", v).text(v.replace(/-/g, " "));
});
conditionSelects[key] = select;
});
// Decision selector
policyBuilder.append("div")
.style("margin-top", "10px")
.style("font-size", "11px")
.style("color", colors.gray)
.text("Access Decision:");
const decisionSelect = policyBuilder.append("select")
.style("width", "100%")
.style("padding", "6px")
.style("margin-top", "4px")
.style("border-radius", "4px")
.style("border", `1px solid ${colors.gray}`)
.style("font-size", "12px");
Object.entries(decisions).forEach(([key, config]) => {
decisionSelect.append("option")
.attr("value", key)
.text(config.label);
});
// Priority selector
const priorityGroup = policyBuilder.append("div")
.style("margin-top", "10px")
.style("display", "flex")
.style("gap", "10px")
.style("align-items", "center");
priorityGroup.append("label")
.style("font-size", "11px")
.style("color", colors.gray)
.text("Priority:");
const priorityInput = priorityGroup.append("input")
.attr("type", "number")
.attr("min", "1")
.attr("max", "100")
.attr("value", "50")
.style("width", "60px")
.style("padding", "4px")
.style("border-radius", "4px")
.style("border", `1px solid ${colors.gray}`)
.style("font-size", "11px");
priorityGroup.append("span")
.style("font-size", "9px")
.style("color", colors.gray)
.text("(1=highest)");
// Add rule button
const addPolicyBtn = policyBuilder.append("button")
.text("Add Policy Rule")
.style("margin-top", "12px")
.style("padding", "10px")
.style("width", "100%")
.style("background", colors.teal)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "4px")
.style("cursor", "pointer")
.style("font-weight", "bold")
.style("font-size", "12px");
// Sample policies button
const sampleBtn = policyBuilder.append("button")
.text("Load Sample Policies")
.style("margin-top", "8px")
.style("padding", "8px")
.style("width", "100%")
.style("background", colors.gray)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "4px")
.style("cursor", "pointer")
.style("font-size", "11px");
// Active policies list
const policiesList = leftPanel.append("div")
.style("background", colors.white)
.style("border", `1px solid ${colors.gray}`)
.style("padding", "10px")
.style("border-radius", "8px")
.style("max-height", "200px")
.style("overflow-y", "auto");
policiesList.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "8px")
.style("font-size", "12px")
.text("Active Policies");
const policiesContainer = policiesList.append("div")
.attr("class", "policies-container");
// === RIGHT PANEL: Testing & Visualization ===
const rightPanel = mainLayout.append("div")
.style("display", "flex")
.style("flex-direction", "column")
.style("gap", "15px");
// Test scenario section
const testSection = rightPanel.append("div")
.style("background", colors.navy)
.style("padding", "15px")
.style("border-radius", "8px")
.style("color", colors.white);
testSection.append("div")
.style("font-weight", "bold")
.style("margin-bottom", "10px")
.style("font-size", "14px")
.text("Test Scenario");
// Scenario dropdown
const scenarioRow = testSection.append("div")
.style("display", "flex")
.style("gap", "10px")
.style("align-items", "center")
.style("margin-bottom", "10px");
scenarioRow.append("label")
.style("font-size", "11px")
.text("Select Scenario:");
const scenarioSelect = scenarioRow.append("select")
.style("flex", "1")
.style("padding", "6px")
.style("border-radius", "4px")
.style("border", "none")
.style("font-size", "11px");
testScenarios.forEach(s => {
scenarioSelect.append("option")
.attr("value", s.id)
.text(s.name);
});
scenarioSelect.append("option")
.attr("value", "custom")
.text("Custom Scenario");
// Scenario description
const scenarioDesc = testSection.append("div")
.attr("class", "scenario-desc")
.style("font-size", "11px")
.style("margin-bottom", "10px")
.style("padding", "8px")
.style("background", "rgba(255,255,255,0.1)")
.style("border-radius", "4px")
.text(testScenarios[0].description);
// Custom scenario inputs (hidden by default)
const customInputs = testSection.append("div")
.attr("class", "custom-inputs")
.style("display", "none")
.style("display", "grid")
.style("grid-template-columns", "1fr 1fr 1fr")
.style("gap", "6px")
.style("margin-bottom", "10px");
const customSelects = {};
Object.entries(conditionTypes).forEach(([key, config]) => {
const group = customInputs.append("div")
.style("display", "flex")
.style("flex-direction", "column")
.style("gap", "2px");
group.append("label")
.style("font-size", "9px")
.style("color", colors.lightGray)
.text(config.label);
const select = group.append("select")
.style("padding", "4px")
.style("border-radius", "3px")
.style("border", "none")
.style("font-size", "10px");
config.values.forEach(v => {
select.append("option").attr("value", v).text(v.replace(/-/g, " "));
});
customSelects[key] = select;
});
// Evaluate button
const evaluateBtn = testSection.append("button")
.text("Evaluate Access Request")
.style("padding", "12px 20px")
.style("background", colors.orange)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "4px")
.style("cursor", "pointer")
.style("font-weight", "bold")
.style("font-size", "13px")
.style("width", "100%");
// Decision result display
const resultBox = rightPanel.append("div")
.attr("class", "result-box")
.style("background", colors.lightGray)
.style("padding", "15px")
.style("border-radius", "8px")
.style("text-align", "center");
resultBox.append("div")
.style("font-size", "12px")
.style("color", colors.gray)
.style("margin-bottom", "8px")
.text("Access Decision");
const resultDecision = resultBox.append("div")
.attr("class", "result-decision")
.style("font-size", "28px")
.style("font-weight", "bold")
.style("color", colors.gray)
.text("--");
const resultReason = resultBox.append("div")
.attr("class", "result-reason")
.style("font-size", "11px")
.style("color", colors.gray)
.style("margin-top", "8px")
.text("Run an evaluation to see the decision");
// Decision tree visualization
const treeSection = rightPanel.append("div")
.style("background", colors.white)
.style("border", `1px solid ${colors.gray}`)
.style("padding", "10px")
.style("border-radius", "8px");
treeSection.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "10px")
.style("font-size", "12px")
.text("Decision Tree Visualization");
const treeSvg = treeSection.append("svg")
.attr("viewBox", "0 0 550 200")
.attr("width", "100%")
.style("min-height", "180px");
// === BOTTOM SECTION: Audit Log & Analysis ===
const bottomSection = container.append("div")
.style("display", "grid")
.style("grid-template-columns", "1fr 1fr")
.style("gap", "15px");
// Audit log
const auditSection = bottomSection.append("div")
.style("background", colors.lightGray)
.style("padding", "15px")
.style("border-radius", "8px");
const auditHeader = auditSection.append("div")
.style("display", "flex")
.style("justify-content", "space-between")
.style("align-items", "center")
.style("margin-bottom", "10px");
auditHeader.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("font-size", "12px")
.text("Audit Log");
const clearLogBtn = auditHeader.append("button")
.text("Clear Log")
.style("padding", "4px 8px")
.style("background", colors.gray)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "3px")
.style("cursor", "pointer")
.style("font-size", "10px");
const auditContainer = auditSection.append("div")
.attr("class", "audit-container")
.style("max-height", "150px")
.style("overflow-y", "auto")
.style("font-size", "10px");
// Policy analysis
const analysisSection = bottomSection.append("div")
.style("background", colors.white)
.style("border", `1px solid ${colors.gray}`)
.style("padding", "15px")
.style("border-radius", "8px");
analysisSection.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "10px")
.style("font-size", "12px")
.text("Policy Analysis");
const analysisContainer = analysisSection.append("div")
.attr("class", "analysis-container")
.style("font-size", "11px");
// === HELPER FUNCTIONS ===
function formatConditions(conditions) {
return Object.entries(conditions)
.filter(([k, v]) => v !== "*")
.map(([k, v]) => `${conditionTypes[k]?.label || k}: ${v}`)
.join(", ");
}
function renderPolicies() {
policiesContainer.selectAll("*").remove();
if (policies.length === 0) {
policiesContainer.append("div")
.style("color", colors.gray)
.style("font-style", "italic")
.style("font-size", "11px")
.text("No policies defined. Add rules above.");
return;
}
// Sort by priority
const sortedPolicies = [...policies].sort((a, b) => a.priority - b.priority);
sortedPolicies.forEach((policy, i) => {
const item = policiesContainer.append("div")
.style("display", "flex")
.style("justify-content", "space-between")
.style("align-items", "flex-start")
.style("padding", "6px")
.style("margin-bottom", "4px")
.style("background", decisions[policy.decision]?.color || colors.gray)
.style("opacity", "0.8")
.style("border-radius", "4px")
.style("font-size", "10px")
.style("color", colors.white);
const info = item.append("div")
.style("flex", "1");
info.append("div")
.style("font-weight", "bold")
.text(`[${policy.priority}] ${decisions[policy.decision]?.label || policy.decision}`);
info.append("div")
.style("font-size", "9px")
.style("opacity", "0.9")
.style("margin-top", "2px")
.text(formatConditions(policy.conditions) || "All requests");
item.append("button")
.text("X")
.style("background", "transparent")
.style("border", "none")
.style("color", colors.white)
.style("cursor", "pointer")
.style("font-size", "12px")
.style("padding", "0 5px")
.on("click", () => {
policies = policies.filter((p, idx) => idx !== policies.indexOf(policy));
renderPolicies();
analyzePolicy();
});
});
}
function addPolicy() {
const conditions = {};
Object.entries(conditionSelects).forEach(([key, select]) => {
conditions[key] = select.node().value;
});
const policy = {
conditions,
decision: decisionSelect.node().value,
priority: parseInt(priorityInput.node().value) || 50
};
policies.push(policy);
renderPolicies();
analyzePolicy();
// Reset form
Object.values(conditionSelects).forEach(s => s.node().value = "*");
priorityInput.node().value = "50";
}
function loadSamplePolicies() {
policies = [
// High priority security rules
{
conditions: { deviceHealth: "compromised" },
decision: "deny",
priority: 1
},
{
conditions: { userRole: "guest", resourceSensitivity: "restricted" },
decision: "deny",
priority: 5
},
{
conditions: { userRole: "guest", resourceSensitivity: "confidential" },
decision: "deny",
priority: 5
},
// Admin access with conditions
{
conditions: { userRole: "admin", location: "office", deviceHealth: "compliant" },
decision: "allow",
priority: 10
},
{
conditions: { userRole: "admin", location: "public" },
decision: "mfa",
priority: 15
},
// Employee rules
{
conditions: { userRole: "employee", deviceType: "managed-laptop", deviceHealth: "compliant", location: "office" },
decision: "allow",
priority: 20
},
{
conditions: { userRole: "employee", location: "home", deviceHealth: "compliant" },
decision: "mfa",
priority: 25
},
{
conditions: { userRole: "employee", timeOfDay: "after-hours", resourceSensitivity: "confidential" },
decision: "mfa",
priority: 30
},
// BYOD restrictions
{
conditions: { deviceType: "byod", resourceSensitivity: "restricted" },
decision: "deny",
priority: 35
},
{
conditions: { deviceType: "byod", resourceSensitivity: "confidential" },
decision: "limited",
priority: 35
},
// IoT device rules
{
conditions: { userRole: "device", deviceType: "iot-device", deviceHealth: "compliant" },
decision: "limited",
priority: 40
},
// Guest access
{
conditions: { userRole: "guest", resourceSensitivity: "public" },
decision: "allow",
priority: 50
},
// Default deny
{
conditions: {},
decision: "deny",
priority: 100
}
];
renderPolicies();
analyzePolicy();
}
function matchesConditions(policy, context) {
return Object.entries(policy.conditions).every(([key, value]) => {
if (value === "*" || !value) return true;
return context[key] === value;
});
}
function evaluateRequest() {
const context = scenarioSelect.node().value === "custom"
? Object.fromEntries(Object.entries(customSelects).map(([k, s]) => [k, s.node().value]))
: currentScenario.context;
// Sort policies by priority
const sortedPolicies = [...policies].sort((a, b) => a.priority - b.priority);
let matchedPolicy = null;
let evaluationPath = [];
for (const policy of sortedPolicies) {
const matches = matchesConditions(policy, context);
evaluationPath.push({
policy,
matches,
reason: matches ? "Conditions matched" : "Conditions not met"
});
if (matches) {
matchedPolicy = policy;
break;
}
}
// Default to deny if no policy matches
const decision = matchedPolicy ? matchedPolicy.decision : "deny";
const decisionInfo = decisions[decision] || decisions.deny;
lastEvaluation = {
context,
decision,
matchedPolicy,
evaluationPath,
timestamp: new Date()
};
// Update result display
resultDecision
.text(decisionInfo.label)
.style("color", decisionInfo.color);
resultReason.text(
matchedPolicy
? `Matched rule at priority ${matchedPolicy.priority}: ${formatConditions(matchedPolicy.conditions) || "Default rule"}`
: "No matching policy found - default DENY applied"
);
// Add to audit log
addAuditEntry(lastEvaluation);
// Render decision tree
renderDecisionTree(lastEvaluation);
}
function renderDecisionTree(evaluation) {
treeSvg.selectAll("*").remove();
if (!evaluation || !evaluation.evaluationPath.length) {
treeSvg.append("text")
.attr("x", 275)
.attr("y", 100)
.attr("text-anchor", "middle")
.attr("fill", colors.gray)
.attr("font-size", "12px")
.text("Run an evaluation to see the decision tree");
return;
}
const nodeWidth = 120;
const nodeHeight = 40;
const startX = 20;
const startY = 30;
const hSpacing = 130;
const vSpacing = 60;
// Request node
const requestNode = treeSvg.append("g")
.attr("transform", `translate(${startX}, ${startY})`);
requestNode.append("rect")
.attr("width", nodeWidth)
.attr("height", nodeHeight)
.attr("rx", 6)
.attr("fill", colors.navy);
requestNode.append("text")
.attr("x", nodeWidth / 2)
.attr("y", nodeHeight / 2 + 4)
.attr("text-anchor", "middle")
.attr("fill", colors.white)
.attr("font-size", "10px")
.attr("font-weight", "bold")
.text("Access Request");
// Draw evaluation path (limited to first 3 policies for space)
const displayPath = evaluation.evaluationPath.slice(0, 3);
let lastX = startX + nodeWidth;
let lastY = startY + nodeHeight / 2;
displayPath.forEach((step, i) => {
const x = startX + hSpacing * (i + 1);
const y = startY + (step.matches ? 0 : vSpacing);
// Connection line
treeSvg.append("line")
.attr("x1", lastX)
.attr("y1", lastY)
.attr("x2", x)
.attr("y2", y + nodeHeight / 2)
.attr("stroke", step.matches ? colors.green : colors.red)
.attr("stroke-width", 2)
.attr("stroke-dasharray", step.matches ? "none" : "4,2");
// Policy node
const policyNode = treeSvg.append("g")
.attr("transform", `translate(${x}, ${y})`);
const decisionColor = decisions[step.policy.decision]?.color || colors.gray;
policyNode.append("rect")
.attr("width", nodeWidth)
.attr("height", nodeHeight)
.attr("rx", 6)
.attr("fill", step.matches ? decisionColor : colors.lightGray)
.attr("stroke", step.matches ? decisionColor : colors.gray)
.attr("stroke-width", step.matches ? 0 : 1);
policyNode.append("text")
.attr("x", nodeWidth / 2)
.attr("y", 15)
.attr("text-anchor", "middle")
.attr("fill", step.matches ? colors.white : colors.gray)
.attr("font-size", "9px")
.text(`Priority ${step.policy.priority}`);
policyNode.append("text")
.attr("x", nodeWidth / 2)
.attr("y", 30)
.attr("text-anchor", "middle")
.attr("fill", step.matches ? colors.white : colors.gray)
.attr("font-size", "10px")
.attr("font-weight", "bold")
.text(step.matches ? decisions[step.policy.decision]?.label : "No Match");
if (step.matches) {
lastX = x + nodeWidth;
lastY = y + nodeHeight / 2;
}
});
// Final decision node
const finalX = startX + hSpacing * (displayPath.length + 1);
const finalY = startY;
treeSvg.append("line")
.attr("x1", lastX)
.attr("y1", lastY)
.attr("x2", finalX)
.attr("y2", finalY + nodeHeight / 2)
.attr("stroke", decisions[evaluation.decision]?.color || colors.red)
.attr("stroke-width", 3);
const finalNode = treeSvg.append("g")
.attr("transform", `translate(${finalX}, ${finalY})`);
finalNode.append("rect")
.attr("width", nodeWidth)
.attr("height", nodeHeight)
.attr("rx", 6)
.attr("fill", decisions[evaluation.decision]?.color || colors.red)
.attr("stroke", colors.navy)
.attr("stroke-width", 2);
finalNode.append("text")
.attr("x", nodeWidth / 2)
.attr("y", nodeHeight / 2 + 4)
.attr("text-anchor", "middle")
.attr("fill", colors.white)
.attr("font-size", "11px")
.attr("font-weight", "bold")
.text(decisions[evaluation.decision]?.label || "DENY");
}
function addAuditEntry(evaluation) {
const entry = {
timestamp: evaluation.timestamp,
context: evaluation.context,
decision: evaluation.decision,
policyPriority: evaluation.matchedPolicy?.priority || "default"
};
auditLog.unshift(entry);
if (auditLog.length > 20) auditLog.pop();
renderAuditLog();
}
function renderAuditLog() {
auditContainer.selectAll("*").remove();
if (auditLog.length === 0) {
auditContainer.append("div")
.style("color", colors.gray)
.style("font-style", "italic")
.text("No access attempts logged yet");
return;
}
auditLog.forEach(entry => {
const item = auditContainer.append("div")
.style("display", "flex")
.style("justify-content", "space-between")
.style("padding", "4px 6px")
.style("margin-bottom", "3px")
.style("background", colors.white)
.style("border-radius", "3px")
.style("border-left", `3px solid ${decisions[entry.decision]?.color || colors.gray}`);
const info = item.append("div");
info.append("div")
.style("font-weight", "bold")
.text(`${entry.context.userRole} / ${entry.context.deviceType}`);
info.append("div")
.style("font-size", "9px")
.style("color", colors.gray)
.text(`${entry.context.location} @ ${entry.context.timeOfDay}`);
item.append("div")
.style("font-weight", "bold")
.style("color", decisions[entry.decision]?.color || colors.gray)
.text(decisions[entry.decision]?.label || entry.decision);
});
}
function analyzePolicy() {
analysisContainer.selectAll("*").remove();
if (policies.length === 0) {
analysisContainer.append("div")
.style("color", colors.gray)
.style("font-style", "italic")
.text("Add policies to see analysis");
return;
}
// Check for conflicts
let conflicts = [];
let gaps = [];
// Check for overlapping rules with different decisions
for (let i = 0; i < policies.length; i++) {
for (let j = i + 1; j < policies.length; j++) {
const p1 = policies[i];
const p2 = policies[j];
// Check if conditions overlap
let overlap = true;
Object.keys(conditionTypes).forEach(key => {
if (p1.conditions[key] && p2.conditions[key] &&
p1.conditions[key] !== "*" && p2.conditions[key] !== "*" &&
p1.conditions[key] !== p2.conditions[key]) {
overlap = false;
}
});
if (overlap && p1.decision !== p2.decision) {
conflicts.push(`Rules at priority ${p1.priority} and ${p2.priority} may conflict`);
}
}
}
// Check for common gaps
const hasDefaultDeny = policies.some(p =>
Object.values(p.conditions).every(v => !v || v === "*") && p.decision === "deny"
);
if (!hasDefaultDeny) {
gaps.push("No default deny rule - unmatched requests may be allowed");
}
const hasCompromisedRule = policies.some(p =>
p.conditions.deviceHealth === "compromised" && p.decision === "deny"
);
if (!hasCompromisedRule) {
gaps.push("No rule blocking compromised devices");
}
// Display analysis
if (conflicts.length > 0) {
analysisContainer.append("div")
.style("color", colors.orange)
.style("font-weight", "bold")
.style("margin-bottom", "5px")
.text("Potential Conflicts:");
conflicts.forEach(c => {
analysisContainer.append("div")
.style("padding-left", "10px")
.style("margin-bottom", "2px")
.text("- " + c);
});
}
if (gaps.length > 0) {
analysisContainer.append("div")
.style("color", colors.red)
.style("font-weight", "bold")
.style("margin-top", conflicts.length > 0 ? "8px" : "0")
.style("margin-bottom", "5px")
.text("Coverage Gaps:");
gaps.forEach(g => {
analysisContainer.append("div")
.style("padding-left", "10px")
.style("margin-bottom", "2px")
.text("- " + g);
});
}
if (conflicts.length === 0 && gaps.length === 0) {
analysisContainer.append("div")
.style("color", colors.green)
.style("font-weight", "bold")
.text("Policy looks well-structured!");
analysisContainer.append("div")
.style("margin-top", "5px")
.text(`${policies.length} rules covering ${Object.keys(conditionTypes).length} condition types`);
}
// Policy coverage summary
analysisContainer.append("div")
.style("margin-top", "10px")
.style("padding-top", "10px")
.style("border-top", `1px solid ${colors.lightGray}`)
.style("font-size", "10px")
.style("color", colors.gray)
.text(`Total policies: ${policies.length} | Priority range: ${Math.min(...policies.map(p => p.priority))} - ${Math.max(...policies.map(p => p.priority))}`);
}
// === EVENT HANDLERS ===
addPolicyBtn.on("click", addPolicy);
sampleBtn.on("click", loadSamplePolicies);
evaluateBtn.on("click", evaluateRequest);
clearLogBtn.on("click", () => {
auditLog = [];
renderAuditLog();
});
scenarioSelect.on("change", function() {
const value = this.value;
if (value === "custom") {
customInputs.style("display", "grid");
scenarioDesc.text("Configure custom scenario below:");
} else {
customInputs.style("display", "none");
currentScenario = testScenarios.find(s => s.id === value);
scenarioDesc.text(currentScenario?.description || "");
}
});
// === INITIAL RENDER ===
renderPolicies();
renderAuditLog();
analyzePolicy();
renderDecisionTree(null);
// Legend
const legend = container.append("div")
.style("display", "flex")
.style("justify-content", "center")
.style("gap", "20px")
.style("margin-top", "15px")
.style("flex-wrap", "wrap")
.style("font-size", "11px");
Object.entries(decisions).forEach(([key, config]) => {
const item = legend.append("div")
.style("display", "flex")
.style("align-items", "center")
.style("gap", "5px");
item.append("div")
.style("width", "14px")
.style("height", "14px")
.style("background", config.color)
.style("border-radius", "3px");
item.append("span")
.style("color", colors.navy)
.text(config.label);
});
return container.node();
}1375.2 Understanding Zero Trust Security
The Zero Trust model fundamentally changes how we approach security:
1375.2.1 Traditional vs Zero Trust
| Aspect | Traditional (Perimeter) | Zero Trust |
|---|---|---|
| Trust Model | Trust internal network | Never trust, always verify |
| Verification | At perimeter only | Every access request |
| Network Position | Determines trust level | Does not grant trust |
| Access Control | Network-based | Identity and context-based |
| Lateral Movement | Often unrestricted | Continuously monitored |
1375.2.2 Zero Trust Principles
TipCore Zero Trust Principles
- Verify explicitly: Always authenticate and authorize based on all available data
- Use least privilege access: Limit user access with just-in-time and just-enough-access
- Assume breach: Minimize blast radius and segment access, verify end-to-end encryption
1375.3 Policy Conditions Explained
1375.3.1 User Role
- Admin: Full system administrators
- Employee: Regular staff members
- Contractor: External workers with limited tenure
- Guest: Visitors with minimal access needs
- Device: Machine identities (IoT, servers)
1375.3.2 Device Type
- Managed Laptop: Corporate-issued, MDM-enrolled
- Managed Mobile: Corporate phone/tablet
- BYOD: Personal devices used for work
- IoT Device: Sensors, cameras, actuators
- Unknown: Unrecognized devices
1375.3.3 Device Health
- Compliant: Meets all security requirements
- Non-compliant: Missing updates or configurations
- Unknown: Health status cannot be determined
- Compromised: Known security incident detected
1375.3.4 Location
- Office: Corporate premises
- Home: Remote work from known home network
- Public: Coffee shops, airports, hotels
- Foreign: International locations
- Unknown: Unrecognized network
1375.4 Building Effective Policies
1375.4.1 Recommended Policy Structure
- Highest priority (1-10): Security emergency rules
- Block compromised devices
- Deny from known malicious locations
- High priority (11-30): Strict access rules
- Admin access with strong conditions
- Restricted resource protection
- Medium priority (31-60): Normal operations
- Employee access patterns
- Time-based restrictions
- Low priority (61-90): Special cases
- Guest access to public resources
- IoT device allowances
- Default (91-100): Catch-all rules
- Default deny for unmatched requests
1375.4.2 Example Policy Set for IoT Environments
Priority 1: deviceHealth=compromised → DENY
Priority 5: userRole=guest, resource=restricted → DENY
Priority 10: userRole=device, deviceType=iot, health=compliant → LIMITED
Priority 20: userRole=employee, device=managed, location=office → ALLOW
Priority 30: userRole=employee, location=home → MFA REQUIRED
Priority 100: * → DENY (default)
1375.5 What’s Next
- Zero Trust vs Perimeter Comparison - Animated comparison
- Network Segmentation Visualizer - Design network zones
- Security Overview - Comprehensive security concepts
- Authentication Methods - Identity verification techniques
TipTool Technical Notes
This simulator implements:
- Policy rule builder: Multi-condition rule creation with priority
- Access decision engine: Evaluates requests against sorted policy set
- Decision tree visualization: Shows evaluation path through policies
- Conflict detection: Identifies potentially overlapping rules
- Gap analysis: Highlights missing security coverage
- Audit logging: Records access decisions for review
- Pre-built scenarios: Common access patterns for testing
Educational simplifications:
- Real zero-trust systems use more condition types (risk scores, behavior analytics)
- Policy languages are more expressive (AND/OR/NOT logic)
- Decisions may include granular permissions (read/write/execute)
- Integration with SIEM, SOAR, and identity providers