Hint 1: Policies only ever add allows
There is no deny rule. A pod no policy selects accepts everything, and a broad policy anywhere in the namespace reopens what a narrow one meant to close. Start from podSelector: {} with no rules, then allow each flow.
Hint 1 unlocks after your first failed run.
Hint 2: Indentation is the security decision
In YAML, "- namespaceSelector: …" followed by " podSelector: …" is one peer (AND), while two dashes are two peers (OR). In these objects that is one object with both keys versus two objects in the from array.
Hint 2 unlocks after 2 failed runs.
Reveal the reference patch and explanation
const policy = (name, spec) => ({
apiVersion: 'networking.k8s.io/v1',
kind: 'NetworkPolicy',
metadata: {name, namespace: 'shop'},
spec,
});
const app = name => ({matchLabels: {app: name}});
const namespace = name => ({matchLabels: {'kubernetes.io/metadata.name': name}});
const tcp = port => [{protocol: 'TCP', port}];
export function shopNetworkPolicies() {
return [
// Every pod in shop refuses ingress unless another policy allows it.
policy('default-deny-ingress', {podSelector: {}, policyTypes: ['Ingress']}),
policy('web-from-ingress-controller', {
podSelector: app('web'),
ingress: [
{
from: [
{
namespaceSelector: namespace('ingress-nginx'),
podSelector: {matchLabels: {'app.kubernetes.io/name': 'ingress-nginx'}},
},
],
ports: tcp(8080),
},
],
}),
policy('api-from-web-and-prometheus', {
podSelector: app('api'),
ingress: [
{from: [{podSelector: app('web')}], ports: tcp(8080)},
{
from: [{namespaceSelector: namespace('monitoring'), podSelector: app('prometheus')}],
ports: tcp(9090),
},
],
}),
// The database: only api on 5432 in, and nothing out.
policy('db-from-api-no-egress', {
podSelector: app('postgres'),
policyTypes: ['Ingress', 'Egress'],
ingress: [{from: [{podSelector: app('api')}], ports: tcp(5432)}],
}),
];
}Allowed: ingress-nginx/controller-1 to web on 8080, shop/web-1 to api on 8080, monitoring/prometheus-0 to api on 9090 and shop/api-1 to the database on 5432. Refused: sandbox/web-x to api (a web label in another namespace), sandbox/prometheus-x to api on 9090 (a namespace labelled purpose=monitoring by its own team), monitoring/grafana-0 to api on 9090, Prometheus to the database, web, debug and the ingress controller to the database or straight to api, web to the metrics port, Prometheus to api on 8080, sandbox and debug pods to web, UDP to the database, and any connection from the database, to api or to 203.0.113.10:443.
Compare it with your own fix, then rerun the tests. The editor is not changed when you open this explanation.
The reference patch unlocks after you run code you have edited.
Inspect the actual test harness
The browser and the lab download run the same cases, including cases that catch common wrong fixes, such as comparing with one fixture value instead of the real rule. The download includes the complete test adapter and cryptographic fixtures.
Download cases.mjsHow the browser sandbox runs your code
Your code runs in QuickJS 2025-09-13 (quickjs-emscripten 0.32.0) inside a Web Worker, isolated from the page, with a 32 MiB memory limit and a 2.5-second time limit. The test cases and the pass or fail verdict run outside that sandbox, so changing built-ins such as Array.prototype.push cannot change the result. The lab download runs the same cases under Node.js, where your code runs directly.
The same as in current Node.js:
- ES2025 syntax and built-ins, including Set methods such as intersection, iterator helpers, Promise.try, RegExp.escape and the RegExp v flag
Added to the sandbox so that it behaves like Node.js:
- structuredClone, Array.fromAsync, queueMicrotask, atob and btoa
- URL and URLSearchParams, parsed by your browser’s own URL implementation
- TextEncoder and TextDecoder for UTF-8
- Intl.Collator and a locale-aware String.prototype.localeCompare
- console.log and related methods, whose output appears with your results
Not available in the browser sandbox:
- Timers (setTimeout, setInterval), fetch and other network or storage APIs; the exercises are deterministic and offline
- Node.js modules and globals: import, require, process and Buffer
- Web Crypto (crypto.getRandomValues, crypto.subtle); each workshop supplies the cryptographic operations it needs
- Intl other than Collator: toLocaleString and toLocaleDateString ignore the locale, so numbers have no digit grouping and dates use one fixed format in the device’s time zone
- using declarations, Symbol.dispose and DisposableStack (explicit resource management)
- Uint8Array.fromBase64 and toBase64, JSON.rawJSON, Atomics and Error.captureStackTrace
The grader evaluates the policy objects; a real cluster enforces them only when its network plugin supports NetworkPolicy, so prove each rule with live connections as the guide shows. The model has no Services, node or hostNetwork traffic, and gives pods no IP addresses, so ipBlock matches only the external host. Restricting egress for the other shop pods also needs DNS on UDP and TCP port 53 to the cluster DNS pods. NetworkPolicy works at layers 3 and 4: it does not authenticate the caller or authorize a request, and labels used in selectors need protecting through RBAC and admission control. The OWASP Kubernetes Top 10 (2025) lists this risk as K05, Missing Network Segmentation Controls.
Your next task: Pick a namespace you run. Which pods does no NetworkPolicy select today, and which policy, if someone added it tomorrow, would reopen your database to the whole cluster?
Kubernetes documentation: Network Policies
Related guide: Prove network and admission policies enforce the intended boundary