CSE Roadmap

Core tier · about 8 hours · 7 parts

Part 6 of 7 · 5 min read

Exercises

Easy

Easy 1: Run and read (20 minutes)

In code/mini-auth, run pytest and python demo.py. For the first call, write down every request in order (who, what, status) and the field in the 401 that told the client where to go next.

Done when: you list the first MCP call (401), the metadata fetch, the authorization server's metadata, the client's own metadata document, the authorization, the token request and the retried call (200), and you name resource_metadata as the pointer and scope as the first scope guidance.

Hint

the requests_by_host line in the demo counts them; the statuses are in mcp_statuses.

Easy 2: 401, 403 or 400? (20 minutes)

Give the status and the error value for each: (1) no Authorization header; (2) a token with a bad signature; (3) a token that expired a second ago; (4) a valid token issued for a different server; (5) a valid token with notes:read calling a write tool; (6) a request body that is not JSON.

Done when: you answer 401, 401 invalid_token, 401 invalid_token, 401 invalid_token, 403 insufficient_scope with the scope named, and 400, and explain in a sentence why (5) is not a 401.

Hint

401 means "I do not know who you are or I do not accept this token"; 403 means "I know who you are and the answer is no".

Easy 3: Find the flaw (20 minutes)

Name the problem in each: (1) a tool takes user_id as an argument and reads that user's notes; (2) the server decodes the token and trusts sub without verifying; (3) tokens are accepted in ?access_token=; (4) the first challenge lists every scope; (5) the authorization server accepts any redirect address that starts with the registered one; (6) the MCP server forwards the client's token to the upstream API.

Done when: you name, in order, a broken-object-level authorization, no signature check, a token in the URL (logs), over-broad scope, open redirect, and token passthrough, and match each to the experiment in evaluate.py that shows it.

Hint

each flaw has a switch in ProtectedMcp or AuthServer.

Medium

Medium 1: A new tool with its own scope (60 minutes)

Add a notes_delete tool that requires a new scope notes:delete, implied by admin:export but not by notes:write. Make the server challenge with all scopes needed for that call in one 403, and have the client step up.

Done when: tests show a write-only user getting a 403 that names notes:delete, one step-up that keeps the old scopes, an administrator's token covering it through the hierarchy, and the client giving up after the retry limit for a user who may not have it.

Hint

put the scope in notes_server.REQUIRED and the hierarchy in IMPLIES; the client already takes a union.

Medium 2: Canonical resource addresses (90 minutes)

The server compares aud to one string. Make it accept the spec's canonical forms (a trailing slash or an uppercase scheme and host are the same resource) and reject https://mcp.example.com#fragment, a missing scheme, and a different port.

Done when: a table-driven test covers at least ten spellings, each with the expected accept or reject, and you explain which spellings you refused to treat as equal and why.

Hint

parse with urllib.parse, lowercase only the scheme and host, and compare ports explicitly.

Medium 3: Harden the fetcher (90 minutes)

Write ten more hostile URLs for guarded_fetch (an IPv6 zone id, a trailing dot on a name, a userinfo trick, a redirect chain that ends internal on the third hop, a port you did not expect, and others) and make every one fail or pass for the right reason.

Done when: all ten have tests, any that passed unexpectedly were fixed in net.py, and you list one thing a simulated network cannot show you about real DNS.

Hint

write the failing test first; for the trailing dot, ask what resolve receives.

Hard

Hard 1: A real authorization server (180 minutes)

Protect the notes server with a real authorization server (a development tenant of a hosted service, or an open-source one you run) using an official SDK on both sides. Capture the 401, the metadata, the authorization request and the token. (I did not run this, so your results are the ground truth.)

Done when: you give a table of differences between your capture and this example's messages, say whether the server supports Client ID Metadata Documents and the iss parameter, record what the token's aud actually contained, and say what you had to configure to make it name your server.

Hint

read the metadata first; the aud is often the first surprise.

Hard 2: How gullible is a real model? (180 minutes)

With a real model through a host you control, build the four poisoned descriptions from this module and a harmless control, run each 30 times, and record how often the model sends the secret. Repeat with the approval after sensitive reads control on. (I did not run this.)

Done when: you report rates for each variant with and without the control, say which variant worked best against the model you used, state clearly that your numbers describe that model on that day, and say what you would monitor in production.

Hint

fix the temperature and the prompt, and count a leak only when the secret appears in the server's log.

Hard 3: A security review (240 minutes)

Write the security review for the notes server as if it exposed your company's internal data: a data-flow diagram, at least eight threats (use the sections of this module as a source, and add two of your own), the control for each, and a test that would fail if the control were removed.

Done when: every threat maps to a control and a test, you have identified what stays unmitigated (and who accepts it), and a colleague who has not read this module can follow the diagram.

Hint

draw the arrows first; every arrow that crosses a boundary is a threat.