CenterForDigitalHumanities / CenterForDigitalHumanities/TPEN-services

PUT page preserves user-supplied `creator` on existing-line updates

Open Beginner friendly
#500 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Summary

The page PUT route lets a client override `creator` on an existing-line update. Authenticated callers can attribute a saved line to any agent IRI they choose. New lines are unaffected.

## Where

`page/index.js:119-123`

```js
const line = item.id?.startsWith?.('http')
? new Line(item)
: Line.build(projectId, pageId, item, user.agent.split('/').pop())
line.creator ??= user.agent.split('/').pop()
```

For the `http`-id branch, `new Line(item)` reads `creator` from input via destructuring (`classes/Line/Line.js:16-29`). The `??=` on the next line only fills when nullish, so an input-supplied `creator` is preserved. The `Line.build` branch is safe — its destructure (`Line.js:32-36`) drops `creator` from input and uses the function-arg from `user.agent`.

`#saveLineToRerum` then writes the line to RERUM via `creator: await fetchUserAgent(this.creator)`. `fetchUserAgent` (`utilities/shared.js:343-358`) returns any string starting with `http` verbatim, so the supplied IRI flows through unchanged.

## Reproduction

PUT a page with an existing-line update whose body carries a fake `creator`:

```http
PUT /project/{projectId}/page/{pageId}
Authorization: Bearer
Content-Type: application/json

{
"items": [
{
"id": "https://store.rerum.io/v1/id/",
"body": [{ "type": "TextualBody", "value": "x", "format": "text/plain" }],
"target": { /* unchanged */ },
"creator": "https://store.rerum.io/v1/id/SOMEONE_ELSE"
}
]
}
```

Returns 200. The new RERUM version of that line records `creator: https://store.rerum.io/v1/id/SOMEONE_ELSE`, not the authenticated user's agent.

## Impact

- Audit/provenance via `__rerum.generatedBy` is intact (RERUM stamps that from auth), so the underlying "who actually wrote this version" is recoverable.
- The body-level `creator` is what consumers read for attribution display ("authored by X"). Anything that surfaces creator in the UI shows the wrong agent.
- Authentication is required, so this isn't anonymous spoofing — but a token holder can attribute lines to other users, which has obvious abuse and audit-trail implications.
- `screenContentMiddleware` / `hasSuspiciousPageData` don't normalize `creator`; the common_keys list checks for script injection in label/value/text/etc., not identity fields.

## Suggested fix

Strip `creator` from input on the existing-line branch and force it from auth, mirroring how the page-level creator is set at `page/index.js:95`:

```js
const line = item.id?.startsWith?.('http')
? new Line({ ...item, creator: undefined })
: Line.build(projectId, pageId, item, user.agent.split('/').pop())
line.creator = user.agent.split('/').pop()
```

(Or hard-assign instead of `??=` and document that `creator` is server-controlled, then strip in the constructor as a defense-in-depth.)

Worth a similar audit on the `PUT /line/:lineId` and `PATCH` routes — `Object.assign(line, req.body)` at `line/index.js:130` has the same shape and likely the same exposure.

## Found via

`/static-review` of [TPEN-Prompts#4](https://github.com/CenterForDigitalHumanities/TPEN-Prompts/pull/4). The prompts UI accepts JSON pasted from an LLM and submits it via this PUT; an LLM that hallucinates a `creator` field would silently misattribute lines. The right defense is here, not in the client.

Contributor guide

Open the contributing guide

Research direction

Start at the page PUT route in page/index.js:119-123, then read the Line constructor and build method in classes/Line/Line.js:16-36. Check how fetchUserAgent in utilities/shared.js:343-358 handles the existing-line update. Done means an input-supplied creator cannot replace the authenticated user's agent on the saved line.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
api, backend, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.