Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ generated: true
---
## S3AssetStorageStrategy

<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="155" packageName="@vendure/asset-server-plugin" />
<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="156" packageName="@vendure/asset-server-plugin" />

An [AssetStorageStrategy](/reference/typescript-api/assets/asset-storage-strategy#assetstoragestrategy) which uses [Amazon S3](https://aws.amazon.com/s3/) object storage service.
To us this strategy you must first have access to an AWS account.
Expand Down Expand Up @@ -86,7 +86,7 @@ class S3AssetStorageStrategy implements AssetStorageStrategy {
</div>
## S3Config

<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="19" packageName="@vendure/asset-server-plugin" />
<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="20" packageName="@vendure/asset-server-plugin" />

Configuration for connecting to AWS S3.

Expand Down Expand Up @@ -133,7 +133,7 @@ Using type `any` in order to avoid the need to include `aws-sdk` dependency in g
</div>
## configureS3AssetStorage

<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="119" packageName="@vendure/asset-server-plugin" />
<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="120" packageName="@vendure/asset-server-plugin" />

Returns a configured instance of the [S3AssetStorageStrategy](/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy#s3assetstoragestrategy) which can then be passed to the [AssetServerOptions](/reference/core-plugins/asset-server-plugin/asset-server-options#assetserveroptions)`storageStrategyFactory` property.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: "TelemetryService"
generated: true
---
<GenerationInfo sourceFile="packages/core/src/telemetry/telemetry.service.ts" sourceLine="41" packageName="@vendure/core" since="3.6.0" />
<GenerationInfo sourceFile="packages/core/src/telemetry/telemetry.service.ts" sourceLine="44" packageName="@vendure/core" since="3.6.0" />

The TelemetryService collects anonymous usage data on Vendure application startup
and sends it to the Vendure telemetry endpoint. This data helps the Vendure team
Expand Down
4 changes: 2 additions & 2 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3352,7 +3352,7 @@
"title": "TelemetryService",
"slug": "telemetry-service",
"file": "docs/reference/typescript-api/telemetry/telemetry-service.mdx",
"lastModified": "2026-04-20T16:08:16+02:00"
"lastModified": "2026-07-08T09:33:32Z"
},
{
"title": "WrappedMethodArgs",
Expand Down Expand Up @@ -3548,7 +3548,7 @@
"title": "S3AssetStorageStrategy",
"slug": "s3asset-storage-strategy",
"file": "docs/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy.mdx",
"lastModified": "2026-04-20T16:08:16+02:00"
"lastModified": "2026-07-08T09:33:32Z"
},
{
"title": "SharpAssetPreviewStrategy",
Expand Down
129 changes: 129 additions & 0 deletions packages/core/src/telemetry/collectors/config.collector.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -690,5 +690,134 @@ describe('ConfigCollector', () => {
expect(result.hasCustomFulfillmentProcess).toBe(false);
});
});

describe('API / security posture', () => {
beforeEach(() => {
mockConfigService.apiOptions = {
introspection: false,
adminApiPlayground: true,
shopApiPlayground: false,
adminApiDebug: false,
shopApiDebug: false,
trustProxy: true,
cors: { origin: '*', credentials: true },
} as any;
mockConfigService.authOptions = {
...mockConfigService.authOptions,
tokenMethod: ['cookie', 'bearer'],
requireVerification: false,
disableAuth: true,
superadminCredentials: { identifier: 'superadmin', password: 'superadmin' },
cookieOptions: { secure: true, sameSite: 'strict' },
};
mockConfigService.settingsStoreFields = {
dashboard: [{ name: 'a' }, { name: 'b' }],
theme: [{ name: 'c' }],
} as any;
});

it('reduces api/auth posture to booleans and short enums', () => {
const result = collector.collect();

expect(result.apiIntrospectionEnabled).toBe(false);
expect(result.apiPlaygroundEnabled).toBe(true);
expect(result.apiDebugEnabled).toBe(false);
expect(result.trustProxyEnabled).toBe(true);
expect(result.corsWildcardOrigin).toBe(true);
expect(result.tokenMethods).toEqual(['cookie', 'bearer']);
expect(result.requireVerification).toBe(false);
expect(result.authDisabled).toBe(true);
expect(result.cookieSecure).toBe(true);
expect(result.cookieSameSite).toBe('strict');
expect(result.settingsStoreFieldCount).toBe(3);
});

it('introspection defaults to enabled when not explicitly disabled', () => {
mockConfigService.apiOptions = {} as any;
expect(collector.collect().apiIntrospectionEnabled).toBe(true);
});

it('treats cors: true as a wildcard origin', () => {
mockConfigService.apiOptions = { cors: true } as any;
expect(collector.collect().corsWildcardOrigin).toBe(true);
});

it('reports corsWildcardOrigin false when cors is disabled', () => {
mockConfigService.apiOptions = { cors: false } as any;
expect(collector.collect().corsWildcardOrigin).toBe(false);
});

it('reports corsWildcardOrigin false for a specific origin', () => {
mockConfigService.apiOptions = {
cors: { origin: 'https://example.com' },
} as any;
expect(collector.collect().corsWildcardOrigin).toBe(false);
});

it('normalizes a single tokenMethod string to an array', () => {
mockConfigService.authOptions = { tokenMethod: 'bearer' } as any;
expect(collector.collect().tokenMethods).toEqual(['bearer']);
});

it('detects the default superadmin credentials', () => {
expect(collector.collect().defaultSuperadminCredentials).toBe(true);
});

it('returns false and never reads the actual values for changed credentials', () => {
mockConfigService.authOptions = {
...mockConfigService.authOptions,
superadminCredentials: { identifier: 'admin', password: 'hunter2' },
};
expect(collector.collect().defaultSuperadminCredentials).toBe(false);
});
});

describe('customizedStrategies', () => {
it('flags only single-strategy fields that differ from the default config', () => {
mockConfigService.assetOptions = {
// Differs from the default NoAssetStorageStrategy
assetStorageStrategy: { constructor: { name: 'S3AssetStorageStrategy' } },
// Matches the default
assetNamingStrategy: { constructor: { name: 'DefaultAssetNamingStrategy' } },
assetPreviewStrategy: { constructor: { name: 'NoAssetPreviewStrategy' } },
} as any;

const result = collector.collect();

expect(result.customizedStrategies).toContain('assetOptions.assetStorageStrategy');
expect(result.customizedStrategies).not.toContain('assetOptions.assetNamingStrategy');
expect(result.customizedStrategies).not.toContain('assetOptions.assetPreviewStrategy');
});

it('emits dotted paths, never strategy class names', () => {
mockConfigService.assetOptions = {
assetStorageStrategy: { constructor: { name: 'SecretInternalStorageStrategy' } },
} as any;

const paths = collector.collect().customizedStrategies ?? [];

expect(paths).toContain('assetOptions.assetStorageStrategy');
for (const path of paths) {
expect(path).not.toContain('SecretInternalStorageStrategy');
}
});

it('returns an empty array when no strategies are customized', () => {
// The base mock uses default-equivalent strategies for the fields it defines
mockConfigService.assetOptions = {
assetStorageStrategy: { constructor: { name: 'NoAssetStorageStrategy' } },
} as any;
mockConfigService.entityOptions = {
entityIdStrategy: { constructor: { name: 'AutoIncrementIdStrategy' } },
moneyStrategy: { constructor: { name: 'DefaultMoneyStrategy' } },
} as any;
mockConfigService.orderOptions = { orderSellerStrategy: undefined, process: [] } as any;
mockConfigService.taxOptions = {} as any;
mockConfigService.systemOptions = {} as any;
mockConfigService.jobQueueOptions = {} as any;

expect(collector.collect().customizedStrategies).toEqual([]);
});
});
});
});
Loading
Loading