feat(admin): add Collections and Placements tabs to adagents.json builder#4424
Merged
bokelley merged 3 commits intoMay 12, 2026
Merged
Conversation
…lder (#4420) Adds v3 parity for collections[] and placements[] surfaces to the adagents.json builder tool (deferred from PR #4415 by expert consensus). - Collections tab: 8-field form (collection_id, name, kind, language, status, cadence, description) + raw JSON escape hatch for advanced fields - Placements tab: placement_id, name, description, property_ids checkbox list from state.properties, property_tags, placement_tags chip input - Placement Tags sub-section within Placements tab (same chip+delete pattern as existing Signal Tags tab) - Agent modal: optional Placement constraints section (placement_ids and placement_tags checkboxes — overlays on any property-scoped auth type) - Import (importFile, startManaging) and export (exportFile, updateJsonPreview) all round-trip collections, placements, placement_tags - All card renders use createElement/textContent for XSS safety No backend or schema changes. server/src/adagents-manager.ts already emits v3 and is field-agnostic. https://claude.ai/code/session_01Fkmv4wwAtqD1JoKeaioXgr
…builder tabs
- Revert spurious adcp_version downgrade in static/schemas/source/index.json
- Strip reserved keys (collection_id, name, etc.) from advanced textarea before
Object.assign in saveCollectionForm; prevents silent override of validated fields
- Add duplicate-ID guards on saveCollectionForm and savePlacementForm
- Convert state.placementTags from string[] to {tag:{name,description}} map,
preserving imported metadata on round-trips (import/export/startManaging)
- removePlacementTag cascade-cleans pl.tags[] on every placement (mirrors removeSignalTag)
- createPlacementTag normalises input (.toLowerCase + /[^a-z0-9_]/ → '_')
- savePlacementForm validates property_tags pattern; normalises placement tags on save
- escapeHtml() all user-originated values in innerHTML template literals:
showPlacementForm (prop.property_id, prop.name), updateAgentPlacementCheckboxes
(pl.placement_id, pl.name, tag), renderPlacementTags (tag, meta.description)
- Fix hardcoded #666 colours → var(--color-text-muted) in placement checkbox empties
- Expand changeset body with full change description
https://claude.ai/code/session_01Fkmv4wwAtqD1JoKeaioXgr
Post-expert-review finding on PR #4424: renderPlacementTags built its delete button as onclick="removePlacementTag('${escapeHtml(tag)}')" escapeHtml encodes ' to ', but the browser decodes the HTML attribute before parsing the JS string literal — so a malicious placement_tags key like `x'),alert(1)//` breaks out of the JS context. Reachable via importFile and validateDomain paths. Rewrote the function with createElement + textContent and a closure-based onclick (captures `tag` directly), matching the pattern renderPlacements/renderCollections already use. E2E (Playwright + Docker): seeded state.placementTags with the attribute-bypass payload + an HTML-injection payload — no dialog, no injected <img>, payloads visible as text, delete button still removes via closure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Post-expert-review fix landed in e1a855e:
E2E (Playwright + Docker): seeded CI fully green including |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #4420
Adds Collections and Placements tabs to the
adagents.jsonbuilder (server/public/adagents-builder.html), bringing the builder to v3 parity for these two top-level arrays that were deferred from the initial release.collection_id,name,kind,status,cadence,language,description, plus a raw-JSON escape hatch for remaining optional schema fields (genre, content_rating, talent[], etc.). Duplicate-ID guard on save; reserved keys stripped from the advanced textarea to prevent silent override of validated fields.placement_id,name,description, property scope (per-property checkboxes + free-text property tags), and placement tags. Placement tag registry stored as{ tag: { name, description } }map matching theplacement_tagsschema shape; metadata is preserved on import/export round-trips. Tags auto-registered on placement save; cascade-deleted from all placements on tag removal (mirrorsremoveSignalTag). Pattern validation (^[a-z0-9_]+$) on property tags; placement tags normalised to[a-z0-9_]on save.placement_ids/placement_tagsoverlay fields rendered as live checkboxes on property-scoped agent authorization entries.Not surfaced in this PR (deliberate scope):
collection_idsandformat_idson placement definitions; these can be added in a follow-up once requirements are clearer.Non-breaking justification
Pure UI addition to a standalone HTML admin tool. No schema changes, no API changes, no protocol changes. The builder is not part of the AdCP protocol surface.
Security
All new
innerHTMLtemplate literals pipe user-originated values (placement IDs, names, tag strings) through the existingescapeHtml()helper. No neweval,innerHTMLwithout escaping, or dynamic<script>injection.Pre-PR review
Two expert agents reviewed the diff before this PR was opened:
placement_tagsdata loss on import, and tag pattern validation — all fixed.removePlacementTagcascade gap,Object.assign(col, advanced)override risk, spurious schema file changes, duplicate-ID gap, and tag normalization — all fixed.Test plan
anyOfrequirement mettags[]in the JSON previewplacement_tagsmetadata; export and verify metadata is preserved verbatimcollection_id; verify alert fires{"collection_id":"override"}in the advanced textarea; verify it is silently strippedGenerated by Claude Code