Design discussion: knowledge as a capability, and scoring graphs vs attack graphs
Raising this as a discussion rather than a PR, because it touches the closed edge
vocabulary — and that closure is the design decision everything else rests on. If
the answer is "no, deliberately", that is a completely reasonable outcome and the
reasoning is worth having written down.
Where this comes from
I tried to express a shipped CTF in the v2 entity graph — three challenges,
thirteen scored questions, five hundred points, running on real hardware at a
conference. Not to port it, just to find where the model stops fitting. Most of
it fits well. Two things did not, and they turned out to be the same thing.
1. Every edge type is a capability; some steps only grant knowledge
The eight types — network_reach, shell_as, creds_for, db_session,
file_read, file_write, code_exec, token_for — all say some version of
"you can now do X".
Three steps in the challenge grant no new technical ability at all, and between
them gate 265 of 500 points:
| Step |
What it grants |
Gates |
| A base64 note in a JPEG comment naming an undocumented query parameter |
knowing the endpoint exists |
100 pts |
| A published policy describing how staff passwords are constructed |
knowing the derivation rule |
150 pts |
| A printed card teaching the credential's shape |
knowing the search space |
the crack |
None grants file_read — the attacker could already read the file. None grants
creds_for — no credential is disclosed anywhere. What changes is that the
attacker now knows where to look, or how to build something.
entity_graph_model.md addresses this directly: information disclosure without a
typed capability edge "is merged into larger entities". That is a coherent choice,
but I think it has a consequence worth naming.
Difficulty in this domain lives almost entirely in the knowledge steps. A
capability edge is a door you can push once you reach it. A knowledge edge is
something you must notice. Model only the first and you can generate an
arbitrarily long chain in which no step is hard — which matches the observation
in docs/eval/overview.md that there is no difficulty stratification yet. I
suspect these are the same gap seen from two sides: difficulty is not currently
representable, so it cannot be scored.
Merging knowledge into its consumer also loses the thing that makes the step
tunable. Whether the policy page is linked from the same index as the staff
export is the entire difference between a five-minute step and a one-hour one,
and after merging there is nowhere to express that.
Rough shape, if it were wanted
A ninth type, knowledge_of, with params (subject, host, specificity), where
subject names what became known — an endpoint, a rule, a naming convention, an
identity — and specificity distinguishes "there is an admin panel somewhere"
from "the admin panel is at /manage/v2".
Validation stays mechanical: a consumer declaring requires: knowledge_of is
satisfied by any producer of a matching subject. The DAG and reachability checks
are unchanged. What it buys is that the planner can no longer quietly assume the
attacker knows things, and the graph gains the edges that carry the difficulty.
The real cost is honest and worth stating: it is the first edge type whose
satisfaction cannot be proven by a procedure. You can assert a shell opened. You
cannot assert someone understood. The best available proxy is that the
downstream step becomes performable, which is what the chain test already
measures — so perhaps knowledge_of is validated only transitively, and that
asymmetry may be exactly why it was left out.
2. Attack graphs and scoring graphs are different graphs
Eight of the thirteen questions grant no onward capability whatsoever. Reading a
maintenance page's source is worth 12 points; reading an IT bulletin is worth 8.
They are scored observations passed on the way to somewhere else, not steps in
the chain.
In the current model they are entities with empty provides. The validator will
accept that, but the build pipeline has no particular reason to produce them and
the L2 probe has nothing to assert beyond "some text is present".
A capability graph answers what can I do next. A scoring graph answers what do
I get credit for. They share a topology and are not the same graph. Only the
first exists today — reasonably, since GoE targets ranges rather than
competitions. But the training market wants the second, and the packaged artifact
is the natural place for it: a list of scoreable observations, each with a
location, an expected value and a point weight, derived from the graph the
pipeline already built. That is close to free and it is the difference between "a
vulnerable environment" and "an exercise".
What I am not proposing
Not asking for RF, physical props, or pixel-based answers — the rest of the
challenge needed all three and they are plainly out of scope for a container-based
generator. Those are boundaries of the domain, not gaps in the model.
Happy to be told no
If knowledge is deliberately out of scope because it cannot be validated, that is
a sound reason and I would rather see it recorded in entity_graph_model.md than
argued away. The current text explains what happens to information disclosure
but not why, and the why is the interesting part.
Design discussion: knowledge as a capability, and scoring graphs vs attack graphs
Raising this as a discussion rather than a PR, because it touches the closed edge
vocabulary — and that closure is the design decision everything else rests on. If
the answer is "no, deliberately", that is a completely reasonable outcome and the
reasoning is worth having written down.
Where this comes from
I tried to express a shipped CTF in the v2 entity graph — three challenges,
thirteen scored questions, five hundred points, running on real hardware at a
conference. Not to port it, just to find where the model stops fitting. Most of
it fits well. Two things did not, and they turned out to be the same thing.
1. Every edge type is a capability; some steps only grant knowledge
The eight types —
network_reach,shell_as,creds_for,db_session,file_read,file_write,code_exec,token_for— all say some version of"you can now do X".
Three steps in the challenge grant no new technical ability at all, and between
them gate 265 of 500 points:
None grants
file_read— the attacker could already read the file. None grantscreds_for— no credential is disclosed anywhere. What changes is that theattacker now knows where to look, or how to build something.
entity_graph_model.mdaddresses this directly: information disclosure without atyped capability edge "is merged into larger entities". That is a coherent choice,
but I think it has a consequence worth naming.
Difficulty in this domain lives almost entirely in the knowledge steps. A
capability edge is a door you can push once you reach it. A knowledge edge is
something you must notice. Model only the first and you can generate an
arbitrarily long chain in which no step is hard — which matches the observation
in
docs/eval/overview.mdthat there is no difficulty stratification yet. Isuspect these are the same gap seen from two sides: difficulty is not currently
representable, so it cannot be scored.
Merging knowledge into its consumer also loses the thing that makes the step
tunable. Whether the policy page is linked from the same index as the staff
export is the entire difference between a five-minute step and a one-hour one,
and after merging there is nowhere to express that.
Rough shape, if it were wanted
A ninth type,
knowledge_of, with params(subject, host, specificity), wheresubjectnames what became known — an endpoint, a rule, a naming convention, anidentity — and
specificitydistinguishes "there is an admin panel somewhere"from "the admin panel is at
/manage/v2".Validation stays mechanical: a consumer declaring
requires: knowledge_ofissatisfied by any producer of a matching subject. The DAG and reachability checks
are unchanged. What it buys is that the planner can no longer quietly assume the
attacker knows things, and the graph gains the edges that carry the difficulty.
The real cost is honest and worth stating: it is the first edge type whose
satisfaction cannot be proven by a procedure. You can assert a shell opened. You
cannot assert someone understood. The best available proxy is that the
downstream step becomes performable, which is what the chain test already
measures — so perhaps
knowledge_ofis validated only transitively, and thatasymmetry may be exactly why it was left out.
2. Attack graphs and scoring graphs are different graphs
Eight of the thirteen questions grant no onward capability whatsoever. Reading a
maintenance page's source is worth 12 points; reading an IT bulletin is worth 8.
They are scored observations passed on the way to somewhere else, not steps in
the chain.
In the current model they are entities with empty
provides. The validator willaccept that, but the build pipeline has no particular reason to produce them and
the L2 probe has nothing to assert beyond "some text is present".
A capability graph answers what can I do next. A scoring graph answers what do
I get credit for. They share a topology and are not the same graph. Only the
first exists today — reasonably, since GoE targets ranges rather than
competitions. But the training market wants the second, and the packaged artifact
is the natural place for it: a list of scoreable observations, each with a
location, an expected value and a point weight, derived from the graph the
pipeline already built. That is close to free and it is the difference between "a
vulnerable environment" and "an exercise".
What I am not proposing
Not asking for RF, physical props, or pixel-based answers — the rest of the
challenge needed all three and they are plainly out of scope for a container-based
generator. Those are boundaries of the domain, not gaps in the model.
Happy to be told no
If knowledge is deliberately out of scope because it cannot be validated, that is
a sound reason and I would rather see it recorded in
entity_graph_model.mdthanargued away. The current text explains what happens to information disclosure
but not why, and the why is the interesting part.