Skip to content

[medium] fix: schema_clusters.json silently accepts any related entry (keywords on the array, not items) - #1282

Open
elhoim wants to merge 2 commits into
MISP:mainfrom
elhoim:fix/schema-related-items
Open

elhoim wants to merge 2 commits into
MISP:mainfrom
elhoim:fix/schema-related-items

Conversation

@elhoim

@elhoim elhoim commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

BLUF — schema_clusters.json misplaces its related keywords, so any related entry validates today

  • Problem — In schema_clusters.json the properties and additionalProperties: false keywords on related sit on the array node beside items rather than inside it, so JSON Schema skips them and the only rule enforced is "each item is an object" — dest-uuid may be a non-string or absent, and any key at all is accepted.
  • Fix — Move those keywords inside items and add required: [dest-uuid, type], then correct the four entries in android.json, microsoft-activity-group.json, ransomware.json and surveillance-vendor.json where value-level metadata (refs, synonyms, payment-method, price) was written one level too deep.
  • Effect — Validation rejects malformed related entries instead of waving them through.

Problem

schema_clusters.json looks like it constrains related entries, but it does not. properties and additionalProperties: false sit on the array node, as siblings of items, while items is left bare:

"related": {
  "type": "array",
  "additionalProperties": false,     <-- applies to the array, i.e. to nothing
  "items": { "type": "object" },     <-- the only constraint that actually runs
  "properties": { "dest-uuid": ..., "type": ..., "tags": ... }   <-- also ignored
}

In JSON Schema, properties and additionalProperties apply to an object instance. The instance here is an array, so both keywords match nothing and are silently skipped. The one rule in force is "each item is an object" — dest-uuid need not be a string, or present at all, and any key whatsoever is accepted.

Demonstrated against both schemas with {"dest-uuid": 12345, "totally-bogus-key": "x"}:

committed  -> ACCEPTS a bogus related entry
fixed      -> REJECTS: 12345 is not of type 'string'

Three records have already drifted through the gap. I enumerated every key used across all 64,683 related entries in the repo:

  dest-uuid          64683
  type               64683
  tags               19421
  refs                   2   <- ransomware.json, surveillance-vendor.json
  synonyms               1   <- android.json
  payment-method         1   <- ransomware.json
  price                  1   <- ransomware.json

dest-uuid, type and tags are the intended vocabulary. The other four are value-level metadata that was written one level too deep.

Fix

Schema — move properties and additionalProperties: false inside items where they take effect, add required: ["dest-uuid", "type"] (both are present on all 64,683 entries, so this codifies existing practice), and constrain dest-uuid with a UUID pattern.

Data — the stray keys are migrated to the value's meta, where they belong, rather than deleted:

cluster value moved
android.json RedAlert2 related.synonyms → meta.synonyms
ransomware.json Booyah related.refs, related.payment-method, related.price → meta.*
surveillance-vendor.json RedLattice related.refs → meta.refs

microsoft-activity-group.json had two related entries on NOBELIUM with "dest-uuid": "" — they point at nothing and would fail the new pattern, so they are removed.

All four touched clusters get a version bump so instances pick the corrections up.

Verification

=== every related entry in the repo vs the new items subschema ===
  64681 related entries, 0 failures

=== full-schema validation ===
  android.json                       OK
  ransomware.json                    OK
  surveillance-vendor.json           OK
  microsoft-activity-group.json      OK
  threat-actor.json                  OK
  uavs.json                          OK
  tidal-tactic.json                  OK
  mitre-attack-pattern.json          OK

=== schema itself ===
  check_schema passed

(64,681 rather than 64,683 — the two empty-dest-uuid NOBELIUM entries are gone.)

Note

This makes the schema structurally enforce related. It does not check that a dest-uuid resolves to a cluster that exists — 2,214 edges currently point at unknown uuids. That is a referential-integrity check for CI, submitted separately.

🤖 Generated with Claude Code

properties and additionalProperties:false sit on the `related` ARRAY node as
siblings of items, while items is bare {"type": "object"}. Both keywords
apply to object instances, so against an array they match nothing and are
silently skipped. The only rule in force is "each item is an object":
dest-uuid need not be a string or even present, and any key is accepted.

  committed schema -> ACCEPTS {"dest-uuid": 12345, "totally-bogus-key": "x"}
  fixed schema     -> REJECTS: 12345 is not of type 'string'

Enumerating every key across all 64,683 related entries: dest-uuid 64683,
type 64683, tags 19421 -- plus refs 2, synonyms 1, payment-method 1,
price 1, which are value-level metadata written one level too deep.

Move properties/additionalProperties inside items, require dest-uuid and
type (present on all 64,683 entries), and add a UUID pattern for dest-uuid.

Migrate the four stray keys to the value's meta rather than dropping them
(android/RedAlert2, ransomware/Booyah, surveillance-vendor/RedLattice), and
remove the two related entries on microsoft-activity-group/NOBELIUM with an
empty dest-uuid. Version bumped on all four clusters.

Verified: 64,681 related entries validate with 0 failures; full-schema
validation passes on the touched and largest clusters; check_schema passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZGwPoa8MMfkhCw47rDLA4
@elhoim elhoim changed the title fix: schema_clusters.json silently accepts any related entry (keywords on the array, not items) [medium] fix: schema_clusters.json silently accepts any related entry (keywords on the array, not items) Sep 3, 2026
Only the android cluster version conflicted: main is at 27 after its new entries, so it is now 28.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant