or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-storage.mdequivalence.mdindex.mdmarkings.mdobject-creation.mdpattern-matching.mdrelationships.mdstix-domain-objects.mdstix-observables.mdutilities.mdversioning.md

versioning.mddocs/

0

# Object Versioning and Evolution

1

2

Version management system for creating new versions of STIX objects, tracking changes over time, and handling object revocation with proper timestamp and identifier management.

3

4

## Capabilities

5

6

### Versioning Functions

7

8

Core functions for creating new versions and revoking STIX objects.

9

10

```python { .api }

11

def new_version(stix_obj, **kwargs):

12

"""

13

Create a new version of an existing STIX object.

14

15

Parameters:

16

- stix_obj: Existing STIX object to create new version of

17

- **kwargs: Properties to update in the new version

18

19

Returns:

20

New STIX object with updated properties and incremented modified timestamp

21

22

Raises:

23

UnmodifiablePropertyError: If attempting to modify immutable properties

24

TypeNotVersionableError: If object type doesn't support versioning

25

ObjectNotVersionableError: If object lacks required versioning properties

26

RevokeError: If attempting to version a revoked object

27

"""

28

29

def revoke(stix_obj):

30

"""

31

Create a revoked version of a STIX object.

32

33

Parameters:

34

- stix_obj: STIX object to revoke

35

36

Returns:

37

New STIX object marked as revoked

38

39

Raises:

40

RevokeError: If object is already revoked

41

TypeNotVersionableError: If object type doesn't support revocation

42

"""

43

```

44

45

### Basic Versioning

46

47

Create new versions of STIX objects with updated properties.

48

49

Usage examples:

50

51

```python

52

from stix2 import new_version, Indicator, Malware

53

54

# Create initial version

55

indicator_v1 = Indicator(

56

name="Suspicious Domain",

57

indicator_types=["malicious-activity"],

58

pattern_type="stix",

59

pattern="[domain-name:value = 'suspicious.com']",

60

confidence=50

61

)

62

63

print(f"V1 ID: {indicator_v1.id}")

64

print(f"V1 Created: {indicator_v1.created}")

65

print(f"V1 Modified: {indicator_v1.modified}")

66

print(f"V1 Confidence: {indicator_v1.confidence}")

67

68

# Create new version with updated confidence

69

indicator_v2 = new_version(indicator_v1, confidence=85)

70

71

print(f"V2 ID: {indicator_v2.id}") # Same ID

72

print(f"V2 Created: {indicator_v2.created}") # Same creation time

73

print(f"V2 Modified: {indicator_v2.modified}") # New modification time

74

print(f"V2 Confidence: {indicator_v2.confidence}") # Updated confidence

75

76

# Create another version with additional changes

77

indicator_v3 = new_version(indicator_v2,

78

confidence=95,

79

description="Confirmed malicious domain used in phishing campaign",

80

labels=["phishing", "credential-theft"]

81

)

82

83

# Versioning preserves object identity

84

assert indicator_v1.id == indicator_v2.id == indicator_v3.id

85

assert indicator_v1.created == indicator_v2.created == indicator_v3.created

86

assert indicator_v1.modified < indicator_v2.modified < indicator_v3.modified

87

```

88

89

### Immutable Properties

90

91

Certain properties cannot be changed when creating new versions.

92

93

```python

94

from stix2 import new_version, UnmodifiablePropertyError

95

96

try:

97

# Attempting to change immutable properties raises error

98

invalid_version = new_version(indicator_v1,

99

id="indicator--new-id-12345678-1234-1234-1234-123456789012", # Cannot change ID

100

type="malware", # Cannot change type

101

created="2022-01-01T00:00:00.000Z" # Cannot change creation time

102

)

103

except UnmodifiablePropertyError as e:

104

print(f"Cannot modify: {e.unchangable_properties}")

105

106

# Valid versioning - only modifiable properties

107

valid_version = new_version(indicator_v1,

108

name="Updated Suspicious Domain", # Can change name

109

confidence=75, # Can change confidence

110

description="Additional context added", # Can add description

111

external_references=[ # Can add external references

112

{

113

"source_name": "internal-analysis",

114

"description": "Internal security team analysis"

115

}

116

]

117

)

118

```

119

120

### Object Revocation

121

122

Mark objects as revoked to indicate they should no longer be used.

123

124

```python

125

from stix2 import revoke, RevokeError

126

127

# Create malware object

128

malware = Malware(

129

name="Old Malware Name",

130

malware_types=["trojan"]

131

)

132

133

# Revoke the object

134

revoked_malware = revoke(malware)

135

136

print(f"Original revoked: {malware.revoked}") # False (or not present)

137

print(f"Revoked version: {revoked_malware.revoked}") # True

138

print(f"Same ID: {malware.id == revoked_malware.id}") # True

139

print(f"Updated modified: {revoked_malware.modified > malware.modified}") # True

140

141

# Cannot revoke already-revoked objects

142

try:

143

double_revoked = revoke(revoked_malware)

144

except RevokeError as e:

145

print(f"Revocation error: {e}")

146

147

# Cannot create new versions of revoked objects

148

try:

149

new_version_of_revoked = new_version(revoked_malware, name="Updated Name")

150

except RevokeError as e:

151

print(f"Versioning error: {e}")

152

```

153

154

### Version Tracking

155

156

Track and manage multiple versions of objects over time.

157

158

```python

159

from stix2 import MemoryStore, Filter

160

161

# Create store for version tracking

162

store = MemoryStore()

163

164

# Add all versions of an object

165

threat_actor_v1 = ThreatActor(

166

name="APT Example",

167

threat_actor_types=["nation-state"],

168

first_seen="2020-01-01T00:00:00.000Z"

169

)

170

171

threat_actor_v2 = new_version(threat_actor_v1,

172

aliases=["APT-EX", "Example Group"],

173

sophistication="advanced"

174

)

175

176

threat_actor_v3 = new_version(threat_actor_v2,

177

last_seen="2021-12-31T23:59:59.000Z",

178

goals=["espionage", "data-theft"]

179

)

180

181

# Store all versions

182

store.add([threat_actor_v1, threat_actor_v2, threat_actor_v3])

183

184

# Get latest version (most recent modified timestamp)

185

latest_version = store.get(threat_actor_v1.id) # Returns most recent

186

print(f"Latest version modified: {latest_version.modified}")

187

188

# Get all versions of an object

189

all_versions = store.all_versions(threat_actor_v1.id)

190

print(f"Total versions: {len(all_versions)}")

191

192

for version in all_versions:

193

print(f"Version modified: {version.modified}")

194

print(f"Properties: {len(version._inner)}")

195

196

# Get specific version by timestamp

197

specific_version = store.get(threat_actor_v1.id, version=threat_actor_v2.modified)

198

print(f"Specific version: {specific_version.modified}")

199

```

200

201

### Version Comparison

202

203

Compare different versions to track changes.

204

205

```python

206

def compare_versions(obj_v1, obj_v2):

207

"""Compare two versions of the same STIX object."""

208

if obj_v1.id != obj_v2.id:

209

raise ValueError("Objects must have the same ID")

210

211

changes = {}

212

213

# Get all properties from both versions

214

all_props = set(obj_v1._inner.keys()) | set(obj_v2._inner.keys())

215

216

for prop in all_props:

217

v1_val = getattr(obj_v1, prop, None)

218

v2_val = getattr(obj_v2, prop, None)

219

220

if v1_val != v2_val:

221

changes[prop] = {

222

'old': v1_val,

223

'new': v2_val

224

}

225

226

return changes

227

228

# Compare versions

229

changes = compare_versions(threat_actor_v1, threat_actor_v3)

230

for prop, change in changes.items():

231

print(f"{prop}: {change['old']} -> {change['new']}")

232

233

# Output:

234

# modified: 2021-04-23T10:30:00.000Z -> 2021-04-23T11:45:00.000Z

235

# aliases: None -> ['APT-EX', 'Example Group']

236

# sophistication: None -> advanced

237

# last_seen: None -> 2021-12-31T23:59:59.000Z

238

# goals: None -> ['espionage', 'data-theft']

239

```

240

241

### Version Lifecycle Management

242

243

Manage the complete lifecycle of versioned objects.

244

245

```python

246

from stix2 import Bundle

247

248

class VersionManager:

249

"""Utility class for managing object versions."""

250

251

def __init__(self, store):

252

self.store = store

253

254

def create_object(self, cls, **kwargs):

255

"""Create new object and store it."""

256

obj = cls(**kwargs)

257

self.store.add(obj)

258

return obj

259

260

def update_object(self, obj_id, **updates):

261

"""Create new version of object with updates."""

262

current = self.store.get(obj_id)

263

if not current:

264

raise ValueError(f"Object {obj_id} not found")

265

266

if getattr(current, 'revoked', False):

267

raise RevokeError("Cannot update revoked object")

268

269

new_obj = new_version(current, **updates)

270

self.store.add(new_obj)

271

return new_obj

272

273

def revoke_object(self, obj_id, reason=None):

274

"""Revoke object and optionally add reason."""

275

current = self.store.get(obj_id)

276

if not current:

277

raise ValueError(f"Object {obj_id} not found")

278

279

revoked_obj = revoke(current)

280

if reason:

281

revoked_obj = new_version(revoked_obj,

282

external_references=[{

283

"source_name": "revocation-reason",

284

"description": reason

285

}]

286

)

287

288

self.store.add(revoked_obj)

289

return revoked_obj

290

291

def get_version_history(self, obj_id):

292

"""Get chronological version history."""

293

versions = self.store.all_versions(obj_id)

294

return sorted(versions, key=lambda x: x.modified)

295

296

def is_current_version(self, obj):

297

"""Check if object is the most current version."""

298

latest = self.store.get(obj.id)

299

return obj.modified == latest.modified

300

301

# Usage example

302

store = MemoryStore()

303

manager = VersionManager(store)

304

305

# Create initial object

306

indicator = manager.create_object(Indicator,

307

name="Initial Indicator",

308

indicator_types=["malicious-activity"],

309

pattern_type="stix",

310

pattern="[ip-addr:value = '192.168.1.1']",

311

confidence=30

312

)

313

314

# Update object multiple times

315

indicator_v2 = manager.update_object(indicator.id, confidence=60)

316

indicator_v3 = manager.update_object(indicator.id,

317

confidence=85,

318

description="Confirmed C2 server"

319

)

320

321

# Get version history

322

history = manager.get_version_history(indicator.id)

323

print(f"Version history: {len(history)} versions")

324

325

for i, version in enumerate(history, 1):

326

print(f"V{i}: {version.modified} - Confidence: {version.confidence}")

327

328

# Revoke object

329

revoked = manager.revoke_object(indicator.id,

330

reason="Indicator found to be false positive")

331

332

# Check current status

333

is_current = manager.is_current_version(indicator_v3)

334

print(f"V3 is current: {is_current}") # False, revoked version is current

335

```

336

337

### Versioning in Relationships

338

339

Handle versioning when objects are referenced in relationships.

340

341

```python

342

from stix2 import Relationship, ThreatActor, Malware

343

344

# Create related objects

345

actor = ThreatActor(

346

name="Initial Actor Name",

347

threat_actor_types=["nation-state"]

348

)

349

350

malware = Malware(

351

name="Banking Trojan",

352

malware_types=["trojan"]

353

)

354

355

# Create relationship

356

relationship = Relationship(

357

relationship_type="uses",

358

source_ref=actor.id,

359

target_ref=malware.id

360

)

361

362

# Store objects

363

store.add([actor, malware, relationship])

364

365

# Update referenced object

366

actor_v2 = new_version(actor,

367

name="Updated Actor Name",

368

aliases=["Actor Alias"]

369

)

370

store.add(actor_v2)

371

372

# Relationship still references same ID

373

retrieved_relationship = store.get(relationship.id)

374

referenced_actor = store.get(retrieved_relationship.source_ref)

375

376

print(f"Relationship references: {referenced_actor.name}") # "Updated Actor Name"

377

print(f"Actor version: {referenced_actor.modified}") # Latest version

378

379

# Create new relationship version if needed

380

relationship_v2 = new_version(relationship,

381

description="Updated relationship description"

382

)

383

store.add(relationship_v2)

384

```

385

386

### Bundle Versioning

387

388

Version entire bundles of related objects.

389

390

```python

391

from stix2 import Bundle

392

393

# Create bundle with multiple objects

394

bundle_v1 = Bundle(

395

indicator_v1,

396

malware,

397

relationship

398

)

399

400

# Create new bundle version with updated objects

401

bundle_v2 = new_version(bundle_v1,

402

objects=[indicator_v3, malware, relationship_v2] # Updated objects

403

)

404

405

print(f"Bundle V1: {len(bundle_v1.objects)} objects")

406

print(f"Bundle V2: {len(bundle_v2.objects)} objects")

407

print(f"Same bundle ID: {bundle_v1.id == bundle_v2.id}")

408

```

409

410

### Error Handling

411

412

Handle versioning errors appropriately.

413

414

```python

415

from stix2.exceptions import (

416

TypeNotVersionableError,

417

ObjectNotVersionableError,

418

UnmodifiablePropertyError,

419

RevokeError

420

)

421

422

def safe_version_update(obj, **updates):

423

"""Safely attempt to create new version with error handling."""

424

try:

425

return new_version(obj, **updates)

426

427

except TypeNotVersionableError:

428

print(f"Object type {obj.type} does not support versioning")

429

return None

430

431

except ObjectNotVersionableError:

432

print(f"Object lacks required properties for versioning")

433

return None

434

435

except UnmodifiablePropertyError as e:

436

print(f"Cannot modify properties: {e.unchangable_properties}")

437

# Filter out immutable properties and retry

438

valid_updates = {k: v for k, v in updates.items()

439

if k not in e.unchangable_properties}

440

if valid_updates:

441

return new_version(obj, **valid_updates)

442

return None

443

444

except RevokeError:

445

print("Cannot version revoked object")

446

return None

447

448

# Safe versioning attempt

449

updated_obj = safe_version_update(some_object,

450

name="New Name",

451

id="invalid-id-change", # This will be filtered out

452

confidence=75

453

)

454

```