Chapter 15 — Guided lab: reconstruct a bounded incident timeline
Lesson map
Lesson 15 published. Map, example, EX043.
flowchart TD
N0["Learning outcomes and connection"]
N1["15.1 Why timelines matter"]
N2["15.2 Normalization rules"]
N3["15.3 Bounding"]
N4["15.4 Lab T-15 synthetic : permits-clerk-4"]
N5["15.5 Timeline record format"]
N6["Chapter synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6
N6 --> EX["Worked example"]
EX --> Q["Exercise EX043"]
Logical drawing
flowchart LR
N0["Learning outcomes and connection"]
N1["15.1 Why timelines matter"]
N2["15.2 Normalization rules"]
N3["15.3 Bounding"]
N4["15.4 Lab T-15 synthetic : permits-clerk-4"]
N5["15.5 Timeline record format"]
N6["Chapter synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> Q["EX043"]
[Learning outcomes and connection]
|
v
[15.1 Why timelines matter]
|
v
[15.2 Normalization rules]
|
v
[15.3 Bounding]
|
v
[15.4 Lab T-15 synthetic : permits-clerk-4]
|
v
[15.5 Timeline record format]
|
v
[Chapter synthesis]
|
v
[Worked example] --> [Exercise EX043]
Learning outcomes and connection
You will be able to (1) normalize timestamps from sources with different zones and clock errors; (2) merge them into a bounded timeline with each correction recorded; and (3) state what the timeline shows and what remains unknown. This guided lab applies Chapters 4, 9, 12 and 14 to one case.
15.1 Why timelines matter
A timeline orders events so that causes can be distinguished from effects. It answers "what happened first?", "how long did the actor have?" and "what was the earliest evidence we missed?". It is only as good as its clocks: a five-minute error can reverse a sign-in and a file access. In FCS-214, the order of the push approval, the new MFA method and the archive read mattered for deciding between the hypotheses. The timeline showed the approval at 14:02 while the clerk was in a training session, which supported the external-actor explanation.
15.2 Normalization rules
Convert every timestamp to UTC in RFC 3339 form. For each source, record the original format, its zone (explicit, configured or assumed) and any measured clock offset. Measure offsets at collection by comparing the source's clock with an NTP-synchronized reference (RFC 5905); record the measurement. Never silently "fix" a time; keep original and corrected values. In FCS-214, every source was normalized this way before the hypothesis table was built, so the push approval and the new MFA method could be placed in the same order with confidence.
15.3 Bounding
A bounded timeline has a defined start and end and a defined set of sources. State them. Gaps inside the bound are as important as events: "VPN logs present; file server logs only from 14:00; no endpoint data for the clerk's laptop". For FCS-214, the bound began with the spraying on 9 May and ended with containment. Stating it tells reviewers what the timeline does not cover, for example events before 9 May that were not examined. Each gap is listed with its effect, so a later reader knows which questions the timeline cannot answer.
15.4 Lab T-15 (synthetic): permits-clerk-4
Case FCS-214 (fictional): did someone use permits-clerk-4 to read the permits archive? Sources: identity provider logs in UTC; VPN appliance logs in local time without a zone (the appliance is configured for UTC−5); file-server audit logs whose clock was measured 95 seconds fast at collection. The script applies each correction and prints the merged timeline.
# Lab T-15: reconstruct a bounded timeline from three synthetic sources
from datetime import datetime, timedelta, timezone
# Source clocks: IdP logs UTC; VPN appliance local time UTC-5 (no zone in log);
# file server clock measured 95 seconds fast against NTP at collection.
idp = [("2026-05-11T14:02:10Z","signin ok user=permits-clerk-4 from 198.51.100.23 mfa=push-approved"),
("2026-05-11T14:20:44Z","mfa method added user=permits-clerk-4")]
vpn = [("2026-05-11 09:03:02","session start user=permits-clerk-4 src=198.51.100.23"),
("2026-05-11 09:41:30","session end user=permits-clerk-4")]
fs = [("2026-05-11T14:12:35Z","bulk read 412 files share=permits-archive user=permits-clerk-4")]
ev=[]
for t,m in idp: ev.append((datetime.fromisoformat(t.replace("Z","+00:00")),"IdP",m,"none"))
for t,m in vpn:
d=datetime.strptime(t,"%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone(timedelta(hours=-5)))
ev.append((d.astimezone(timezone.utc),"VPN",m,"+5h zone"))
for t,m in fs:
ev.append((datetime.fromisoformat(t.replace("Z","+00:00"))-timedelta(seconds=95),"FS",m,"-95s skew"))
for d,s,m,c in sorted(ev):
print(d.strftime("%Y-%m-%dT%H:%M:%SZ"), s.ljust(3), f"[{c}]", m)
first=min(e[0] for e in ev); last=max(e[0] for e in ev)
print("window minutes", round((last-first).total_seconds()/60, 1))
Expected output:
2026-05-11T14:02:10Z IdP [none] signin ok user=permits-clerk-4 from 198.51.100.23 mfa=push-approved
2026-05-11T14:03:02Z VPN [+5h zone] session start user=permits-clerk-4 src=198.51.100.23
2026-05-11T14:11:00Z FS [-95s skew] bulk read 412 files share=permits-archive user=permits-clerk-4
2026-05-11T14:20:44Z IdP [none] mfa method added user=permits-clerk-4
2026-05-11T14:41:30Z VPN [+5h zone] session end user=permits-clerk-4
window minutes 39.3
Reading the result: the IdP sign-in (14:02:10) precedes the VPN session (14:03:02) by under a minute from the same address, consistent with one actor. The bulk read of 412 files at corrected 14:11:00 falls inside the session. The new MFA method at 14:20:44 suggests persistence. Without the zone correction, the VPN session would have appeared five hours before the sign-in; without the skew correction, the file read would have moved by 95 seconds — not decisive here but recorded. What remains unknown: whether the clerk herself was acting (she must be interviewed with her manager, Chapter 16), and what was done with the 412 files.
15.5 Timeline record format
Each row: corrected UTC time, source, original timestamp, correction applied, event, actor, asset, evidence reference (file and hash), analyst note. Analyst notes are labelled as interpretation. Keeping the original timestamp and the correction side by side lets anyone check a conversion later. If a measured clock offset is applied to one source, the row shows the offset, so a reviewer can tell whether two events really occurred in the order shown. The evidence reference with its hash lets a reviewer open the exact file the row came from, and the analyst note is kept out of the event field so interpretation never looks like observation.
15.6 Common errors and limits
- Assuming a log without a zone is UTC.
- Ignoring measured skew because it is small.
- Mixing interpretation into event descriptions.
- Unbounded timelines that grow until nobody can read them.
Each error can reverse an order of events. A log assumed to be UTC that was actually local time can move an event by hours; a two-minute skew can place a file access before the sign-in that caused it; interpretation in event descriptions makes the timeline argue rather than record; and an unbounded timeline hides its gaps. FCS-214's timeline avoided them by recording zones and offsets for each source.
Exercises — chapter 15
Attempt each exercise before opening the worked results. Every case, organization, person, address and number in an exercise is synthetic unless a cited public source is named.
EX043 — Wrong assumption. Steps: rerun the lab treating VPN times as UTC. Describe the resulting timeline and the false conclusion it invites. Rubric: 3 = VPN session appears at 09:03–09:41, before sign-in, implying VPN access without the IdP sign-in and ending before the file read; explains the false conclusion; 2 = describes only; 1 = partial; 0 = none.
EX044 — Gap statement. Steps: write the bound and gap statement for FCS-214. Rubric: 3 = start/end, sources, known gaps (laptop endpoint, data after read, email), impact on conclusions; 2 = missing impact; 1 = vague; 0 = none.
EX045 — Timeline row. Steps: write the full record row for the file-server event. Rubric: 3 = all fields of the timeline record format with original and corrected times and hash reference; 2 = missing original or hash; 1 = partial; 0 = none.
Chapter synthesis
Normalize every time to UTC with recorded zone and skew corrections, keep originals, bound the timeline by window and sources, state gaps explicitly and keep interpretation labelled. Small clock errors can invert causality; corrections are evidence too.
Continue studying — chapter 15 of 30
← Chapter 14 · Course chapter index · Chapter 16 →
Continue to the next authored lesson.