Skip to content

Commit

Permalink
Merge pull request #22 from mitre-attack/21-incorrect-key-name-in-tax…
Browse files Browse the repository at this point in the history
…ii-21-envelope-resource-endpoints

fix: change envelope 'items' key to 'objects'
  • Loading branch information
seansica authored Oct 23, 2024
2 parents 4e95535 + 395334c commit d9ca03d
Show file tree
Hide file tree
Showing 13 changed files with 50 additions and 50 deletions.
8 changes: 4 additions & 4 deletions src/common/interceptors/set-taxii-date-headers.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class SetTaxiiDateHeadersInterceptor implements NestInterceptor {
// (i.e., an EnvelopeDto instance or a ManifestDto instance)
// The objects property will be either an envelope (Array<StixObjectPropertiesDto>) or a
// manifest (Array<ManifestRecordDto>)
if (data.items) {
if (data.objects) {
// ok cool - so we're about to send either an envelope or manifest to the user, which case we need
// to set the headers which indicates the date_added timestamp of the first object and the last
// object of the response.
Expand All @@ -69,7 +69,7 @@ export class SetTaxiiDateHeadersInterceptor implements NestInterceptor {
switch (this.type) {
case TaxiiDateFrom.ENVELOPE: {
// get the array of objects from the response body
const stixObjects: StixObjectPropertiesInterface[] = data.items;
const stixObjects: StixObjectPropertiesInterface[] = data.objects;
if (stixObjects.length >= 1) {
addedFirst = stixObjects[0].created;
addedLast = stixObjects[stixObjects.length - 1].created;
Expand All @@ -82,7 +82,7 @@ export class SetTaxiiDateHeadersInterceptor implements NestInterceptor {
break;
}
case TaxiiDateFrom.MANIFEST: {
const manifestRecords: ManifestRecordDto[] = data.items;
const manifestRecords: ManifestRecordDto[] = data.objects;
if (manifestRecords.length >= 1) {
addedFirst = manifestRecords[0].dateAdded;
addedLast =
Expand All @@ -97,7 +97,7 @@ export class SetTaxiiDateHeadersInterceptor implements NestInterceptor {
}
case TaxiiDateFrom.VERSIONS: {
// TODO confirm this works then delete TODO: complete this block after version endpoint is implemented
const versions: string[] = data.items;
const versions: string[] = data.objects;
if (versions.length >= 1) {
addedFirst = versions[0];
addedLast = versions[versions.length - 1];
Expand Down
2 changes: 1 addition & 1 deletion src/taxii/providers/discovery/dto/discovery-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class DiscoveryResource extends OmitType(DiscoveryDto, []) {

@ApiProperty({
description:
"The default API Root that a TAXII Client MAY use. Absence of this property indicates that there is no default API Root. The default API Root MUST be an item in api_roots.",
"The default API Root that a TAXII Client MAY use. Absence of this property indicates that there is no default API Root. The default API Root MUST be an object in api_roots.",
type: String,
required: false,
})
Expand Down
2 changes: 1 addition & 1 deletion src/taxii/providers/discovery/dto/discovery.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class DiscoveryDto {

/**
* @descr The default API Root that a TAXII Client MAY use. Absence of this property indicates that there
* is no default API Root. The default API Root MUST be an item in api_roots.
* is no default API Root. The default API Root MUST be an object in api_roots.
* @type string
* @required false
*/
Expand Down
6 changes: 3 additions & 3 deletions src/taxii/providers/envelope/dto/envelope-bundle.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class EnvelopeBundleDto
for (let i = 0; i < this.pages.length; i++) {
const curEnvelope: EnvelopeDto = this.pages[i];
const curOldestObjectInEnvelope =
curEnvelope.items[curEnvelope.items.length - 1].created;
curEnvelope.objects[curEnvelope.objects.length - 1].created;
if (new Date(curOldestObjectInEnvelope).toISOString() == addedAfter) {
/** If the date matches the supplied added_after query parameter, then try to serve the *next*
envelope after this one, because the user wants all objects after the specified added_after date **/
Expand Down Expand Up @@ -82,7 +82,7 @@ export class EnvelopeBundleDto
id: new IdentifierDto().toString(),
more: false,
next: undefined,
items: objects.slice(i, stop),
objects: objects.slice(i, stop),
});
if (this.pages.length > 0) {
// If this is not the first envelope in the bundle, update prev before pushing the new envelope
Expand All @@ -98,7 +98,7 @@ export class EnvelopeBundleDto
id: new IdentifierDto().toString(),
more: false,
next: undefined,
items: objects,
objects: objects,
});

// if this is not the first envelope in the bundle...
Expand Down
2 changes: 1 addition & 1 deletion src/taxii/providers/envelope/dto/envelope-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { StixObjectDto } from "src/stix/dto/stix-object.dto";
export class EnvelopeResource extends OmitType(EnvelopeDto, [
"id",
"next",
"items",
"objects",
]) {
@ApiProperty({
description:
Expand Down
4 changes: 2 additions & 2 deletions src/taxii/providers/envelope/dto/envelope.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface EnvelopeConstructorOptions
id?: string; // <-- INHERITED
more?: boolean; // <-- INHERITED
next?: string; // <-- INHERITED
items?: StixObjectPropertiesInterface[];
objects?: StixObjectPropertiesInterface[];
}

@Exclude()
Expand All @@ -37,7 +37,7 @@ export class EnvelopeDto
@IsOptional()
@Type(() => StixObjectDto)
@Expose({ name: "objects" })
items: StixObjectPropertiesInterface[];
objects: StixObjectPropertiesInterface[];

constructor(options: EnvelopeConstructorOptions) {
super(options);
Expand Down
4 changes: 2 additions & 2 deletions src/taxii/providers/manifest/dto/manifest-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ManifestDto } from "./manifest.dto";
import { ManifestRecordDto } from "./manifest-record.dto";
import { ManifestRecordResource } from "./manifest-record-resource";

export class ManifestResource extends OmitType(ManifestDto, ["id", "items"]) {
export class ManifestResource extends OmitType(ManifestDto, ["id", "objects"]) {
@ApiProperty({
description:
"This property identifies if there is more content available based on the search criteria. The absence of this property means the value is false.",
Expand All @@ -22,7 +22,7 @@ export class ManifestResource extends OmitType(ManifestDto, ["id", "items"]) {

@ApiProperty({
description:
"The list of manifest entries for objects returned by the request. If there are no manifest-record items in the list, this key MUST be omitted, and the response is an empty object.",
"The list of manifest entries for objects returned by the request. If there are no manifest-record objects in the list, this key MUST be omitted, and the response is an empty object.",
type: [ManifestRecordResource],
required: false,
})
Expand Down
4 changes: 2 additions & 2 deletions src/taxii/providers/manifest/dto/manifest.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface ManifestConstructorProperties
id?: string; // <-- INHERITED
more?: boolean; // <-- INHERITED
next?: string; // <-- INHERITED
items?: ManifestRecordDto[];
objects?: ManifestRecordDto[];
}

export class ManifestDto
Expand All @@ -35,7 +35,7 @@ export class ManifestDto
@IsOptional()
@Type(() => ManifestRecordDto)
@Expose({ name: "objects" })
items: ManifestRecordDto[];
objects: ManifestRecordDto[];

constructor(options: ManifestConstructorProperties) {
super(options);
Expand Down
12 changes: 6 additions & 6 deletions src/taxii/providers/pagination/dto/generic-page.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,33 @@ export interface GenericPageOptions<T> {
id?: string;
more?: boolean;
next?: string;
items?: T[];
objects?: T[];
}

export class GenericPageDto {
id: string;
more: boolean;
next: string;
items: any[];
objects: any[];

constructor(options: GenericPageOptions<any>) {
this.id = options.id ? options.id : new IdentifierDto().toString();
this.more = options.more ? options.more : false;
this.next = options.next ? options.next : undefined;
this.items = options.items ? options.items : [];
this.objects = options.objects ? options.objects : [];
}

/**
* toJSON allows the controller to remove the items/objects property from the object if there are no elements in
* toJSON allows the controller to remove the objects property from the object if there are no elements in
* the array. This is a requirement of the TAXII 2.1 specification.
*
* The specification states:
* Empty lists are prohibited in TAXII and MUST NOT be used as a substitute for omitting optional properties. If the
* property is required, the list MUST be present and MUST have at least one value.
*/
toJSON() {
if (this.items.length === 0) {
this.items = undefined;
if (this.objects.length === 0) {
this.objects = undefined;
}
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export class SinglePageInterface<T> {
readonly id: string;
readonly more: boolean;
readonly next: string;
readonly items: T[];
readonly objects: T[];
}
48 changes: 24 additions & 24 deletions src/taxii/providers/pagination/pagination.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ export class PaginationService {
* Handles pagination of STIX object, manifest-record, and version string arrays. This is where all pagination logic
* lives. The other class methods (which are public) call this method to process pages. They themselves just
* transform the generic response object (an instance of GenericPageDto) to their respective resource types.
* @param items A list of either: ManifestRecordDto (if being called by the ManifestService), string (if being
* @param objects A list of either: ManifestRecordDto (if being called by the ManifestService), string (if being
* called by the VersionService), StixObjectPropertiesInterface (if being called by the EnvelopeService)
* @param limit The number of objects that should be included on the page
* @param next Specifies which page is being requested
* @private
*/
private async getPage(
items: any[],
objects: any[],
limit?: number,
next?: number
): Promise<GenericPageDto> {
Expand All @@ -48,14 +48,14 @@ export class PaginationService {
this.constructor.name
);
/**
* The following `if` condition determines whether pagination is even possible for the supplied `items`,
* The following `if` condition determines whether pagination is even possible for the supplied `objects`,
* `limit`, and `next` combination. For example, it is possible for the user to request a page that does not
* exist.
*/
if (limit * next <= items.length) {
if (limit * next <= objects.length) {
/**
* Example:
* all_items = [a,b,c,d,e,f,g], length=7
* all_objects = [a,b,c,d,e,f,g], length=7
* limit=2, next=2
* resulting pages = {
* page-0: [a,b],
Expand All @@ -64,25 +64,25 @@ export class PaginationService {
* page-3: [g] <-- `isMore` determines if this page exists
* }
* We know there are additional pages after page-2 because:
* - nextPage + currentPage < totalItems
* - (limit) + (limit*next) < items.length
* - nextPage + currentPage < totalobjects
* - (limit) + (limit*next) < objects.length
* - (2) + (2*2) < 7
* - 6 < 7
*/
const isMore: boolean = limit + limit * next < items.length;
const isMore: boolean = limit + limit * next < objects.length;

return new GenericPageDto({
more: isMore,
next: isMore ? String(next + 1) : undefined,
items: items.slice(limit * next, limit + limit * next),
objects: objects.slice(limit * next, limit + limit * next),
});
}

/**
* The values for `next` and `limit` are invalid for the selected items array. Usually this happens as a result
* The values for `next` and `limit` are invalid for the selected objects array. Usually this happens as a result
* of the user requesting a page that does not exist.
*
* e.g., We have 10 items and want to paginate by 5 (limit=5). Therefore, there should be two pages. However,
* e.g., We have 10 objects and want to paginate by 5 (limit=5). Therefore, there should be two pages. However,
* the user requested page 3 (next=3), resulting in an out of bounds exception. In this case, we return a 400
* response.
*/
Expand All @@ -96,66 +96,66 @@ export class PaginationService {
// Paginating by `limit`. This will only ever resolve to the first page, since `next` is the mechanism by which
// we step through pages.

const isMore: boolean = limit < items.length;
const isMore: boolean = limit < objects.length;

// Return page 1
return new GenericPageDto({
more: isMore,
next: isMore ? "1" : undefined,
items: items.slice(0, limit),
objects: objects.slice(0, limit),
});
}
}

this.logger.debug(
"Pagination bypassed. Returning page containing all available items",
"Pagination bypassed. Returning page containing all available objects",
this.constructor.name
);
return new GenericPageDto({
more: false,
items: items,
objects: objects,
});
}

/**
* Paginates an array of STIX objects
* @param items The array of STIX objects that should be paginated
* @param objects The array of STIX objects that should be paginated
* @param limit The number of objects that should be included on the page
* @param next Specifies which page is being requested
*/
async getEnvelope(
items: StixObjectPropertiesInterface[],
objects: StixObjectPropertiesInterface[],
limit?: number,
next?: number
): Promise<EnvelopeDto> {
return new EnvelopeDto(await this.getPage(items, limit, next));
return new EnvelopeDto(await this.getPage(objects, limit, next));
}

/**
* Paginates an array of manifest records
* @param items The array of manifest records that should be paginated
* @param objects The array of manifest records that should be paginated
* @param limit The number of manifest records that should be included on the page
* @param next Specifies which page is being requested
*/
async getManifest(
items: ManifestRecordDto[],
objects: ManifestRecordDto[],
limit?: number,
next?: number
): Promise<ManifestDto> {
return new ManifestDto(await this.getPage(items, limit, next));
return new ManifestDto(await this.getPage(objects, limit, next));
}

/**
* Paginates an array of object versions
* @param items The array of object versions (which are just strings) that should be paginated
* @param objects The array of object versions (which are just strings) that should be paginated
* @param limit The number of versions that should be included on the page
* @param next Specifies which page is being requested
*/
async getVersion(
items: string[],
objects: string[],
limit?: number,
next?: number
): Promise<VersionDto> {
return new VersionDto(await this.getPage(items, limit, next));
return new VersionDto(await this.getPage(objects, limit, next));
}
}
4 changes: 2 additions & 2 deletions src/taxii/providers/version/dto/version.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface VersionConstructorOptions extends GenericPageOptions<string> {
id?: string; // <-- INHERITED
more?: boolean; // <-- INHERITED
next?: string; // <-- INHERITED
items?: string[];
objects?: string[];
}

export class VersionDto
Expand All @@ -33,7 +33,7 @@ export class VersionDto
@IsOptional()
@Type(() => String)
@Expose({ name: "versions" })
items: string[];
objects: string[];

constructor(options: VersionConstructorOptions) {
super(options);
Expand Down
2 changes: 1 addition & 1 deletion src/taxii/providers/version/dto/versions-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { VersionDto } from "src/taxii/providers/version/dto/version.dto";
export class VersionsResource extends OmitType(VersionDto, [
"id",
"next",
"items",
"objects",
]) {
@ApiProperty({
description:
Expand Down

0 comments on commit d9ca03d

Please sign in to comment.