Skip to content

Without --conneg, reject non-JSON-LD PUTs with 415 instead of silently storing #295

Description

@melvincarvalho

The inconsistency

JSS is "JSON-LD native" by design — `--conneg` is off by default (`src/config.js:29`: `conneg: false`). The stance is: if you want to read/write Turtle, opt in explicitly.

But the PUT handler today doesn't enforce that stance. Looking at `src/handlers/resource.js:638-647`:

```js
const inputType = contentType.split(';')[0].trim().toLowerCase();
if (connegEnabled && (inputType === RDF_TYPES.TURTLE || inputType === RDF_TYPES.N3)) {
try {
const jsonLd = await toJsonLd(content, contentType, resourceUrl, connegEnabled);
content = Buffer.from(JSON.stringify(jsonLd, null, 2));
} catch (e) { /* 400 */ }
}
// … goes on to storage.write(storagePath, content) unconditionally
```

So with `--conneg` off:

  • Client PUTs `Content-Type: text/turtle` body.
  • The if-block is skipped (conneg disabled).
  • `content` is still the raw Turtle bytes.
  • JSS writes those bytes to disk at, say, `foo.jsonld` → now the file on disk is Turtle content with a `.jsonld` URL.
  • Subsequent GET: JSS reads the file, `getContentType()` sees `.jsonld` → says `application/ld+json`. Serves Turtle bytes with a JSON-LD content-type header. Parser on the client errors.

So `--conneg=false` + Turtle PUT = silent corruption.

Proposal

When `--conneg` is off, reject non-JSON-LD PUT bodies on RDF routes with `415 Unsupported Media Type` and an `Accept-Post` / `Accept-Patch` header listing `application/ld+json`. Makes the server's contract explicit:

"I am JSON-LD native. Start me with `--conneg` if you want Turtle / N3 translation."

Rough shape:

```js
const rdfInputTypes = [RDF_TYPES.JSON_LD, 'application/json'];
if (!connegEnabled && contentType && !rdfInputTypes.includes(inputType) && looksLikeRdfResource(resourceUrl)) {
return reply
.code(415)
.header('Accept', 'application/ld+json')
.send({ error: 'Unsupported Media Type',
message: 'This server is JSON-LD native. Start with --conneg to accept text/turtle or text/n3.' });
}
```

`looksLikeRdfResource()` is the small new helper — e.g. extension in `{.jsonld, .json, .ttl, .n3, .acl, .meta}`, or no extension and path looks like a Solid resource (profile/card, …). Out of scope to get exhaustive; start with the common set.

Non-goals

  • Not changing default mode. `conneg: false` stays the default.
  • Not changing behaviour when `--conneg` is on — that path is fine (translation happens).
  • Not touching binary / non-RDF uploads (images, text, etc.). The guard is specifically for RDF-shaped resources.

Why not fold this into #294

Scope

~20 lines in `handlePut` + a helper. Plus one test per rejected case. Small, but touches the PUT path, so the full suite is worth running.

Happy to PR after #294.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions