Skip to content
Open

Sfi #2608

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
15,607 changes: 3,377 additions & 12,230 deletions package-lock.json

Large diffs are not rendered by default.

25 changes: 17 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
"vscode": "^1.39.0"
},
"dependencies": {
"@azure/ms-rest-js": "^1.5.0",
"@azure/ms-rest-js": "^2.7.0",
"applicationinsights": "^2.9.6",
"args": "^5.0.1",
"axios": "^0.27.0",
"axios": "^0.30.0",
Comment on lines 22 to +26

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package.json changes dependency versions and adds overrides, but this PR doesn’t include the corresponding package-lock.json update. Since the repo commits package-lock.json, please regenerate and commit the lockfile so CI and local installs resolve the same dependency graph.

Copilot uses AI. Check for mistakes.
"etag": "^1.8.1",
"express": "^4.16.4",
"fs-extra": "^11.1.1",
Expand All @@ -36,20 +36,21 @@
"rimraf": "^3.0.2",
"sequelize": "^6.31.0",
"stoppable": "^1.1.0",
"tedious": "^16.7.0",
"tedious": "^18.6.1",
"to-readable-stream": "^2.1.0",
"tslib": "^2.3.0",
"uri-templates": "^0.2.0",
"uuid": "^3.3.2",
"winston": "^3.1.0",
"xml2js": "^0.6.0"
"xml2js": "0.6.2"
Comment on lines 36 to +45

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tedious was bumped to ^18.x, but this major line requires newer Node runtimes (package metadata indicates node >=18), and it also pulls in newer Azure deps that require even higher Node versions. With engines.node still set to >=10.0.0, installs/runs on older Node versions will likely break. Please either bump engines.node (and related @types/node/tooling) accordingly or pin tedious (and other deps) to versions compatible with the supported Node range.

Copilot uses AI. Check for mistakes.
},
"devDependencies": {
"@azure/core-auth": "^1.3.2",
"@azure/core-rest-pipeline": "^1.2.0",
"@azure/data-tables": "^13.0.1",
"@azure/storage-blob": "^12.9.0",
"@azure/storage-queue": "^12.8.0",
"@azure/identity": "^4.2.1",
"@azure/storage-blob": "^12.28.0",
"@azure/storage-queue": "^12.27.0",
"@types/args": "^5.0.0",
"@types/async": "^3.0.1",
"@types/bluebird": "^3.5.27",
Expand All @@ -73,7 +74,7 @@
"@typescript-eslint/eslint-plugin": "^5.54.1",
"@typescript-eslint/parser": "^5.54.1",
"autorest": "^3.6.0",
"azure-storage": "^2.10.3",
"azure-storage": "^2.10.7",
"cross-env": "^7.0.3",
"cross-var": "^1.1.0",
"eslint": "^8.35.0",
Expand Down Expand Up @@ -330,6 +331,14 @@
"db:migrate:blob:metadata": "sequelize db:migrate --config migrations/blob/metadata/config/config.json --migrations-path migrations/blob/metadata/migrations",
"db:create:blob:metadata": "sequelize db:create --config migrations/blob/metadata/config/config.json --migrations-path migrations/blob/metadata/migrations"
},
"overrides": {
"vsce": { "xml2js": "^0.5.0" },
"azure-storage": {
"xml2js": "^0.5.0",
"form-data": "^2.5.4"
},
"tough-cookie": "^4.1.3"
},
"husky": {},
"repository": {
"type": "git",
Expand All @@ -350,4 +359,4 @@
"url": "https://github.com/azure/azurite/issues"
},
"homepage": "https://github.com/azure/azurite#readme"
}
}
13 changes: 6 additions & 7 deletions src/blob/authentication/BlobSharedKeyAuthenticator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class BlobSharedKeyAuthenticator implements IAuthenticator {
public constructor(
private readonly dataStore: IAccountDataStore,
private readonly logger: ILogger
) {}
) { }

public async validate(
req: IRequest,
Expand Down Expand Up @@ -77,8 +77,8 @@ export default class BlobSharedKeyAuthenticator implements IAuthenticator {
const stringToSign: string =
[
req.getMethod().toUpperCase(),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_ENCODING),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_LANGUAGE),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_ENCODING),
Comment on lines 80 to +81

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared-key string-to-sign header order looks incorrect: Content-Language is currently appended before Content-Encoding. For Azure Storage SharedKey auth, the canonical string should include Content-Encoding before Content-Language. Swapping these will cause signature mismatches whenever either header is present.

Suggested change
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_LANGUAGE),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_ENCODING),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_ENCODING),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_LANGUAGE),

Copilot uses AI. Check for mistakes.
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_LENGTH),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_MD5),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_TYPE),
Expand Down Expand Up @@ -137,10 +137,9 @@ export default class BlobSharedKeyAuthenticator implements IAuthenticator {
}
}

if (context.context.isSecondary && blobContext.authenticationPath?.indexOf(account) === 1)
{
// JS/.net Track2 SDK will generate stringToSign from IP style URI with "-secondary" in authenticationPath, so will also compare signature with this kind stringToSign
const stringToSign_secondary: string =
if (context.context.isSecondary && blobContext.authenticationPath?.indexOf(account) === 1) {
// JS/.net Track2 SDK will generate stringToSign from IP style URI with "-secondary" in authenticationPath, so will also compare signature with this kind stringToSign
const stringToSign_secondary: string =
[
req.getMethod().toUpperCase(),
this.getHeaderValueToSign(req, HeaderConstants.CONTENT_ENCODING),
Expand Down Expand Up @@ -171,7 +170,7 @@ export default class BlobSharedKeyAuthenticator implements IAuthenticator {
blobContext.contextId
);

const signature1_secondary= computeHMACSHA256(stringToSign_secondary, accountProperties.key1);
const signature1_secondary = computeHMACSHA256(stringToSign_secondary, accountProperties.key1);
const authValue1_secondary = `SharedKey ${account}:${signature1_secondary}`;
this.logger.info(
`BlobSharedKeyAuthenticator:validate() Calculated authentication header based on key1 and stringToSign with "-secondary": ${authValue1_secondary}`,
Expand Down
10 changes: 8 additions & 2 deletions src/blob/generated/ExpressRequestAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Request } from 'express';
import IRequest, { HttpMethod } from './IRequest';

export default class ExpressRequestAdapter implements IRequest {
public constructor(private readonly req: Request) {}
public constructor(private readonly req: Request) { }

public getMethod(): HttpMethod {
return this.req.method.toUpperCase() as HttpMethod;
Expand Down Expand Up @@ -48,7 +48,13 @@ export default class ExpressRequestAdapter implements IRequest {
}

public getQuery(key: string): string | undefined {
return this.req.query[key];
const queryValue = this.req.query[key];
if ((typeof queryValue) === "string") {
return queryValue;
}
else {
return undefined;
}
}

public getProtocol(): string {
Expand Down
1 change: 0 additions & 1 deletion src/blob/generated/artifacts/parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ export const blobSequenceNumber: msRest.OperationParameter = {
],
mapper: {
serializedName: "x-ms-blob-sequence-number",
defaultValue: 0,
type: {
name: "Number"
}
Expand Down
4 changes: 1 addition & 3 deletions src/blob/generated/utils/xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import * as xml2js from 'xml2js';

export function stringifyXML(obj: any, opts?: { rootName?: string }) {
const builder = new xml2js.Builder({
explicitArray: false,
explicitCharkey: false,
renderOpts: {
pretty: false
},
Expand All @@ -25,7 +23,7 @@ export function parseXML(
emptyTag: undefined
});
return new Promise((resolve, reject) => {
xmlParser.parseString(str, (err?: Error, res?: any) => {
xmlParser.parseString(str, (err?: Error | null, res?: any) => {
if (err) {
reject(err);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/blob/handlers/AppendBlobHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export default class AppendBlobHandler extends BaseHandler
requestId: context.contextId,
eTag: properties.etag,
lastModified: properties.lastModified,
contentMD5: contentMD5Buffer,
contentMD5: contentMD5Buffer ? new Uint8Array(contentMD5Buffer) : undefined,
xMsContentCrc64: undefined,
clientRequestId: options.requestId,
version: BLOB_API_VERSION,
Expand Down
8 changes: 7 additions & 1 deletion src/blob/handlers/BlobBatchHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,13 @@ export class BlobBatchHandler {
return;
}

buffer.fill(chunk, pos, pos + chunk.length);
// Efficiently copy chunk data without allocation
if (Buffer.isBuffer(chunk)) {
(chunk as any).copy(buffer, pos);
} else {
// For Uint8Array or other typed arrays
(buffer as any).set(chunk, pos);
}
pos += chunk.length;
});

Expand Down
4 changes: 2 additions & 2 deletions src/blob/handlers/BlobBatchSubResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ export class BlobBatchSubResponse implements IResponse {
return this.bodyStream;
}

public getBodyContent(): string{
public getBodyContent(): string {
return this.bodyStream.getBodyContent();
}

public end(): void{
public end(): void {
this.setStatusMessage(STATUS_CODES[this.statusCode!] || 'unknown');
}
}
12 changes: 6 additions & 6 deletions src/blob/handlers/SubResponseTextBodyStream.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Writable } from "stream";
import { BlobBatchSubResponse } from "./BlobBatchSubResponse";

export class SubResponseTextBodyStream extends Writable
{
export class SubResponseTextBodyStream extends Writable {
private bodyText: string;
public constructor(
private readonly subResponse: BlobBatchSubResponse) {
Expand All @@ -15,12 +14,13 @@ export class SubResponseTextBodyStream extends Writable
callback();
}

public end(cb?: (() => void) | undefined): void;
public end(chunk: any, cb?: (() => void) | undefined): void;
public end(chunk: any, encoding: BufferEncoding, cb?: (() => void) | undefined): void;
public end(chunk?: unknown, encoding?: unknown, cb?: unknown): void {
public end(cb?: (() => void) | undefined): this;
public end(chunk: any, cb?: (() => void) | undefined): this;
public end(chunk: any, encoding: BufferEncoding, cb?: (() => void) | undefined): this;
public end(chunk?: unknown, encoding?: unknown, cb?: unknown): this {
if (chunk) this.bodyText += (chunk! as any).toString();
Comment on lines +17 to 21

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SubResponseTextBodyStream.end() overrides Writable.end() but never calls super.end(...) and ignores the encoding/cb parameters. This can prevent the stream from reaching the finished state and can break callers that rely on the callback/finish event. Consider delegating to super.end(...) and triggering subResponse.end() in the completion path.

Copilot uses AI. Check for mistakes.
this.subResponse.end();
return this;
Comment on lines 21 to +23

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SubResponseTextBodyStream.end(...) overrides Writable.end but doesn't delegate to super.end(...) or invoke the optional callback. This can break expected stream semantics (e.g., finish events / callback-based completion) and may cause hangs if any caller relies on the callback. Consider calling super.end(chunk as any, encoding as any, cb as any) and only then notifying subResponse.end() (or wrapping it in the callback).

Suggested change
if (chunk) this.bodyText += (chunk! as any).toString();
this.subResponse.end();
return this;
let finalChunk = chunk;
let finalEncoding = encoding;
let finalCallback = cb as (() => void) | undefined;
if (typeof finalChunk === "function") {
finalCallback = finalChunk as () => void;
finalChunk = undefined;
finalEncoding = undefined;
} else if (typeof finalEncoding === "function") {
finalCallback = finalEncoding as () => void;
finalEncoding = undefined;
}
const wrappedCallback = () => {
this.subResponse.end();
if (finalCallback) {
finalCallback();
}
};
return super.end(finalChunk as any, finalEncoding as any, wrappedCallback as any);

Copilot uses AI. Check for mistakes.
}

public getBodyContent(): string {
Expand Down
5 changes: 3 additions & 2 deletions src/blob/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function shutdown(server: BlobServer | SqlBlobServer) {
});
}


/**
* Entry for Azurite blob service.
*/
Expand All @@ -45,9 +46,9 @@ async function main() {
console.log(
`Azurite Blob service successfully listens on ${server.getHttpServerAddress()}`
);

const location = await env.location();
AzuriteTelemetryClient.init(location, !env.disableTelemetry(), env);
AzuriteTelemetryClient.init(location, !env.disableTelemetry(), env);
await AzuriteTelemetryClient.TraceStartEvent("Blob");

// Handle close event
Expand Down
10 changes: 3 additions & 7 deletions src/blob/persistence/LokiBlobMetadataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3113,12 +3113,8 @@ export default class LokiBlobMetadataStore
return undefined;
}

if (obj instanceof Uint8Array) {
return obj;
}

if (obj.type === "Buffer") {
obj = obj.data;
if (obj instanceof Buffer) {
return new Uint8Array(obj);
}

const length = Object.keys(obj).length;
Comment on lines +3116 to 3120

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restoreUint8Array() no longer handles the common JSON shape produced when a Buffer is serialized ({ type: "Buffer", data: [...] }). In that case this method will treat the object keys (type, data) as bytes and return incorrect data. Please re-add handling for the { type: "Buffer", data } form (and ideally keep the fast-path for Uint8Array inputs).

Copilot uses AI. Check for mistakes.
Expand All @@ -3134,7 +3130,7 @@ export default class LokiBlobMetadataStore
arr[i] = obj[i];
}

return arr;
return new Uint8Array(arr);
}
Comment on lines 3116 to 3134

Copilot AI Apr 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restoreUint8Array() no longer handles the common JSON-serialized Buffer form ({ type: "Buffer", data: [...] }). If Loki persists or reloads buffers in that shape, this loop will throw because it expects numeric keys. Please restore support for {type:"Buffer", data} (and/or keep the previous Uint8Array fast-path) to avoid breaking existing persisted DBs.

Copilot uses AI. Check for mistakes.

/**
Expand Down
Loading
Loading