Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/services/meta-classes/base.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,16 @@ class BaseService extends ServiceWithHooks {
data.stix.x_mitre_attack_spec_version = document.stix.x_mitre_attack_spec_version;
data.stix.revoked = document.stix.revoked ?? false;

// Workspace-only updates may originate from client STIX projections that
// omit persisted revision fields. Restore them before comparing the STIX
// revision for immutability.
if (document.stix.x_mitre_modified_by_ref !== undefined) {
data.stix.x_mitre_modified_by_ref = document.stix.x_mitre_modified_by_ref;
}
if (document.stix.object_marking_refs !== undefined) {
data.stix.object_marking_refs = document.stix.object_marking_refs;
}

// Preserve x_mitre_is_subtechnique — changing subtechnique status requires
// the dedicated conversion endpoints, not the generic update path.
if (document.stix.x_mitre_is_subtechnique !== undefined) {
Expand Down
17 changes: 17 additions & 0 deletions app/tests/api/techniques/techniques.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,23 @@ describe('Techniques Basic API', function () {
expect(res.body.message).toContain('immutable');
});

it('PUT /api/techniques accepts a workspace update with omitted persisted STIX fields', async function () {
const body = cloneForCreate(technique1);
delete body.stix.x_mitre_modified_by_ref;
delete body.stix.object_marking_refs;

const res = await request(app)
.put('/api/techniques/' + technique1.stix.id + '/modified/' + technique1.stix.modified)
.send(body)
.set('Accept', 'application/json')
.set('Cookie', `${passportCookie.name}=${passportCookie.value}`)
.expect(200)
.expect('Content-Type', /json/);

expect(res.body.stix.x_mitre_modified_by_ref).toBe(technique1.stix.x_mitre_modified_by_ref);
expect(res.body.stix.object_marking_refs).toEqual(technique1.stix.object_marking_refs);
});

it('POST /api/techniques does not create a technique with the same id and modified date', async function () {
const body = cloneForCreate(technique1);
await request(app)
Expand Down
Loading