dotnet

Activity

Stars and mentions · Last 30 days, Oct 6 – Nov 4

GitHub stars949
Mentions121
014284256Oct 6Oct 20Nov 4
UTC days · repositories dotnet owns56 repositories

Top repositories

dotnet’s repositories ranked by new forks, as on trending.

Loading

Contributes to

Repositories outside dotnet’s account where dotnet opened or reviewed pull requests in the past 365 days.

Who noticed dotnet

Everyone who starred or forked dotnet’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

119posts
Nov 52 posts
  1. 【.NET 11】SafeFileHandleの新規APIでハマった点について はじめに .NET 11 RC2やRC3などがリリースされて、もうすぐ.NET 11の正式リリースが行われる時期になりました。 そしてふと 改めて.NET 11 の新規 API を眺めていると、低レベル I/Oの機能が色々入っています。その中でハンドル関連のAPIについて、色々はまった点も含めて調べた内容に記載しておきます。 learn.microsoft.com 確認環境 .NET 11 RC3 ランタイムで確認しています。 バージョン > dotnet --version 11.0.100-rc.3.26522.148 匿名パイプの作成 SafeFileHandle.CreateAnonymousPipe 書き込みと読み込みが接続された匿名パイプとしてSafeFileHandleを作成することができるSafeFileHandle.CreateAnonymousPipeが追加されます。 // 書き込みと読み込みが接続された匿名パイプを作成する。 SafeFileHandle.CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle); ! SafeFileHandle.CreateAnonymousPipeで作成されるパイプについて、引数であるasyncReadとasyncWriteの値によって内部で作成されるパイプが変わります。1 両方falseつまり同期だと、WindowsではCreatePipe関数による匿名パイプ、それ以外つまり非同期だと、CreateNamedPipeA関数による名前付きパイプが内部では作成されます。 これは、CreatePipe関数の匿名パイプでは非同期IOがサポートされていないためです。23 そして、.NET 11からProcessStartInfoクラスに標準出力/出力/エラーをSafeFileHandleで設定できるStandardOutputHandle/StandardErrorHandle/StandardInputHandleプロパティが追加されています。 これを組み合わせることで、ls | grep XXXのようなパイプラインをSafeFileHandleを用いて直接実装することができます。.NET 10やそれ以前でも実装自体はもちろんできますが、より低レベルなAPIを使うなどちょっと面倒でした。(もしかすると簡単な方法があったかもしれませんが...。) .NET 11からProcessクラスなどでも色々と(待望の!)非同期APIが入ったりなどしているので、どんなものが入るのかは、以下の公式ブログなどを読むことをお勧めします。 devblogs.microsoft.com 公式ブログでも記載されているls | grep XXXの出力結果をファイルに出力するサンプルコードがあるので、これをベースにWindows用にdir | findstr csの出力結果を文字列として取得するサンプルコードが以下になります。 SafeFileHandle.CreateAnonymousPipeのサンプルコード // ここは好きなフォルダ/ディレクトリで問題ない。 string projectDirectory = typeof(Program).Assembly .GetCustomAttributes<AssemblyMetadataAttribute>() .FirstOrDefault(a => a.Key == "ProjectDirectory")?.Value ?? Environment.CurrentDirectory; // 書き込みと読み込みが接続された匿名パイプを作成する。 SafeFileHandle.CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle); // dir | findstr cs のパイプラインを再現する。 Process producer, consumer; // SafeFileHandle.CreateAnonymousPipeで作成したパイプはここで破棄する(重要) using (readHandle) using (writeHandle) { producer = Process.Start(new ProcessStartInfo("cmd", "/c dir") { WorkingDirectory = projectDirectory, StandardOutputHandle = writeHandle, StandardErrorHandle = writeHandle, })!; consumer = Process.Start(new ProcessStartInfo("findstr", "cs") { StandardInputHandle = readHandle, RedirectStandardOutput = true, RedirectStandardError = true, })!; } using (producer) using (consumer) { var (standardOutput, standardError) = await consumer.ReadAllTextAsync(); await Task.WhenAll(producer.WaitForExitAsync(), consumer.WaitForExitAsync()); Console.Write(standardOutput); // 標準出力結果 Console.Write(standardError); // 標準エラー出力結果 } サンプル結果例(実行環境で変わります) 2026/11/03 火 14:13 691 ConsoleApp.csproj 2026/11/04 水 21:02 6,360 Program.cs ここでは、実行プロジェクトのファイル名でcsが含まれているものを抽出する処理を実行しています。 サンプルコードを見てもらって分かる通り、SafeFileHandle.CreateAnonymousPipeで作成した匿名パイプであるSafeFileHandleを各ProcessStartInfoのStandardOutputHandle/StandardErrorHandle/StandardInputHandleプロパティに設定することで、C#/.NET上でパイプ的なことができるようになりました。 ただ、注意事項としてSafeFileHandle.CreateAnonymousPipeで作成したSafeFileHandleの破棄をちゃんとしないとデッドロックを引き起こすという点です。 最初、私は動作確認として以下のようなソースコードを実装していましたが、var (standardOutput, standardError) = await consumer.ReadAllTextAsync();の部分でデッドロックして、少し悩みました。 失敗例 SafeFileHandle.CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle); using (readHandle) using (writeHandle) { producer = Process.Start(new ProcessStartInfo("cmd", "/c dir") { WorkingDirectory = projectDirectory, StandardOutputHandle = writeHandle, StandardErrorHandle = writeHandle, })!; consumer = Process.Start(new ProcessStartInfo("findstr", "cs") { StandardInputHandle = readHandle, RedirectStandardOutput = true, RedirectStandardError = true, })!; // ここで止まる...なぜ? var (standardOutput, standardError) = await consumer.ReadAllTextAsync(); await Task.WhenAll(producer.WaitForExitAsync(), consumer.WaitForExitAsync()); Console.Write(standardOutput); // 標準出力結果 Console.Write(standardError); // 標準エラー出力結果 } そして公式ブログのサンプルコードをちゃんと見ると以下のコメントがありました。 The producer process has its own copy of the write end of the pipe, we need to dispose the parent copy producer プロセスはパイプの書き込み側の自分用のコピーを持っているので、親側のコピーは破棄する必要があります (この部分はOpus 5.5による翻訳です) ということで、破棄する必要があるのかと思い、最初に提示したコードに変更したところ、無事実行できました。公式ブログのサンプルコードもちゃんと見るとusing内でDisposeしています(普通に見逃していました。) 実際の原因としては、今回のコードでいうと、SafeFileHandle.CreateAnonymousPipeで作成した匿名パイプのハンドルのコピーをProcess.StandardOutputHandle経由でProcess.Startで起動するproducerの子プロセスへ受け渡しています。この時点で親のwriteHandleのハンドルと子プロセスのハンドルが二つ開いた状態になっています。 そして、consumerのプロセスは、producerの子プロセスの出力が終了するまで待っていますが、匿名パイプの仕様として書き込み側のハンドルが全て閉じないとEOFと判断されないため、consumerは入力を待ち続ける動作(デッドロック)が発生していました。 ! ハンドルが破棄がされていないのが原因なので、例えば最初のサンプルコードからusing (writeHandle)をコメントアウトしても、同じように再現します。 ということでSafeFileHandle.CreateAnonymousPipeの動作や実際はまった点についての紹介でした。この辺りの匿名パイプの仕様は昔一度調べていたはずなので、すっかり忘れていたのでいい勉強になりました笑。 終わりに 今回は触れていませんが、SafeFileHandleにはこれ以外にも新規APIが増えたりして、やっとProcessクラス以外の選択肢も出来たと思います。互換性の問題だとは思いますが、引数にProcessStartInfoを用いるのはちょっと納得がいっていないですが では今日のところはこのへんで。 参考URL learn.microsoft.com 脚注 dotnet/runtime SafeFileHandle.Windows.cs - github.com/dotnet/runtime/blob/v11.0.0-rc.3.26522… ↩︎ CreatePipe 関数 learn.microsoft.com ↩︎ CreateNamedPipeA 関数 - learn.microsoft.com ↩︎

  2. Privacy Boundaries for AI Telemetry Turning off prompt capture is not enough to enforce a complete telemetry recording policy. Exceptions can still include customer text in spans. If an investigation is limited to ten samples, it can end up with forty if four replicas each track their own counters. Queued samples can exceed their intended retention if the destination resets the expiry upon arrival. The recording-policy tip establishes what to allow. This article focuses on enforcement: inspect the actual SDK output, authorize capture with a narrow scope, reserve quota across replicas, and carry an absolute expiry through every copy of evidence. The example is a support assistant that reads a private ticket, fetches a refund policy, calls an eligibility tool, and drafts a reply. An investigation requires the policy excerpt and the generated reply, but should not collect the ticket or account-tool payload. Verify what the SDK actually records Keep ordinary message capture explicitly disabled. This composition fragment assumes an existing provider client and uses Microsoft.Extensions.AI 10.10.0: using Microsoft.Extensions.AI; IChatClient client = new ChatClientBuilder(providerClient) .UseOpenTelemetry( sourceName: "SupportReply.AI", configure: telemetry => telemetry.EnableSensitiveData = false) .Build(); Setting this option explicitly overrides OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT for this wrapper. This is not a telemetry allowlist. Reviewing the 10.10.0 source shows there are additional paths: | Recorded value | Behavior with sensitive capture disabled | | --- | --- | | Error status description | The error helper passes the exception message to the activity status. | | Exception log | When a logger is configured and warnings are enabled, the helper passes the exception to ILogger, independently of span export. | | Conversation identifier | The wrapper records ChatOptions.ConversationId when supplied and the activity requests data. | | Tool definitions | It serializes tool definitions with optional properties excluded. Tool identities still need review. | For example, if a provider exception includes a synthetic customer marker, that marker remains in Activity.StatusDescription even if raw message capture is disabled. The conversation identifier can also link a trace to a private application record. For a strict routine destination, use application-owned instrumentation built from approved values. Do not connect its tracer provider to the wrapper’s source until the source’s output complies with policy. Review the configured logger and its providers separately. Excluding the activity source does not prevent exception logging. Catching exceptions outside the wrapper does not remove a completed span or an exception already passed to a logger. Treat any allowed sanitization as specific to the exact fields and signal paths it covers. Removing message attributes alone does not address status descriptions, events, resource attributes, log bodies, and other instrumentation. Collector redaction is an extra safeguard, but it cannot reverse data already sent to an exporter or persistent queue. These are observations specific to this version, not a guarantee for all SDK releases. Repeat inspections and marker checks with every upgrade. Keep routine evidence useful and bounded An operator investigating an ineligible refund often starts with operational evidence: | Investigation question | Approved routine evidence | | --- | --- | | Which behavior was deployed? | Build identity, prompt version, configured model alias, policy version | | Did retrieval return context? | Retrieval outcome, result count, context size | | Did the eligibility tool complete? | Registered tool name, duration, controlled outcome code | | What happened to the reply? | Completion outcome, token usage when available, application validation result | A result count alone does not establish relevance or provide sufficient evidence. To determine if the model saw a conflicting refund clause, you may need separately authorized access to content. Constrain not only field names but also the values. Use controlled failure codes. Exception messages, document titles, URLs, and log scopes can leak customer details or secrets. Keep record lookups behind authorized application access. Opaque IDs and hashes still allow correlation and should not be used in high-cardinality metric dimensions. Review OpenTelemetry Baggage separately, since propagation can expose it to downstream services via HTTP headers. Exclude customer content and credentials, treat incoming baggage as untrusted, and configure propagation at external boundaries. Make diagnostic capture require a versioned grant Keep ordinary capture disabled during an incident. Use a dedicated capture service to select permitted fields and write only to a restricted evidence store. A global content flag would gather unrelated conversations as well. An authorized service owner can issue a grant with the following limits. These values are illustrative, not SDK defaults or universal limits: | Property | Authorized decision | | --- | --- | | Purpose and scope | Refund-policy investigation for one tenant, the support-reply operation type, and an explicit set of operation IDs | | Fixed capture schema | Policy excerpt and generated reply. Exclude the private ticket and account-tool payload | | Collection window | Thirty minutes. Revoke earlier if necessary | | Shared quota | Ten reservations across all replicas, with each payload at most 16 KiB after redaction and UTF-8 serialization | | Destination and readers | Restricted evidence store for assigned investigators | | Evidence lifetime | Twenty-four hours from collection, independently of the collection window | An operation ID comes from trusted application context. It is not a model argument or an investigation header used for authorization. If the incident is linked to a conversation, resolve authorized operations on the server and list those IDs. A broad tenant-wide capture requires separate justification. This application supports a single fixed diagnostic schema: policy excerpt and generated reply. A grant authorizes only this schema and cannot select arbitrary fields. Adding or changing fields requires a separately reviewed schema and capture path with new grants. Assign each grant a version. Changes to scope, collection deadline, evidence lifetime, or byte limit invalidate old authorizations at reservation time. The capture writer cannot expand a grant or read evidence. Investigator access is a separate role. Put selection, reservation, and expiry on one path The following C# code shows the enforcement boundary inside a capture service. The application-specific collaborators described below are not features provided by OpenTelemetry: public sealed record CaptureGrant( Guid Id, int Version, string TenantId, string OperationType, IReadOnlySet<Guid> OperationIds, DateTimeOffset CollectUntilUtc, TimeSpan EvidenceTtl, int MaxUtf8Bytes); public async Task TryCaptureAsync( CaptureGrant grant, TrustedOperation operation, CancellationToken ct) { if (grant.TenantId != operation.TenantId || grant.OperationType != operation.Type || !grant.OperationIds.Contains(operation.Id)) return; using var budget = CancellationTokenSource.CreateLinkedTokenSource(ct); budget.CancelAfter(TimeSpan.FromSeconds(2)); var slot = await reservations.TryReserveAsync(grant, operation, budget.Token); if (slot is null) return; // This type exposes only the two fields authorized by this design. var selected = new DiagnosticFields(operation.PolicyExcerpt, operation.Reply); byte[]? safePayload = redactor.TrySerializeUtf8( selected, grant.MaxUtf8Bytes, budget.Token); if (safePayload is null || safePayload.Length > grant.MaxUtf8Bytes) return; var now = clock.GetUtcNow(); var expiresAt = slot.CollectedAtUtc + grant.EvidenceTtl; if (now >= slot.CollectUntilUtc || now >= expiresAt) return; var sample = new EvidenceSample(slot.SampleId, grant.Id, grant.Version, operation.Id, slot.CollectedAtUtc, expiresAt, safePayload); await evidence.WriteIfFreshAsync(sample, budget.Token); } grant is an immutable, server-authorized snapshot loaded after verifying the investigation purpose and the requesting identity. TrustedOperation is provided by the application. The first guard checks for context mismatches before selecting content. reservations revalidates the current grant, including its version, scope, revocation status, deadline, and shared quota. It returns a unique sample ID and database timestamp only after committing the reservation. CollectedAtUtc records the time the reservation was admitted, before payload selection and redaction, ensuring that processing or transport delays do not extend retention. redactor serializes only DiagnosticFields, respects the byte and cancellation budgets, removes required recognized secrets, and returns no payload if it cannot verify removal. This does not guarantee that arbitrary text is non-sensitive, so the destination must be authorized for any remaining content. Do not serialize the entire operation and then try to remove private fields afterward. evidence accepts the same sample ID idempotently and checks the absolute expiry again when admitting the sample. The fragment omits exception handling. Its calling capture boundary must catch operational exceptions from these collaborators, including capture timeouts, without failing the user request. On failed redaction or an operational failure, skip capture and record only a controlled outcome such as capture_skipped. Do not log the payload or exception text as a fallback. The example’s two-second budget requires all collaborators to honor cancellation and keep their work bounded. Reserve quota atomically across replicas A separate read of the remaining quota followed by an unconditional write races. In PostgreSQL, use a short transaction: acquire a lock on the grant row, check the deadline, and then perform a conditional increment: BEGIN; SELECT id FROM capture_grants WHERE id = @grant_id FOR UPDATE; WITH admission AS MATERIALIZED ( SELECT clock_timestamp() AS collected_at_utc ) UPDATE capture_grants SET samples_used = samples_used + 1 FROM admission WHERE id = @grant_id AND version = @version AND tenant_id = @tenant_id AND operation_type = @operation_type AND @operation_id = ANY(operation_ids) AND revoked_at IS NULL AND collect_until_utc > admission.collected_at_utc AND samples_used < max_samples RETURNING admission.collected_at_utc, collect_until_utc; COMMIT; Here, id is the primary key, operation_ids is a UUID array, and timestamps use timestamptz. The @ names are driver parameters. Each replica locks the same grant row. Evaluate database time after acquiring the lock so the deadline check uses the current time rather than a timestamp from before the wait. The materialized CTE provides a single collection timestamp for both the expiry check and the returned receipt. A successful committed increment uses up one reservation. If no row is returned, the capture is denied. Apply grant changes and revocations through the same authority, incrementing the version when the scope or lifetime changes. Include lock waits in the capture budget. Using database time prevents replica clocks from making independent decisions about when reservations expire. Reservations are not refunded after failed redaction or storage. This approach can result in fewer than ten samples but will never authorize more than ten captures. An ambiguous reservation response may mean capacity was consumed. Do not reset the counter when retrying. Reuse a sample ID for evidence write retries, keeping the original timestamps. The reservation is the authorization point. Revocation stops new reservations but does not retroactively cancel any that have already been issued. The producer must complete its work within the short budget and the collection deadline. If you need to immediately revoke in-flight capture, the evidence-admission service must check the current grant state before committing. Carry absolute expiry through queues and copies Every sample needs explicit lifecycle fields. The sample created above has this shape: public sealed record EvidenceSample( Guid SampleId, Guid GrantId, int GrantVersion, Guid OperationId, DateTimeOffset CollectedAtUtc, DateTimeOffset ExpiresAtUtc, byte[] Payload); GrantId and GrantVersion identify the authorization used, even if the live grant changes or is revoked later. Preserve that authorized version for audit purposes. Suppose reservation admission happens at 10:00 UTC and the evidence lifetime is twenty-four hours. ExpiresAtUtc will be 10:00 UTC the next day. If a queue delivers the redacted payload at 09:50 the next morning, the destination has ten minutes left, not another full day. Any arrival after 10:00 must be rejected. The destination should compute any storage TTL based on the original expiry minus the current time. Apply the absolute deadline on reads, since a scheduled deletion sweep may run later. Clock synchronization and destination-side time checks are still needed. Producer-side checks alone cannot prevent delayed arrivals. Stopping collection after thirty minutes does not delete accepted samples, and deleting from the active store does not remove all exported copies. Maintain a copy register with concrete enforcement and clear ownership: | Copy | Required control | Owner | | --- | --- | --- | | Exporter buffers, configured Collector queues, routine backend | Approved fields before enqueue, reviewed retention and access at each destination | Service and telemetry-platform owners | | Restricted sample and any sending queue | Original sample ID and absolute expiry, no expired admission or investigator reads, deletion mechanism | Evidence-service owner | | Download, alert attachment, incident document | Authorized destination, readers, expiry, and tracked deletion or approved preservation | Named investigation owner | | Production-derived evaluation case and outputs | Separate reuse authorization and lifecycle with traceable origin | Dataset owner | A dashboard role is insufficient if a routine operator can still access evidence through an API or export permission. Also verify service identities and administrator access. Placing data in a separate table only restricts access if the backend enforces it. If a dashboard forwards content to an AI diagnosis service, include that destination in the register. Backups, replicas, and preservation requirements need a defined lifecycle outside the active-store sweep. If an unexpected copy appears, contain the recording path and involve the responsible incident team in its resolution. Disabling the source flag does not dispose of existing evidence. Treat reuse for evaluation as a new authorization A production failure can become a regression case, but its investigation grant does not authorize keeping the dataset indefinitely. Prefer using a synthetic case that preserves the failure mechanism without including the original ticket. If real content is needed, separately approve its new purpose, readers, destination, and expiry. Maintain lineage to the source sample and record the new authorization. Copying into CI or a golden dataset must not silently remove the original deadline. A judge model is another possible destination. Its explanations and reviewer comments may quote the input and require content controls. A numeric score with approved labels can go into routine telemetry, but explanations must remain restricted. Versioning a dataset does not extend retention permission. Verify failure paths before enabling real capture Use distinct synthetic markers in messages, policy excerpts, tool payloads, conversation IDs, tool names, and provider exceptions. Include a fictional email address and a fake credential. Inspect spans, status descriptions, events, logs, queues, backend queries, and exports at their destinations. | Exercise | Required observation | | --- | --- | | Provider exception with content capture disabled | Raw wrapper status and a configured logger can receive the marker. Test and control each path independently before export. | | Environment flag accidentally enabled | Explicit wrapper setting still excludes message capture. Conversation IDs, tool identities, other instrumentation and logs are checked separately. | | Wrong tenant, operation ID or type, stale version, revoked or expired grant | No reservation or content write. | | Many replicas compete for ten remaining slots | At most ten committed reservations across the whole deployment. | | Redaction fails, payload is oversized, or evidence storage is unavailable | No evidence sample and no fallback containing raw text. | | Evidence-write retry after a lost acknowledgement | Same sample ID, grant version, and original expiry, with no new retention clock. | | Reservation commits but its acknowledgement is lost | Capacity stays consumed. No sample is written without a confirmed reservation receipt. | | Queue arrival or read after absolute expiry | Admission or read denied, even before the deletion sweep runs. | | Routine operator uses the query API or export route | Investigation content remains inaccessible. | Marker tests detect known paths, not every possible private sentence. Repeat these tests after any SDK, exporter, Collector, or evaluation changes. For a real incident, also test the copy register and deletion process. A successful source-side unit test does not confirm backend access or erasure. When to use this approach Use this approach when production debugging requires selected user or business content across multiple service instances. Start with a single feature, a versioned grant, a shared reservation authority, and a restricted store that enforces absolute expiry. The existing observability walkthrough covers instrumentation setup, and trust-boundary guidance provides the broader authorization model. When not to use this approach Experiments with only synthetic data may need simpler controls. Mandatory audit evidence and investigations that require unredacted records need a separately designed evidence process with the necessary availability, preservation, and retention guarantees. This best-effort diagnostic path is a single application control, not a complete privacy or governance program. Related reading - Define what AI telemetry may contain before recording content - Observability for Agents with OpenTelemetry, Aspire, and Application Insights - Trust Boundaries Around AI Features Sources - Microsoft Learn: EnableSensitiveData defaults and environment-variable precedence - .NET Extensions source: OpenTelemetryChatClient at the 10.10.0 package revision - .NET Extensions source: Error status and exception recording at the same revision - OpenTelemetry: Handling sensitive data - OpenTelemetry: Baggage security considerations - OpenTelemetry Collector Contrib: Redaction processor behavior - PostgreSQL 18: Read Committed isolation and concurrent updates - PostgreSQL 18: UPDATE and RETURNING - PostgreSQL 18: Current-time functions and clock_timestamp

  3. Ran an experiment that ran for a day that resulted in ~90 PRs to improve the performance of asp.net Core: aspnetcore#70219 Fun facts: - These were using Github's new cloud agents support that shipped this week at GHU. - Opus 5.5 is very good - ~$230 in tokens. #dotnet #aspnetcore

    111 likes · 10 reposts · 8,526 views
  4. 【CVE-2026-111497】Brotliの脆弱性(Broccoli2Shell) 概要 Googleが開発するデータ圧縮ライブラリ「Brotli」において、細工された圧縮ストリームを展開(デコード)する際に境界外書き込みが発生する脆弱性が公表された。未認証の遠隔の攻撃者から悪意あるBrotli圧縮ストリームを処理させられることで、隣接するデコーダー状態メモリの破壊、機密メモリ内容の漏洩、サービス拒否(DoS)、または任意のコード実行(RCE)に至る可能性がある1。 項目 内容 CVE ID CVE-2026-111497 対象ソフトウェア Google Brotli(C言語デコーダー実装 c/dec/decode.c) CVSSスコア ・9.2 Critical (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N — Google CNA / GitHub) ・9.8 Critical (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — NVD) ・8.1 High / Important (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — Microsoft / Red Hat / AWS) 脆弱性種別 CWE-252: 戻り値の未チェック(Unchecked Return Value)、CWE-787: 境界外書き込み(Out-of-bounds Write) 発見・報告者 Khai Tran 氏、Sylvie Mayer 氏(OpenAI Codex Security) 悪用・KEV登録状況 Imperva社がNode.js 24.22.0(Linux arm64公式イメージ)環境でのRCE実証を報告。2026年10月27日付で米国CISA KEVに追加(対策期限: 2026年11月10日) 本脆弱性は、発見から波及範囲の判明に至る経緯の中で以下の3つの通称で呼ばれている。 Node2Shell: 2026年10月21日のNode.jsセキュリティリリース公表後、Imperva Red TeamがNode.js組み込みBrotliデコーダーにおける解析およびRCE実証に命名した呼称である4。 HTTP2Shell: 当初はNode.jsに関連して「Node2Shell」と呼称されていたが、根本原因がHTTP通信(Content-Encoding: br)等で広く利用されるBrotliのC言語実装にあり、複数製品が影響を受けることから「HTTP2Shell」とも呼称されている(HTTP/2プロトコル自体の脆弱性ではない)5。 Broccoli / Broccoli2Shell: セキュリティ研究者のJuliano Rizzo氏が「Node2Shellという名称は影響範囲を狭く見せてしまう」として「Broccoli」と呼ぶことを提唱したことに端を発し、追跡サイト「broccoli2shell.com」等で用いられている呼称である6。 影響を受けるバージョンと修正バージョン 1. Brotli の影響範囲と修正状況 Brotliの公式C実装において、バージョン 1.0.3 から 1.2.0 までのデコーダーが影響を受ける。各系統の修正バージョンは以下の通りである1。 系統 / ブランチ 影響を受けるバージョン 修正版 補足 1.0 系 >= 1.0.3, < 1.0.10 1.0.10 コミット 50a9b70a(0ed4268a のバックポート) 1.1 系 >= 1.1.0, < 1.1.1 1.1.1 コミット da980636(0ed4268a のバックポート) 1.2 系 >= 1.2.0, < 1.2.1 1.2.1 コミット b418fa81 / 0ed4268a 2. 主要ランタイム・言語パッケージ・アプライアンスの対応状況 BrotliのC言語デコーダーをバンドルまたはバインディングとして組み込んでいる各ランタイム・パッケージおよびネットワーク製品の対応状況は以下の通りである。 ランタイム / 製品 影響を受けるバージョン 修正版 補足 Node.js (v24 LTS) 24.x(< 24.22.1) v24.22.1 / v24.22.2 3 v24.22.1 で修正(v24.22.2 はリグレッション修正版) Node.js (v22 LTS) 22.x(< 22.23.4) v22.23.4 組み込みBrotliの更新(2026年10月セキュリティリリース) Node.js (v26 Current) 26.x(< 26.12.1) v26.12.1 組み込みBrotliの更新(EOL系統の <= 20.x, 23.x, 25.x も影響対象) Bun >= 1.0.21, < 1.4.4 1.4.4 8 バンドルするBrotliを 1.2.0 から 1.2.1 へ更新(GHSA-663w-cv8g-jj2p) Python (brotli / PyPI) >= 1.0.3, < 1.2.1 1.2.1 7 PyPI上の公式C拡張パッケージ(GHSA-9v8h-qqgm-4m35) Python (brotlicffi / PyPI) >= 1.0.3, < 1.2.1.0 1.2.1.0 Brotli C実装のCFFIバインディング(GHSA-9v8h-qqgm-4m35) .NET 8 / 9 / 10 8.0.0〜8.0.32 9.0.0〜9.0.21 10.0.0〜10.0.13 Runtime 8.0.33 Runtime 9.0.22 Runtime 10.0.14 9 SDKは 8.0.133/8.0.427、9.0.123/9.0.320、10.0.114/10.0.403 で修正 Palo Alto Networks PAN-OS PAN-OS 12.2 / 12.1 / 11.2 の未修正版 12.2.4-h1 12.1.7-h11 等 6 データプレーンでTLS復号とBrotli検査を有効化している場合のみ影響(デフォルト無効) 3. 主要Linuxディストリビューションの対応状況 OSの共有ライブラリ(libbrotli)およびディストリビューション提供パッケージにおける対応状況は以下の通りである。ディストリビューションによってはNode.jsや.NETパッケージがOS標準の共有ライブラリ(libbrotli)を動的リンクしているため、影響有無がパッケージ単位で異なる点に注意が必要である。 ディストリビューション 対象パッケージ 修正版 補足 Amazon Linux 2023 brotli, libbrotli, python3-brotli nodejs24, dotnet8.0 1.1.0-1.amzn2023.0.2 アドバイザリ: ALAS2023-2026-3334(brotli)、ALAS2023-2026-3330(nodejs24)、ALAS2023-2026-3367(dotnet8.0)。nodejs22・dotnet9.0・dotnet10.0 は影響なし Ubuntu brotli, libbrotli1, python3-brotli dotnet8, dotnet10, dotnet11 24.04 LTS: 1.1.0-2ubuntu0.1 22.04 LTS: 1.0.9-2ubuntu0.1 アドバイザリ: USN-8956-1(Brotli)、USN-8958-1(.NET 8.0.33 / 10.0.14) Debian brotli, libbrotli1 Trixie (13): 1.1.0-2+deb13u1 Bookworm (12): 1.0.9-2+deb12u1 DSA-6582-1 / DLA-4855-1。Debianの nodejs と python-brotlicffi はシステムの brotli を利用 RHEL (Red Hat) brotli, nodejs:24, dotnet8.0, firefox, thunderbird 提供済(各RHSA) RHSB-2026-015(Important)。RHEL 8/9の nodejs:22、RHEL 10の nodejs22/nodejs24、および dotnet9.0/10.0/11.0 はシステムの brotli を利用 4. AWS マネージドサービスの対応状況 AWSの 2026-155-AWS で公表されている主要マネージドサービスの対応状況は以下の通りである10。 対象サービス / リソース 修正版 補足 AWS Lambda(Node.js 22/24/26、.NET 8/9/10) 更新済 マネージドランタイム・ベースイメージ更新済。コンテナイメージ利用時は最新ベースイメージでの再ビルドが必要(Python/Ruby/provided.al2023 はBrotli非同梱) AWS Elastic Beanstalk (Node.js) 6.11.12 Node.js 24.22.1 および 22.23.4 を含むプラットフォームバージョン 6.11.12 を提供済 AWS App Runner / Amplify / CodeBuild / Synthetics 更新済 App Runnerは2026年10月26日に全リージョン更新済(未デプロイのサービスは1回再デプロイが必要) 影響を受ける条件 本脆弱性は、ライブラリがシステム上に存在するだけでは発火せず、「信頼できないBrotli圧縮ストリームを、対象アプリケーションやプロキシがデコード(展開)する」 処理が行われた際に成立する6。 1. 前提となるシステム・ネットワーク構成 攻撃が成立する代表的な経路は以下の4パターンである。 パターン1:クライアントからの圧縮HTTPリクエストボディを展開するサーバー / API クライアントから送信された Content-Encoding: br ヘッダー付きのリクエストボディを、ミドルウェア(Node.jsのExpress 5.0.0〜5.3.0やbody-parser 2.0.0〜2.3.0における raw() / json() 等)やバックエンドアプリケーションが自動または明示的にBrotli展開している構成(なお、素の node:http サーバー自体は単体ではリクエストボディを自動展開しない)。 パターン2:外部サーバーからの圧縮HTTPレスポンスを展開するHTTPクライアント / クローラー 接続先サーバーから返却された Content-Encoding: br レスポンスを自動展開する構成(Node.jsやBunの組み込み fetch()、Brotliパッケージがインストールされた環境でのPython urllib3 / requests / aiohttp / httpx[brotli] 等)。 多くのHTTPクライアントはリクエスト時に Accept-Encoding: br を送信したかどうかにかかわらず、レスポンスヘッダーに Content-Encoding: br が含まれていれば自動的にBrotliデコーダーを呼び出す。そのため、任意のURLを取得するWebhook、リンクプレビュー、クローラー、または非暗号化通信経路上の中間者攻撃において、悪意あるサーバーから一方的に Content-Encoding: br のレスポンスを返されるだけでトリガーされ得る。 パターン3:ファイアウォール等のデータプレーンにおけるトラフィック検査 Palo Alto NetworksのPAN-OS(PA-Series / VM-Series)において、データプレーン上でTLS/SSL復号(Decryption)とBrotliインスペクションの両方が有効化されている場合、通過するBrotliトラフィックをファイアウォール自身が展開するため影響を受ける。 パターン4:デコーダーAPIの直接呼び出し・ファイル処理 アーカイブ、キャッシュ、メッセージキュー等の外部データを、node:zlib(brotliDecompress*、createBrotliDecompress)、Web標準の DecompressionStream、Pythonの brotli.decompress()、.NETの BrotliStream / BrotliDecoder 等で直接展開している場合。 ! 影響を受けないケース: 圧縮(エンコード)のみ・静的 .br ファイル配信のみの構成: 脆弱性はデコーダー(c/dec/decode.c)に存在するため、信頼できるデータをサーバー側でBrotli圧縮して送信するのみの構成や、事前圧縮された静的な .br ファイルをそのまま配信するだけの構成ではデコード処理が行われず、影響を受けない。 .NET のデフォルト挙動: Microsoftのアドバイザリによれば、ASP.NET Coreおよび HttpClient はデフォルトではBrotli圧縮データを自動展開しない。自動展開でBrotliを有効化するよう明示的に設定変更している場合、または System.IO.Compression(BrotliStream / BrotliDecoder)で信頼できないデータを直接展開している場合のみ影響を受ける9。 2. スタンドアロン実行ファイル・コンテナイメージに関する注意点 自己完結型バイナリ(Self-Contained / 単一実行ファイル): .NETのSelf-ContainedデプロイやNativeAOTでビルドされたアプリケーション、およびBunの bun build --compile で生成された実行ファイルは、ビルド時点のランタイム(および脆弱なBrotliデコーダー)をバイナリ内部に埋め込んでいるため、ランタイム更新後にアプリケーションの再ビルドと再デプロイが必要である。 動的リンクとバンドルの違い: Red Hat Enterprise LinuxやDebian、Amazon Linux 2023では、パッケージによってBrotliを内部バンドルしているもの(例:AL2023およびRHEL 8/9の nodejs24、dotnet8.0)と、OSの共有ライブラリ libbrotli を動的リンクしているもの(例:Debianの nodejs、RHEL 8/9およびAL2023の nodejs22、dotnet9.0、dotnet10.0)が混在している。したがって、OSパッケージとランタイムの双方を確認・更新し、実行中のプロセスを再起動する必要がある。 3. 自環境の確認方法 1. Node.js のバージョンおよびバンドルされている Brotli バージョンの確認 node --version node -p "process.versions.brotli" 2. Bun のバージョン確認(1.4.4 以上であるか) bun --version 3. .NET ランタイムのバージョン確認(8.0.33 / 9.0.22 / 10.0.14 以上であるか) dotnet --list-runtimes 4. Python パッケージの確認(Brotli 1.2.1 以上、brotlicffi 1.2.1.0 以上であるか) python3 -m pip show brotli brotlicffi 5. Linux OSパッケージおよびロード済み共有ライブラリの確認 dpkg -l | grep -i brotli # Debian / Ubuntu rpm -qa | grep -i brotli # RHEL / Amazon Linux sudo grep -l brotlidec /proc/[0-9]*/maps 2>/dev/null 脆弱性の技術的な仕組み 前提:Brotliのブロック切り替えと高速パス(Fast Path)/ 安全パス(Safe Path) Brotliの圧縮ストリームは複数のブロックに分割されており、デコーダーはストリームを読み進めながらリテラル(tree_type = 0)、挿入・コピーコマンド(tree_type = 1)、距離コード(tree_type = 2)のブロックタイプを切り替える。 ブロックタイプを切り替える際、デコーダーは c/dec/decode.c 内の DecodeBlockTypeAndLength() を呼び出して次のブロックタイプ番号とブロック長をビットストリームから読み取る。このときBrotliデコーダーは、入力バッファに十分なデータがある場合に境界チェックを省略して高速に処理する Fast Path(safe = 0) と、入力不足を検査しながら処理する Safe Path(safe = 1) の2つの経路を持っている。 原因:戻り値の未チェック(CWE-252)と境界外書き込み(CWE-787) 脆弱性の根本原因は、c/dec/decode.c 内の DecodeBlockTypeAndLength() が返す真偽値 BROTLI_BOOL の仕様と、呼び出し元における戻り値の未チェックにある1。 戻り値 BROTLI_FALSE における2種類の意味の混在: 修正前の DecodeBlockTypeAndLength() は戻り値として単なる真偽値(BROTLI_BOOL)を返していた。この関数は、Safe Pathで入力ビットが一時的に不足している場合(!SafeReadSymbol や !SafeReadBlockLength)に BROTLI_FALSE を返すだけでなく、ストリームのブロックタイプ最大数 max_block_type が 1 以下であるにもかかわらずブロック切り替えが要求されたというストリーム構文エラー(フォーマット不正) の場合(if (max_block_type <= 1) return BROTLI_FALSE;)にも同じ BROTLI_FALSE を返していた。 Fast Path(safe = 0)での戻り値の無視: 修正前のコードでは、Fast Path用のラッパー関数 DecodeLiteralBlockSwitch()、DecodeCommandBlockSwitch()、DecodeDistanceBlockSwitch() の戻り値型が static void と定義されており、内部で呼び出す Decode*BlockSwitchInternal(0, s) の戻り値を完全に無視していた。さらに呼び出し元のマクロ BROTLI_SAFE(METHOD) も、safe == 0 の分岐では戻り値を検証せずにそのまま処理を継続する実装となっていた。 不正な内部状態でのデコード継続と境界外書き込み: 細工された圧縮ストリームによって本来起こり得ないブロック切り替えが要求された際、脆弱なデコーダーは構文エラーで停止せず、「追加入力が必要である」状態と誤認する、あるいはFast Path上でエラーを無視して不整合な内部状態のまま処理を継続してしまう。その結果、内部バッファの終端を越えて攻撃者のバイト列が書き込まれ、隣接するデコーダー状態メモリ(BrotliDecoderState)の破壊や機密メモリの漏洩、さらには任意のコード実行(RCE)へとつながる。 攻撃フロー 修正パッチ Googleの公式リポジトリにおける修正コミット 0ed4268a(および各ブランチへの反映コミット 50a9b70a、da980636、b418fa81)では、以下の変更が行われている2。 c/include/brotli/decode.h に、ブロック切り替えのフォーマット異常を表す新しいエラーコード BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH = -17 を追加。 DecodeBlockTypeAndLength() および各 Decode*BlockSwitch*() 関数の戻り値型を BROTLI_BOOL / void から BrotliDecoderErrorCode に変更し、max_block_type <= 1 のフォーマット異常時は BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH、入力不足時は BROTLI_DECODER_NEEDS_MORE_INPUT、成功時は BROTLI_DECODER_SUCCESS を明示的に返すように修正。 Safe PathとFast Pathの両方でステータスコードを検証し、BROTLI_DECODER_SUCCESS 以外の場合は即座に saveStateAndReturn へジャンプして処理を中断するマクロ BROTLI_SAFE_WITH_STATUS(METHOD) を導入。 c/dec/decode.c // google/brotli commit 0ed4268a より抜粋 -static BROTLI_INLINE BROTLI_BOOL DecodeBlockTypeAndLength( +static BROTLI_INLINE BrotliDecoderErrorCode DecodeBlockTypeAndLength( int safe, BrotliDecoderState* s, int tree_type) { brotli_reg_t max_block_type = s->num_block_types[tree_type]; const HuffmanCode* type_tree = &s->block_type_trees[ @@ -1187,7 +1187,7 @@ brotli_reg_t* ringbuffer = &s->block_type_rb[tree_type * 2]; brotli_reg_t block_type; if (max_block_type <= 1) { - return BROTLI_FALSE; - return BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH; } /* Read 0..15 + 3..39 bits. */ @@ -1197,11 +1197,13 @@ } else { BrotliBitReaderState memento; BrotliBitReaderSaveState(br, &memento); - if (!SafeReadSymbol(type_tree, br, &block_type)) return BROTLI_FALSE; - if (!SafeReadSymbol(type_tree, br, &block_type)) { - return BROTLI_DECODER_NEEDS_MORE_INPUT; - } if (!SafeReadBlockLength(s, &s->block_length[tree_type], len_tree, br)) { s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE; BrotliBitReaderRestoreState(br, &memento); - return BROTLI_FALSE; - return BROTLI_DECODER_NEEDS_MORE_INPUT; } } ... ringbuffer[0] = ringbuffer[1]; ringbuffer[1] = block_type; - return BROTLI_TRUE; - return BROTLI_DECODER_SUCCESS; } -static BROTLI_INLINE BROTLI_BOOL DecodeLiteralBlockSwitchInternal( +static BROTLI_INLINE BrotliDecoderErrorCode DecodeLiteralBlockSwitchInternal( int safe, BrotliDecoderState* s) { - if (!DecodeBlockTypeAndLength(safe, s, 0)) { - return BROTLI_FALSE; - BrotliDecoderErrorCode result = DecodeBlockTypeAndLength(safe, s, 0); - if (result != BROTLI_DECODER_SUCCESS) { - return result; } PrepareLiteralDecoding(s); - return BROTLI_TRUE; - return BROTLI_DECODER_SUCCESS; } -static void BROTLI_NOINLINE DecodeLiteralBlockSwitch(BrotliDecoderState* s) { - DecodeLiteralBlockSwitchInternal(0, s); +static BROTLI_NOINLINE BrotliDecoderErrorCode +DecodeLiteralBlockSwitch(BrotliDecoderState* s) { - return DecodeLiteralBlockSwitchInternal(0, s); } 呼び出し元のマクロも以下のように BROTLI_SAFE_WITH_STATUS へ置き換えられ、safe フラグの有無にかかわらずエラー発生時にデコードが即座に停止するようになった。 c/dec/decode.c +/* NB: METHOD should return BROTLI_DECODER_SUCCESS, BROTLI_DECODER_ERROR_*, or - BROTLI_DECODER_NEEDS_MORE_INPUT; the later two break the processing. */ +#define BROTLI_SAFE_WITH_STATUS(METHOD) \ - { \ - BrotliDecoderErrorCode status; \ - if (safe) { \ - status = Safe##METHOD; \ - } else { \ - status = METHOD; \ - } \ - if (status != BROTLI_DECODER_SUCCESS) { \ - result = status; \ - goto saveStateAndReturn; \ - } \ - } 対策と緩和策 1. 恒久対策:修正バージョンへのアップデートと再ビルド 利用しているOSパッケージ、言語ランタイム、および依存ライブラリを修正バージョンへ更新し、稼働中のプロセスを再起動する。また、単一実行ファイル化(Bunの bun build --compile、.NETのSelf-Contained / NativeAOT)やDockerコンテナイメージを利用している場合は、更新後のランタイム・ベースイメージを用いて再ビルドおよび再デプロイを実施する必要がある。 各環境におけるアップデートコマンド例# 1. Amazon Linux 2023 の場合(ALAS2023-2026-3334 / 3330 / 3367) sudo dnf update brotli nodejs24 dotnet8.0 -y 2. Ubuntu / Debian の場合(USN-8956-1 / USN-8958-1 / DSA-6582-1) sudo apt update && sudo apt install --only-upgrade brotli libbrotli1 python3-brotli -y 3. Node.js の更新(nvm を利用している場合) nvm install 24.22.1 # または 22.23.4 / 26.12.1 4. Python パッケージ(PyPI)の更新(1.2.1 / 1.2.1.0 以上) pip install -U "Brotli>=1.2.1" "brotlicffi>=1.2.1.0" 5. Bun の更新(1.4.4 以上) bun upgrade 2. 一時的な緩和策(アップデートが直ちに実施できない場合) 各ベンダーのアドバイザリおよび技術資料では、即座にパッチを適用できない環境向けに以下の緩和策が案内されている。 WAFやリバースプロキシにおける受信 Content-Encoding: br リクエストの遮断: AWSのセキュリティブレティン 2026-155-AWS 等では、クライアントから送信される Content-Encoding: br ヘッダー付きHTTPリクエストをWAFやプロキシで拒否する緩和策が提示されている。AWSが公開しているAWS WAFカスタムルールの設定例(Countモードで検証後に "Count": {} を "Block": {} に変更する手順)は以下の通りである。なお、AWS WAFは受信リクエストのみを検査するため、アプリケーションが外部サーバーから取得するレスポンスには効かない点に留意が必要である10。 { "Name": "Node2Shell_BROTLI", "Priority": 2, "Statement": { "RegexMatchStatement": { "RegexString": "^\s*br\s*$", "FieldToMatch": { "SingleHeader": { "Name": "content-encoding" } }, "TextTransformations": [ { "Priority": 0, "Type": "LOWERCASE" } ] } }, "Action": { "Count": {} }, "RuleLabels": [ { "Name": "Node2Shell_Brotli" } ], "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "Node2Shell_BROTLI" } } HTTPクライアントおよびPython環境におけるBrotliデコードの抑止: 注意点(Accept-Encoding ヘッダー変更の限界): リクエストヘッダーの Accept-Encoding から br を除外したとしても、悪意あるサーバーはそれを無視して Content-Encoding: br のレスポンスを送りつけることができるため、クライアント側で自動解凍自体が有効なままでは対策として不十分である。 Bun: セキュリティアドバイザリ(GHSA-663w-cv8g-jj2p)では、fetch 呼び出し時に decompress: false を渡すことで、Accept-Encoding: br の送信を抑止するとともにレスポンス自体の自動デコードを無効化する回避策が案内されている8。 Python: 直ちにアップグレードできない場合の緩和策として、Brotli および brotlicffi パッケージ(ならびに httpx[brotli] 等のextra指定)自体をアンインストールし、urllib3、requests、aiohttp 等でBrotliデコーダーが呼び出されない状態(gzip / deflate へのフォールバック)にする方法が挙げられている6。 PAN-OS: ホットフィックス適用までの回避策としてCLIから set system setting brotli-encoding disable を実行(またはWeb UIの Device > Setup > Content-ID から Allow Brotli Encoding のチェックを解除)してBrotliインスペクションを無効化する手順が案内されている。 参考資料・出典一覧 CVE.org - CVE-2026-111497 Google Brotli GitHub Repository - Commit 0ed4268a Node.js - Wednesday, October 21, 2026 Security Releases Imperva Blog - Node2Shell: From Brotli Data to Remote Code Execution (CVE-2026-111497) JPCERT/CC - BrotliのC言語実装における境界外書き込みの脆弱性(CVE-2026-111497)に関する注意喚起 (JPCERT-AT-2026-0034) Broccoli2Shell: Brotli decoder RCE (CVE-2026-111497) in Node.js, Python and more GitHub Advisory Database - GHSA-9v8h-qqgm-4m35 (PyPI brotli / brotlicffi) Bun Security Advisory - GHSA-663w-cv8g-jj2p Microsoft .NET Security Advisory - CVE-2026-111497: Out of bounds write in Brotli leads to RCE AWS Security Bulletin - 2026-155-AWS: CVE-2026-111497: Native Brotli Decoder Remote Code Execution and Memory Disclosure

  5. Microsoft Just Shipped Visual Studio's October Update. Here's What It Actually Changes. Visual Studio used to ship big releases every couple of years. Now it ships something every month, and the October update (18.11) is the one where the monthly cadence stops looking like a gimmick and starts looking like a strategy. Version 18.11.0 landed on October 13, followed by 18.11.1 on October 20 and 18.11.2 on October 25. The headline items are a rebuilt GitHub Copilot chat, bulk merge conflict resolution, commit-to-working-tree comparison, and Hot Exit. The smaller changes matter too, because they fix things developers have been voting on for years. The Problem It's Solving Most of this release is about friction. Developers lose unsaved work when the IDE closes. They resolve forty merge conflicts one file at a time when they already know which side wins. They wait for a 20-project solution to finish building after project three has already failed. They reach for the mouse to reorder tabs. None of that is glamorous, and that's the point. Microsoft's own framing is "deep platform integration of AI, stronger fundamentals, and improved performance." The October update is the "stronger fundamentals" half of that sentence, plus a structural change to how the AI half is built. How It Actually Works The new Copilot chat. This is the one with architectural consequences. Visual Studio now has a Copilot chat experience built on the Copilot SDK, the same foundation behind the Copilot CLI and the GitHub Copilot app. It started in July with Agent (Preview), and 18.11 extends the SDK to the whole chat stack, including Ask mode, Agent mode, and the underlying infrastructure. Microsoft lists three gains: agent workflows that need less back-and-forth, longer sessions with better prompt caching, and consistent behavior across Copilot surfaces. To turn it on, go to Tools → Options → GitHub → Copilot → Copilot Chat, check "Use new Copilot chat experience," and restart. It's opt-in, so you choose when to switch. Git, three ways. Right-click a commit in the Git Repository window and choose Compare with Working Tree to diff that commit against your current code, committed and uncommitted changes together, without checking anything out. This one is useful in refactors. For merges, Keep All Current and Take All Incoming now work on any file, folder, or the repo root under Unmerged Changes in Git Changes, so one action resolves everything beneath it. This builds on the worktree support and Git submodule integration that arrived in 18.9. Hot Exit. When enabled, Visual Studio saves session state on close and restores it on reopen. That covers unsaved files, saved files with unsaved changes, undo history including Ctrl+Z, and your tab and window layout. It's off by default. Enable it under Tools → Options → Projects and Solutions → General by selecting "Reopen unsaved documents on solution load or empty environment." The new Markdown template in the New File dialog (Ctrl+N) works with it, so scratch notes survive a restart. Stop build on first failure. Another opt-in setting, under Tools → Options → Projects and Solutions → Build and Run, called "Stop solution build after first failure." Visual Studio stops queuing projects and safely halts the ones in flight, and the build still reports as failed rather than canceled. Microsoft's test showed four regular builds averaging 26 seconds with it off and 1.5 seconds with it on, a 94% cut in post-failure time. That's one solution, and the release notes say results vary. Long dependency chains and heavily parallel builds should benefit most. Fonts and Colors. The legacy dialog has been rebuilt on Unified Settings, with category navigation, search, inline previews, and a grid view. It now participates in the settings.json model, so changes made in the UI show up in the file and vice versa. That makes IDE customization something you can version and share. The small ones. Ctrl+Numpad 1 through 9 jumps to numbered document tabs. Ctrl+Shift+PgUp and PgDn move tabs, with pinned and unpinned tabs staying separated, and the shortcuts take over the old Text Editor bindings for Edit.ViewTopExtend and Edit.ViewBottomExtend. Ctrl+NumPad0 resets editor zoom to 100%. A Quick Info hover delay setting (50 to 10000 ms, default 150) stops tooltips popping up over the line you're aiming for. Open Folder now respects the same restore settings that solutions do. What Developers Are Actually Using It For The October features are fresh, so I'd rather not invent adoption stories. What the release notes do make clear is what each feature is built for. Compare with Working Tree is for refactoring and for answering "what have I changed since that commit?" without a stash or a checkout. Bulk conflict resolution is for large merges where you've already decided the policy, such as taking incoming on generated files or keeping current on a vendored folder. Fail-fast builds are for large solutions where a broken base project makes the rest of the build pointless. Hot Exit is for anyone who has closed the IDE with a dozen dirty buffers. The Copilot work from earlier in the cycle is where the agentic usage shows up. Version 18.9 added a Git agent that reviews uncommitted changes and commits, with inline comments, on both GitHub and Azure DevOps repos. Version 18.10 let it explore pull requests in chat, with navigable links to comments and files. It also added Bring Your Own Key in preview, supporting Microsoft Foundry, OpenAI, Anthropic, and Ollama, though only for the new Agent (Preview). The earlier BYOK path in the old Ask and Agent modes is no longer supported. Version 18.8 added built-in .NET and Azure skills, off by default. Two smaller details are worth knowing for agent work. Version 18.9 added low, medium, and high thinking-effort controls for supported models, plus a usage view, because Copilot moved to usage-based billing on token consumption. And since 18.7, Visual Studio has applied trust validation for MCP servers. It compares each server's configuration and its tools, prompts, resources, and instructions against the last approved fingerprint, and prompts you to re-approve if anything changed. That one is on by default. For anyone wiring MCP servers into an IDE agent, it's the most important security feature in the cycle. Why This Is a Bigger Deal Than It Looks There are two reasons. First, the SDK consolidation. When Visual Studio's chat runs on the same Copilot SDK as the CLI and the Copilot app, the IDE stops being a special case that gets agent features months late. Microsoft says it plainly: Visual Studio gets better as Copilot gets better. For a product with this much legacy surface area, that's a significant change in how AI features reach users. The cost is churn. The BYOK breaking changes in 18.10 show that migrating onto the new harness will break things for some users along the way. Second, the cadence. Since 18.6 in May, there has been a numbered update every month, with weekly point releases between them. The September rollout shipped alongside a long list of .NET and ASP.NET Core security fixes, and 18.11.2 includes three more advisories. A faster release train means more frequent patches, and also more frequent regressions. 18.11.2 lists a known issue where F5 fails to launch the Blazor WebAssembly debug adapter, and earlier point releases fixed Copilot chat failing to open. The monthly rhythm works if you treat the first two weeks of each cycle as a soft launch. Also keep in mind that three of the most useful October features, Hot Exit, fail-fast builds, and the new chat, ship disabled. If you update and see no difference, that's why. Availability and Access Visual Studio 2026 is available from the Visual Studio download page, and 18.11.2 is the current build. Existing installs update through the Visual Studio Installer. The Copilot features require a GitHub Copilot plan, and Copilot usage now counts against token-based limits. Org-level custom agents and custom instructions require a GitHub organization. Pull request browsing and review in the IDE sits under Tools → Options → Preview Features. The full list of changes and bug fixes is in the official release notes, and feature feedback goes through Developer Community. --- Visual Studio is becoming a client for a shared agent runtime, and the rest of the release is Microsoft paying down small debts to keep people from leaving. Both changes are real. Only the second one is finished. Follow for more coverage on MCP, agentic AI, and AI infrastructure.

    5 reactions · 4 comments
  6. Reddit · · r/RimWorld · u/GTurkistane · dotnet/runtime

    If your game does not launch or keep crashing when trying the new Waylith Engine 1.7 Unstable version, Hardware-enforced Stack Protection (CET) might be the cause. Here's what fixed it for me. NOTE: As per the developer's note below, the solution described here is unnecessary if you have installed the latest Windows cumulative updates. However, if you're stuck on an older version of Windows 10 and cannot upgrade for any reason, the solution below should work for you. Otherwise, simply install the latest Windows cumulative update, and there should be no need to apply this workaround. For anyone having trouble launching RimWorld 1.7 with the new Waylith Engine, I recently ran into an issue where the game simply refused to start. After quite a bit of troubleshooting, I managed to find the cause and fix it. The problem turned out to be Hardware-enforced Stack Protection (CET), a Windows security feature that was interfering with the game's startup. What is Hardware-enforced Stack Protection (CET)? Hardware-enforced Stack Protection is a Windows security feature that uses CPU hardware to protect applications against certain memory-based attacks. CET stands for Control-flow Enforcement Technology. One of its features uses something called a shadow stack, which keeps a protected copy of function return addresses. This helps prevent malicious software from hijacking a program's execution flow. It's an important security feature, but certain applications and runtimes can have compatibility issues with it, particularly on older Windows builds. In my case, RimWorld 1.7 uses the new Waylith Engine and its customized .NET 10 runtime, and the game was crashing during startup whenever this protection was enabled. How to fix it (only for RimWorld) Do NOT disable this protection for your entire system. Windows allows you to configure it for individual applications. 1. Open the Start menu, search for Windows Security, and open it. 2. Go to App & browser control. 3. Scroll down to Exploit protection and click Exploit protection settings. 4. Select the Program settings tab. 5. Click Add program to customize. 6. Select Choose exact file path. 7. Navigate to your RimWorld installation folder and select RimWorld.exe. For example: C:\Program Files (x86)\Steam\steamapps\common\RimWorld\RimWorld.exe Your installation path may be different. 8. A window will appear with several security settings. Scroll down until you find Hardware-enforced Stack Protection. 9. Check Override system settings. 10. Change the setting from On to Off. Your setting should look like this. 1. Leave the other options, including Enforce for all modules and Audit only, unchecked. 2. Click Apply, close the settings, and try launching RimWorld again. That's it! In my case, the game immediately started working after making this change. My system specifications (relevant to the issue) - Operating System: Windows 10 IoT Enterprise, 64-bit - Windows Version: 20H2 - OS Build: 19042 - CPU: AMD Ryzen 9 7950X3D - RimWorld Version: 1.7.4990.1069 (Waylith Engine, unstable branch) - Game Runtime: .NET 10.0.13-waylithclr.dev (WaylithCLR) - Installed .NET Runtime: microsoft.netcore.app 10.0.3 - Faulting Module: KERNELBASE.dll (10.0.19041.1466) - Exception Code: 0x80131506 These details might be particularly relevant if you're running an older Windows 10 build. I suspect there's a compatibility issue between the newer .NET runtime, CET, and certain older Windows components. Important security note This is a workaround, not necessarily a permanent fix. Disabling Hardware-enforced Stack Protection reduces exploit protection for the application you've selected. That's why I strongly recommend disabling it only for RimWorld.exe, rather than changing system-wide security settings. Microsoft has documented similar CET-related .NET runtime compatibility problems on older Windows builds. Updating Windows to a properly serviced version may resolve the underlying issue without requiring this exception. Relevant Microsoft .NET issue: runtime#108589 If you're experiencing the same problem, give this a try and let me know whether it works for you. Hopefully this saves someone else the hours of troubleshooting I went through!

    2 points · 7 comments
  7. Node2ShellはNode.jsだけの話ではない。Brotliの欠陥でPythonも対象、CVE-2026-111497の該当判定 「Node2Shell? うちは Node.js を使っていないから関係ないな」。10月下旬にこのニュースを見て、そう判断しかけた方もいると思います。ところが、Node.js を使っていなくても確認が必要な場合があります。Node2Shell(HTTP2Shell とも呼ばれる、CVE-2026-111497)の正体は、Node.js ではなく Google の圧縮ライブラリ Brotli の欠陥でした。Python の Brotli パッケージ(入っていると requests などが自動で使う)、Bun、Envoy、Git for Windows にも同じ欠陥がありました。10月27日には、米国の CISA(サイバーセキュリティ・社会基盤安全保障庁)が、実際に悪用が確認された脆弱性の一覧に追加しています。 この記事は、Node.js・Python・Bun でアプリを作っている人や、Windows で Git for Windows を使っている人向けです。読み終えると、次の3つが分かります。 なぜ「Node.js の脆弱性」として広まったのか(原因) 自分の環境が該当するか、どこまで上げればよいか(言語・ツール別の判定) 呼び名・深刻度・RCE の範囲など、記事ごとに食い違う情報のどれを信じればよいか 情報は2026年10月29日時点のものです。技術的な解析は Imperva と OpenSourceMalware の記事が詳しく、Python・curl・Git for Windows の動作検証は技術ブログの lilting が詳しいです。どれも末尾のリンクから読めます。 ! 先に答え(2026年10月29日時点) 出回った言い方 一次情報での答え Node.js の脆弱性 欠陥があるのは Google の Brotli。Node.js は同梱していた Brotli を直した側 Node.js を使っていなければ無関係 Python(Brotli パッケージがあると requests 等が使う)・Bun・Envoy・Git for Windows も対象 Node2Shell と HTTP2Shell は別の脆弱性 どちらも CVE-2026-111497 の1件 深刻度は High? Critical? どちらも正しい。評価した組織が違う 素の Node.js ならどこでも RCE される 実証は Node.js 24.22.0(arm64 と x86-64)で報告された。24.22.0 用の攻撃データは 22.23.3・26.12.0 では通らず、ビルドごとに調整が要る 実際の攻撃はまだ? CISA が10月27日に、悪用が確認された脆弱性の一覧(KEV)に追加した。攻撃通信を観測したという報告もある 自分は該当する? 使っている言語ではなく、「信頼できない相手から来た Brotli データを展開するか」と「同梱している Brotli が直った版か」で決まります。判定だけ知りたい方は「自分は関係ある? 言語・ツール別の判定」へ進んでください。 Brotli とは何で、何が壊れていたのか Brotli は Google が作った圧縮形式で、Web では HTTP の Content-Encoding: br として使われます。欠陥があったのは、圧縮データを元に戻す部分(デコーダ)の C 言語実装です。壊れた圧縮データを渡すと、デコーダが内部バッファの外へ書き込んでしまいます(境界外書き込み)。本来触ってはいけないメモリを書き換えるため、条件がそろうと遠隔からのコード実行(RCE)につながります。 影響を受ける版: 1.0 系は 1.0.3〜1.0.9、1.1 系は 1.1.0、1.2 系は 1.2.0 修正版: 1.0.10 / 1.1.1 / 1.2.1(日本時間の10月22日 04:27 公開) CVE の採番: Google 発見: OpenAI Codex Security なぜ「Node.js の脆弱性」として広まったのか 広く知られるきっかけが、10月21日の Node.js 定例セキュリティリリースだったからです。流れを表にまとめます(日付は日本時間)。 日付 出来事 2月27日 Brotli のリポジトリに修正コミット。メッセージは「Detect and report invalid stream earlier.」(壊れたストリームを早めに検出して報告する)の1行で、脆弱性とは書かれていない。修正版の公開まで約237日 10月21日 Node.js が定例セキュリティリリースで10件を修正。その1件がこの欠陥 10月22日 Brotli の修正版を公開。Imperva が「Node2Shell」の題で RCE の実証記事を公開 10月23日 OpenSourceMalware が Python への波及を公表 10月24日 Envoy がアドバイザリを公開。Git for Windows に Issue #6514 が立つ Imperva はセキュリティ企業です。実証の対象が Node.js で、記事の題にも Node.js が入りました。そのため、「Node.js の脆弱性」として広まりました。 ところが、Node.js が直した中身は Brotli のコードです。Node.js は Brotli を自分のリポジトリに同梱していて、今回は Brotli の修正コミットを cherry-pick で取り込みました。cherry-pick は、別のリポジトリの修正部分だけを持ってくる操作です。 つまり Node.js は、同梱していた Brotli を直した側であって、欠陥の持ち主ではありません。 Python への波及を最初にまとめた OpenSourceMalware(OSM)も、この点を「この脆弱性は、そもそも Node.js のものではない」と書いています。 OSM の原文(英語) The vulnerability isn't really in Node.js at all Node.js 以外に対象になるもの(Python・Bun・Envoy・Git for Windows) 原因が Brotli のデコーダにあるので、同じデコーダを積んでいるものは確認が必要です。10月29日までに、次のものが分かっています。 Python: Brotli / brotlicffi パッケージ。どちらかが入っていると、urllib3 / requests、httpx(brotli extra)、aiohttp、Scrapy が br の応答を自動で展開します Bun: 1.4.4 で同梱の Brotli を更新しました Envoy / Istio: Brotli の展開(decompressor)を設定した構成が対象です curl: リンクしている Brotli のライブラリ次第です Git for Windows: 2.56.0.windows.3 までは、同梱の brotli が 1.2.0 のままでした。10月26日に公開された 2.56.0.windows.4 で 1.2.1 に更新されています このほかにも、対象として次のものが見つかっています。 .NET: 修正版は 8.0.33 / 9.0.22 / 10.0.14 です(.NET Framework は対象外)。BrotliStream などで自分で展開する処理が対象です。HttpClient の応答の自動展開と ASP.NET Core のリクエスト展開は、既定では無効です Go の andybalholm/brotli: Brotli を Go に移植したライブラリです。v1.2.7 に同じ修正が移植されました Linux の git の HTTPS 通信: git が使う libcurl が Brotli 付きでビルドされていれば対象です。oss-security への投稿によれば、多くのディストリの libcurl は Brotli 付きです Git for Windows のメンテナは、10月24日の Issue で次のように書いています。 昨日の時点では、これは「ただの」node2shell だと言われていた。だから Git for Windows は無関係に見えた。 メンテナの原文(英語、Git for Windows Issue #6514) Yesterday, the word on the street was still that this was "only" node2shell, and therefore Git for Windows looked unaffected. メンテナ自身も、名前を見て「自分は関係ない」と判断しかけていたわけです。「Node2Shell」という名前は、最初に話題になった場所を示しているだけで、影響範囲を示してはいません。 該当するかは「言語」ではなく2つの条件で決まる 対象の部品が入っていても、すぐに攻撃されるとは限りません。どの環境から先に上げるかは、次の2つの条件で決まります。 条件1: 信頼できない相手から来た Brotli データを展開するか。 典型的な経路は2つあります。 外部サーバーの応答を、Node.js の fetch() や Python の requests などで受け取る。応答の中身は相手のサーバーが決めます。攻撃者が用意したサーバーなら、壊れたデータを返せます 受け取ったリクエスト本文を、ミドルウェアが展開する。Imperva は、Express 5 / body-parser 2 の raw()・json() がリクエスト本文を展開する経路で実証しています 一方、素の Node.js の HTTP サーバーは、リクエストを受け取っただけでは本文を展開しません(Imperva の記事による)。 条件2: 同梱している Brotli が直った版か。 これは言語やツール本体の版で決まります。Brotli を自分で入れていなくても、ランタイムやツールの中に入っていることがあります。 特に先に上げるべきなのは、外部の URL を取ってくる機能を持つアプリです。Webhook、URL のプレビュー表示、クローラ、「この URL の内容を取ってきて」と頼める機能などがこれに当たります。 下の図の「Brotli デコーダを積んだもの」は、前の節で挙げた Node.js・Python の Brotli / brotlicffi・Bun・Envoy・Git for Windows などです。 自分は関係ある? 言語・ツール別の判定 ! 判定の前に、落とし穴が2つあります。 版番号: Node.js は修正を cherry-pick で取り込みました。そのため、修正版でも process.versions.brotli の表示は修正前と変わりません(lilting の検証では、22.23.4 でも 1.1.0 のまま)。Node.js では、Brotli の版ではなく node -v で Node.js 本体の版を見ます。ただし、ディストリが提供する nodejs はシステムの libbrotli を使うことがあります(Ubuntu・Debian の告知)。その場合は、ディストリの告知で判断します スキャナ: Node.js はランタイムそのものなので、npm audit には出ません。Python では、10月23日に OSM が「Brotli・brotlicffi の PyPI パッケージは、どの脆弱性データベースにも載っていない」と書きました。翌24日には、pip-audit が検出するようになっています(lilting の検証)。スキャナが何も言わないことを、安全の根拠にしないでください 対象 何を見るか OK の基準・対処 優先して上げるべき使い方 Node.js node -v 22・24・26 系は、それぞれ 22.23.4 / 24.22.1 / 26.12.1 以上。それ以外の系列(20 以下など)は修正されないので、サポート中の系列へ移る fetch() で外部 URL を取る。Express 5 / body-parser 2 で br の本文を受ける Python Brotli / brotlicffi が入っているか Brotli 1.2.1 / brotlicffi 1.2.1.0 以上。Linux ディストリビューション(以下ディストリ)が提供するパッケージは、ディストリの更新で上げる requests・httpx・aiohttp 等で外部 URL を取る Bun bun --version 1.4.4 以上。コンパイル済みの実行ファイルは作り直しが必要(Aikido の記述) 外部 URL を取る Envoy Brotli decompressor を設定しているか 1.40.0 / 1.39.3 / 1.38.6 / 1.37.8 / 1.36.12 以上 設定している構成すべて Istio Brotli の展開を有効にしているか(EnvoyFilter など) 1.31.2 / 1.30.6 / 1.29.9 以上(Istio の告知 ISTIO-SECURITY-2026-007) 有効にしている構成すべて Git for Windows git version 2.56.0.windows.4 以上(10月26日公開。同梱の brotli は 1.2.1) 信頼できない HTTPS サーバーへの clone / fetch curl どの Brotli にリンクしているかは、ビルドした配布元次第 配布元(OS・ディストリ・同梱しているツール)の更新情報を確認して上げる。Windows 標準の curl は Brotli 非対応(lilting の検証) --compressed などで、信頼できない URL の応答を展開させる Linux の libbrotli ディストリのセキュリティ情報 ディストリの更新を当てる。版番号を変えずに修正だけ移植する(バックポート)ことがあるので、版番号ではなくディストリの告知で判断する libbrotli を使うアプリが外部データを展開する Bun 1.4.4 のリリースノート(公式ブログ)は、Security の節で Brotli に触れていますが、CVE 番号は書かれていません。CVE 番号で探すと見落としやすいので注意してください。 主なディストリの対応状況は、10月29日時点で次のとおりです。 Ubuntu: brotli は jammy(22.04)・noble(24.04)などで修正済みです(USN-8956-1 / USN-8956-2)。jammy 以降の nodejs はシステムの brotli を使うため「影響なし」とされています。focal(20.04)は、brotli が「評価中(Needs evaluation)」、Brotli を同梱する nodejs が「vulnerable」のままです Debian: brotli は bookworm(DLA-4855-1)と trixie(DSA-6582-1)で修正済みです。ディストリ版の nodejs と trixie の python-brotlicffi は、トラッカー上はまだ「vulnerable」の表示です。ただし、トラッカーの注記では、どちらもシステムの brotli を使うとされています 同じディストリの中でも、パッケージごとに状況が違います。使っているパッケージごとに告知を確かめてください。 Node.js・Python・Git for Windows については、表に書ききれない確かめ方を補足します。 Node.js: node -v で本体の版を見る node -v Docker イメージの中の Node.js を見る例(<image> は自分のイメージ名に置き換える) docker run --rm <image> node -v 出力を表の基準と比べます。手元の Node.js だけでなく、Docker イメージや、Node.js を同梱して配っているバイナリの中の版も見てください(Imperva の推奨)。 Python: Brotli / brotlicffi が入っているかを見る 確かめたい venv を有効にしてから実行する python -m pip show Brotli brotlicffi 出力の Version: の行を、表の基準と比べます。 どちらも見つからなければ、requests などは br の応答を展開しません(OSM の解説と lilting の検証)。ただし、Brotli を別の形で同梱したライブラリや外部ツールを使っている場合は、そちらも確かめてください。また、venv ごとに入っているパッケージは違います。アプリごと・コンテナごとに見てください。 Git for Windows: git version で版を見る git version git version 2.56.0.windows.4 以上と表示されれば、修正版です。10月26日に公開され、Issue #6514 もクローズされました。 同梱の brotli の版は、Git for Windows のインストール先にある etc/package-versions.txt で確かめられます。Git Bash からは次のように見られます。 grep brotli /etc/package-versions.txt 64ビット版の修正版なら mingw-w64-ucrt-x86_64-brotli 1.2.1-1 と出る 上げられるまでの間は、信頼できない HTTPS サーバーへの clone / fetch を避けるのが安全です(lilting の見解)。 呼び名・深刻度・RCE の範囲・悪用、食い違いの答え合わせ ここまでで判定はできます。この節では、調べると記事ごとに食い違って見える4つの点を、一次情報で確かめます。 Node2Shell と HTTP2Shell は同じ1件 Node2Shell は Imperva の記事タイトルが元です。HTTP2Shell は、セキュリティ企業 Aikido の記事の見出し「Node2Shell evolves to HTTP2Shell」(Node2Shell が HTTP2Shell に進化した)に出てくる呼び方です。見出しは、HTTP を使うライブラリやアプリにも影響が広がったことを伝えるものです。ただし、Aikido は呼び名を変えた理由を本文で説明していません。検索すると Broccoli2Shell という呼び方も出てきます。どれも指しているのは CVE-2026-111497 の1件です。 もう1つ紛らわしいのが、2024年の別の脆弱性です。「Node.js Brotli 脆弱性」で検索すると、検索エンジンの AI 要約が CVE-2024-22025 を返すことがあります。これは2024年に見つかった、fetch() が Brotli データを展開するときにサービス停止(DoS)を起こす問題で、今回とは別件です。 High と Critical 9.2 は、評価した組織が違う 深刻度の点数は CVSS という共通の尺度で付けます。CVE を採番した Google は、CVSS 4.0 で 9.2(Critical)と評価しました。GitHub のアドバイザリと NVD(米国の脆弱性データベース)もこの値を載せています。 一方、Node.js は自分のリリース表でこの件を High と書きました。評価したのは Node.js 自身で、対象も Node.js という1つの製品です。評価した組織も、評価の対象にした製品も違うので、どちらも間違いではありません。 RCE の実証は広がったが、成否はビルドで違う 「素の Node.js で RCE できる」という言い方も出回っています。最初に Imperva が実証に使ったのは、公式の Node.js 24.22.0 Linux arm64 イメージです(経路は fetch() と Express 5 / body-parser 2)。成功までには何回か試す必要がありました。 その後、セキュリティ企業の ELLIO が、Node.js 24.22.0 の x86-64 でも RCE に成功したと報告しました。成功した構成は4つで、node:http のサーバーから node:zlib を呼んで本文を展開するもの、公式の slim イメージ、Express 5.3.0(express.json())、Koa 3(@koa/bodyparser)です。node:http の例は、フレームワークを挟まず、アプリのコードが自分で本文を展開する構成です。ELLIO も、素の Node.js の HTTP サーバーはリクエストを受け取っただけでは Brotli の本文を展開しないと書いています(条件1 と同じ)。「node:http を使っているだけで RCE される」という意味ではありません。 一方、24.22.0 用に調整した攻撃データは、22.23.3 と 26.12.0 では1万回試しても成功しませんでした。ELLIO は、これらの版には別の調整が要るとしています。攻撃を成功させるには、ビルドごとに作り込む必要があるということです。「どの環境でもすぐ RCE される」わけではありません。 Python については、RCE を試した報告はまだありません。OSM は、壊れたデータを渡すとデコーダが誤った結果を返すところまでを確かめています。 ただし、24.22.0 用の攻撃データが通らなかったからといって、ほかの版が安全だという証明にはなりません。判定で該当した環境は、実証の有無にかかわらず上げてください。 実際の攻撃は、すでに確認されている 10月25日の時点では、悪用の報告は見当たりませんでした。その後、状況が変わっています。CISA は10月27日に、この CVE を KEV(Known Exploited Vulnerabilities、実際に悪用が確認された脆弱性の一覧)に追加しました。米国の政府機関には、11月10日までの対応が求められています。 ELLIO も10月26日の追記で、自社のセンサーで実際の攻撃通信を観測したと報告しています。狙いは Node.js 24.22.0 の x86-64 で、ELLIO の検証では、観測した攻撃データそのものは成功しませんでした。攻撃を再現するコード(PoC)も公開されて出回っています。 ただし、CISA は KEV に追加した根拠となった具体的な事例を公表していません。どの製品のどの使い方が狙われたのかは、まだ分かりません。 まだ分かっていないこと 10月29日時点で、次の点はまだ決着していません。 Ubuntu の focal(20.04)は、修正版が出ていません。Debian のディストリ版 nodejs と trixie の python-brotlicffi は、トラッカー上「vulnerable」の表示が残っています Python で RCE が可能かどうかを検証した報告は、見当たりません CISA が KEV に追加した根拠の事例は公表されておらず、どこが狙われたのかは分かっていません ブラウザや、Node.js を内蔵するアプリ(Electron など)については、この記事では扱いません。影響の有無を示す一次情報が見当たらないためです。 おわりに Node2Shell の原因は Node.js ではなく Brotli の欠陥で、Node.js は直した側でした。呼び名が違っても指しているのは1件で、深刻度の違いは評価した組織の違いです。RCE の成否はビルドによって違いますが、実際の攻撃はすでに確認されています。 「Node.js は使っていないから関係ない」と思った方も、Python の venv と git version を一度確かめてみてください。判断の材料は「Node2Shell」という名前ではなく、手元にあるパッケージとツールの版です。 参考リンク Node.js: Security Releases: October 21, 2026 GitHub Advisory: GHSA-9v8h-qqgm-4m35(PyPI Brotli / brotlicffi) NVD: CVE-2026-111497 google/brotli: Releases Git for Windows Issue #6514: Bundled brotli is still 1.2.0 in 2.56.0.windows.3 Imperva: Node2Shell: From Brotli Data to Remote Code Execution OpenSourceMalware: Node2Shell affects Python Envoy: GHSA-3wvj-j6qm-62mr Istio: ISTIO-SECURITY-2026-007 Bun: Bun v1.4.4 Aikido: Node2Shell evolves to HTTP2Shell lilting: Brotli CVE-2026-111497 beyond Node.js, tested with Python, curl, and Git for Windows CISA: Known Exploited Vulnerabilities Catalog(CVE-2026-111497) ELLIO: Node2Shell(CVE-2026-111497)の検証記事 Git for Windows: v2.56.0.windows.4 Ubuntu: CVE-2026-111497 Debian Security Tracker: CVE-2026-111497 .NET: dotnet/announcements #494 oss-security: Brotli CVE-2026-111497 and git-remote-https andybalholm/brotli: v1.2.7 の更新履歴

  8. Где Runtime Async в .NET 11 экономит память, а где нет Всем привет, меня зовут Сергей Прощаев, я Tech Lead и руководитель направления Java | Kotlin разработки в FinTech & E-commerce и преподаю на курсах разработки и архитектуры в ОТУС. В этой статье расскажу про Runtime Async в .NET 11: что меняется, когда приостановкой async-методов занимается рантайм, а не код от компилятора, и где выигрыш пропадает. На собеседованиях по C# годами звучит один и тот же вопрос: «во что компилятор превращает async-метод?». Правильный ответ знают все: в структуру с конечным автоматом, методом MoveNext и билдером задачи. В .NET 11 у этого ответа появляется оговорка «если вы не включили runtime-async». Я прогнал бенчмарки команды .NET на RC2 сам: аллокации совпали с опубликованными до байта, но только при одном условии, о котором в анонсах не пишут. Сразу оговорюсь, почему про C# пишет человек с JVM. Мой основной стек — Java и Kotlin, и именно поэтому за асинхронностью в .NET интересно следить со стороны: в 2023 году Java выпустила виртуальные потоки, а команда .NET свой эксперимент с зелёными потоками отложила и пошла улучшать async/await. В ноябре ожидается релиз .NET 11, где Runtime Async уже есть, но как preview-функция: для своего кода её включают явно, а поведение проверяют на своей нагрузке. С изменениями в .NET 11 разобраться проще, если понимать, как async/await работает в классической реализации. Эту тему подробнее разберём 29 октября в 20:00 на бесплатном открытом уроке «Магические слова async/await». Три платформы, три ответа на один вопрос Задача у всех одна: поток не должен простаивать, пока сервис ждёт ответа от базы или соседнего API. Решают её по-разному. Компиляторы Kotlin и классического C# преобразуют асинхронные функции в конечные автоматы. Kotlin превращает каждую suspend-функцию в автомат с полем label и скрытым параметром Continuation. Компилятор C# делает почти то же: генерирует структуру, хранит в её полях локальные переменные, которые живут через await, и раскладывает тело метода по состояниям внутри MoveNext. JVM и CLR исполняют уже сгенерированный код и дают инфраструктуру продолжений, но сам автомат не строят. // (Kotlin) suspend-функция: компилятор добавит скрытый параметр Continuation // и превратит тело в конечный автомат с полем label suspend fun price(id: Int): Int { val base = loadBase(id) // точка приостановки, пример схематичный return base + tax(base) }sourcecraft.dev Java пошла другим путём. В JDK 21, вышедшем 19 сентября 2023 года, появились виртуальные потоки (JEP 444): код остаётся синхронным и блокирующим, а приостановкой и возобновлением занимается JVM, снимая стек виртуального потока с несущего. // (Java 21, схематично) код блокирующий, приостанавливает и возобновляет его JVM try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() -> callBlockingApi(id)); }sourcecraft.dev Команда .NET попробовала тот же путь. За одиннадцать дней до выхода JDK 21, 8 сентября 2023 года, она опубликовала итоги эксперимента с зелёными потоками. Вывод был сдержанным: зелёные потоки возможны, но это вторая модель программирования рядом с async/await, и они плохо уживаются друг с другом. Самая наглядная цифра из отчёта: 100 миллионов минимальных вызовов P/Invoke на зелёном потоке заняли около 1800 мс вместо 300 мс. Э ксперимент поставили на паузу и решили улучшать существующую модель. Следующим шагом стал эксперимент runtime-async в .NET 9, а итогом — Runtime Async в .NET 11. На схеме ниже три подхода сведены рядом, чтобы было видно, где живёт конечный автомат в каждом из них. Главная мысль схемы: .NET 11 занял середину. Программная модель остаётся прежней, с Task, await и окраской методов, но приостановку и возобновление организуют VM и JIT, у которых больше информации. Виртуальные потоки позволяют во многих сценариях писать блокирующий код без async-сигнатур по всему стеку вызовов. .NET от окраски методов не отказывается и выигрывает за счёт того, что ничего не надо переписывать. Что именно меняется под капотом В классической схеме компилятор C# для каждого async-метода выпускает метод-обёртку, тип конечного автомата, поля для захваченного состояния и тело MoveNext. Упрощённо это выглядит так: // (C#, упрощённо) что компилятор делает из async-метода без runtime-async public Task<int> GetPriceAsync(int id) { var sm = new GetPriceAsyncStateMachine { Id = id, State = -1 }; sm.Builder = AsyncTaskMethodBuilder<int>.Create(); sm.Builder.Start(ref sm); return sm.Builder.Task; } struct GetPriceAsyncStateMachine : IAsyncStateMachine { public int State; public int Id; public AsyncTaskMethodBuilder<int> Builder; private TaskAwaiter<int> _awaiter; public void MoveNext() { // switch по State, получение awaiter, // AwaitUnsafeOnCompleted при незавершённой задаче, SetResult в конце } public void SetStateMachine(IAsyncStateMachine stateMachine) { } }sourcecraft.dev С runtime-async компилятор не генерирует пару IAsyncStateMachine и MoveNext. Он оставляет компактный IL, ставит методу флаг async (MethodImplOptions.Async, значение 0x2000 по черновику изменений ECMA-335), а точки приостановки кодирует вызовами методов AsyncHelpers: для Task и ValueTask это перегрузки Await, для прочих awaiter-ов AwaitAwaiter и UnsafeAwaitAwaiter. Для оптимизации важнее всего пара «вызов async-метода и сразу подходящий Await». Приостановку, сохранение нужного состояния и возобновление на другом потоке организуют VM и JIT. Так что меняется не столько место, где строится прежний автомат, сколько распределение ответственности, и поэтому JIT может оптимизировать границы между async-методами, а не только код внутри каждого. Включается фича свойством в файле проекта, выключается для проекта другим свойством. В .NET 11 это по-прежнему preview: <!-- (XML, csproj) включение Runtime Async для своего кода --> <PropertyGroup> <TargetFramework>net11.0</TargetFramework> <Features>$(Features);runtime-async=on</Features> </PropertyGroup> <!-- (XML, csproj) отключение для проекта --> <PropertyGroup> <UseRuntimeAsync>false</UseRuntimeAsync> </PropertyGroup>sourcecraft.dev Есть деталь, которую легко пропустить: сами библиотеки .NET 11 уже собраны с runtime-async=on и конечных автоматов от компилятора больше не содержат. Ваш код при этом может оставаться в классическом режиме, обе модели работают вместе. Получается, что, обновившись на .NET 11, вы уже исполняете runtime-async внутри BCL, даже если флаг не трогали. Что это даёт: пять наблюдений Я бы предпочёл не принимать «быстрее и чище» на веру, поэтому собрал репозиторий dotnet-runtime-async-demo и прогнал его на .NET 11 RC2 (SDK 11.0.100-rc.2) в GitHub Actions на ubuntu-24.04. Классическую схему для отдельного метода оставляет экспериментальный атрибут [RuntimeAsyncMethodGeneration(false)], как у Тауба, поэтому оба варианта работают на одном рантайме и отличаются только тем, как их собрал компилятор. Для сверки рядом цифры из документации .NET 11 и разбора производительности Стивена Тауба. Аллокации между прогонами стабильны, время скачет, поэтому опираюсь на байты. Наблюдение 1. Живые стек-трейсы стали короче в разы. Три вложенных async-метода, внутренний создаёт new StackTrace(). В классическом режиме у меня 13 кадров, из них 3 — AsyncMethodBuilderCore.Start. С runtime-async кадров 4, и это ровно цепочка вызовов. В документации 13 и 5: там Main тоже асинхронный, а мой пробник входит в цепочку из синхронного Main через GetAwaiter().GetResult(), так что точное число кадров зависит от точки входа: (text, вывод с runtime-async=on, results/stack_runtime.txt) at Runtime.InnerAsync() at Runtime.MiddleAsync() at Runtime.OuterAsync() at Program.<Main>$(String[] args)sourcecraft.dev Важная оговорка из той же документации: стек-трейсы исключений из catch выглядят одинаково в обоих режимах, их давно чистит ExceptionDispatchInfo. Выигрывают профилировщики, отладчик и всё, что смотрит на стек во время работы. С виртуальными потоками Java сходство здесь концептуальное: при реальной приостановке await физический стек в .NET по-прежнему разматывается, Runtime Async оптимизирует существующую модель, а не заменяет её стековыми потоками. Наблюдение 2. Когда Runtime Async устраняет промежуточные задачи. Самый интересный механизм — слияние вызовов. Если A ждёт B, а B ждёт C, в классической схеме у каждого метода свой сгенерированный автомат и свой результат-задача. Автомат при этом не обязательно попадает в кучу: на синхронном пути структура живёт на стеке, а платим мы в основном за задачи-результаты. JIT с runtime-async распознаёт пару «вызов метода, возвращающего Task, и сразу await» и передаёт результат дальше обычным значением. Задача материализуется только на внешней границе, где её действительно кто-то ждёт как объект. На схеме ниже эта разница показана на цепочке из трёх методов. Главная мысль схемы: контракт Task никуда не делся, он переехал туда, где задача действительно нужна. На рисунке благоприятный случай: вся цепочка собрана в новом режиме и вызовы идут подряд. В смешанной цепочке с классическими методами или когда задачу кладут в коллекцию и вешают продолжение руками, объект по-прежнему создаётся. Бенчмарк Тауба с двумя уровнями вложенности я повторил в BenchmarkDotNet 0.16.0-preview.2 с MemoryDiagnoser, в обычном режиме, когда каждый бенчмарк идёт в отдельном процессе: Сценарий Аллокации у меня Аллокации у Тауба Цепочка завершилась синхронно, классический async 144 B 144 B Цепочка завершилась синхронно, runtime-async 0 B 0 B Цепочка с Task.Yield, классический async 248 B 248 B Цепочка с Task.Yield, runtime-async 168 B 168 B Байты совпали до единицы. По времени у меня синхронная цепочка ускорилась с 26,95 до 10,72 нс, у Тауба с 21,221 до 6,151 нс: порядок тот же, машины разные. Наблюдение 3. Глубокие цепочки с исключением дешевеют на порядок. Рекурсивная цепочка бросает исключение на дне. На глубине 30 без приостановки классический вариант аллоцирует 84,2 KB, runtime-async — 5,71 KB, у меня и у Тауба одинаково. По времени у меня 56,6 мкс против 13,0 мкс. Пример синтетический: в реальном сервисе картину изменят локальная обработка исключений, логирование, повторы и настоящие точки приостановки. Наблюдение 4. Сборка становится вдвое меньше. Приложение из десяти вложенных async-методов в классическом режиме дало SizeProbe.dll размером 10 752 байта, а с runtime-async — 5632 байта, байт в байт как в разборе. Для одного сервиса это мелочь, в крупном приложении с множеством своих async-методов эффект может накапливаться. Размер итогового NativeAOT-образа и готовых сторонних сборок я не мерил, так что количественного вывода для них не делаю. Наблюдение 5. Продолжения перестали таскать пустой контекст. Это изменение не про флаг, по документации оно работает в .NET 11 для всех. Раньше каждое продолжение снимало и восстанавливало ExecutionContext со значениями AsyncLocal, даже когда восстанавливать было нечего. Теперь рантайм этот цикл пропускает. Пункт из документации, я его не мерил: в сервисе с трассировкой и областями логирования контекст часто непустой, и эффект там, по моей гипотезе, скромный. Где выигрыш пропадает Первое, и для меня главное: результат зависит от того, как запущен замер. Сначала я гонял те же бенчмарки с флагом --inProcess, когда BenchmarkDotNet исполняет их в своём процессе, и написал свой пробник на GC.GetTotalAllocatedBytes, который тоже работает в одном процессе. Оба показали другую картину: Сценарий, в одном процессе Классический async Runtime-async Цепочка завершилась синхронно 144 B 72 B Цепочка с Task.Yield 192 B 240 B Причину расхождения я не установил, поэтому не считаю эти цифры свойством самой реализации. Методики тоже не одинаковые: мой пробник считает аллокации всего процесса, включая пул потоков, а не одного вызова. Оба режима шли на одной модели процессора, AMD EPYC 9V74, так что разница в железе маловероятна. Для выводов об аллокациях беру стандартный запуск BenchmarkDotNet в отдельном процессе: эти значения совпали с опубликованными. Абсолютное время отличается между окружениями, и я считаю его результатом конкретного прогона, а не свойством .NET 11. Внутрипроцессные цифры оставляю отдельным наблюдением, которое ещё надо проверить. Практический вывод: сравнивайте замеры, запущенные одинаково, на одной версии SDK и рантайма, иначе вы меряете методику, а не механизм. Время на коротких цепочках вообще ненадёжно. У Тауба исключение на глубине 1 с приостановкой в runtime-async медленнее в 1,3 раза, а у меня быстрее: 7,709 мкс против 6,438 мкс. Байты в обоих прогонах те же: 1,68 KB против 1,4 KB. Второе: это микробенчмарки. Типовой запрос ждёт базу миллисекунды, а экономия измеряется наносекундами. Могу себе представить нагрузочный тест I/O-bound API, где с флагом латентность не изменится, и это будет честный результат. Заметнее эффект там, где много синхронно завершающихся await (кэши, парсеры, глубокие конвейеры), и в метриках GC, а не во времени ответа. Третье: фича в preview. Документация прямо просит присылать отчёты о пропускной способности и размере библиотек в обе стороны, положительные и отрицательные. До стабилизации производительность и детали поведения могут меняться от сборки к сборке. Переменные окружения DOTNET_RuntimeAsync и UNSUPPORTED_RuntimeAsync больше не поддерживаются, включение теперь через Features, отключение через UseRuntimeAsync=false. Четвёртое: всё, что опирается на внутренности старой схемы, стоит проверить. Кадров билдера в живом стеке у меня стало 0 вместо 3, и если в коде или в инструментах кто-то разбирает стек по именам вида <Method>d__0 или по кадрам MoveNext, их больше не найдут. Я бы проверил: самописные логгеры, которые склеивают стек в строку; правила группировки ошибок в системе мониторинга; диагностику, которая ищет конечный автомат через reflection. Пятое: поддержка не сплошная. Runtime-async работает с четырьмя типами возврата: Task, ValueTask, Task<T> и ValueTask<T>. По разбору Тауба, async void, async-итераторы и собственные task-like типы со своими билдерами пока остаются на классическом преобразовании. Для пограничных случаев, вроде by-ref локальных переменных и await в обработчиках исключений, сверяться стоит с поведением текущего компилятора, а не только с черновиком ECMA-335. Что проверить у себя Мой вариант порядка, если сервис уже на .NET 10 и переезд на .NET 11 запланирован: Обновить проект на .NET 11 без флага и снять базовые метрики: аллокации в секунду, паузы GC, p99 латентности. Часть выигрыша вы получите уже здесь, потому что BCL собрана в новом режиме. В отдельной ветке включить <Features>$(Features);runtime-async=on</Features> и прогнать те же замеры на той же версии SDK и рантайма, тем же способом. Так эффект обновления BCL отделяется от эффекта флага. Сравнивать аллокации, а не только время. Найти горячий путь с глубокой цепочкой await и написать на него бенчмарк BenchmarkDotNet с MemoryDiagnoser. Там будет видно, сливаются ли вызовы. Проверить всё, что читает стек: логирование, APM-агент, группировку ошибок. Если что-то ломается, лучше узнать до GA, а не после. Если встали ограничения, отключить фичу для проекта через <UseRuntimeAsync>false</UseRuntimeAsync> и оставить отчёт команде .NET. Сроки поджимают: по официальной политике поддержки .NET 8 и .NET 9 перестают поддерживаться 10 ноября 2026 года, а .NET 11 RC2 вышел 13 октября с go-live лицензией. Окно для экспериментов как раз сейчас. Вывод Самое интересное здесь, на мой взгляд, не наносекунды, а выбор архитектуры. Java с виртуальными потоками позволила во многих сценариях писать блокирующий код без async-сигнатур, переложив приостановку на JVM, хотя асинхронные API в экосистеме никуда не делись. Kotlin до сих пор строит конечный автомат в компиляторе, ровно как C# последние двенадцать лет. .NET в 2023 году отказался от зелёных потоков и пошёл третьим путём: модель программирования оставили прежней, а реализацию перенесли туда, где у неё больше информации. Для разработчика на C# это означает простую вещь: ответ «async — это синтаксический сахар над конечным автоматом» теперь неполный. Компилятор больше не выпускает привычный MoveNext, приостановку и возобновление организуют VM и JIT, и JIT может оптимизировать границы между вложенными async-методами, потому что видит цепочку вызовов. Для разработчика на JVM это хороший повод посмотреть на корутины Kotlin иначе: компиляторная схема не единственная, и платформа с похожей моделью только что показала, сколько она стоит. Ссылка на мой репозиторий с экспериментом Начать стоит с README.md: там все таблицы этой статьи, включая разницу между запуском в одном процессе и в отдельных. shared/Chains.cs — измеряемые цепочки. src/AsyncProbe/Program.cs — стек-трейсы и аллокации. bench/AsyncBench/Program.cs — бенчмарки. ./run.sh — собирает всё с нуля и сверяет результат с инвариантами из expected.txt. ИСТОЧНИКИРепозиторий с экспериментом What's new in .NET 11 runtime, Microsoft Learn, обновлено для RC2 Toub S., Performance Improvements in .NET 11, .NET Blog, 15.09.2026 Runtime-async, черновик изменений ECMA-335, dotnet/runtime Green Thread Experiment Results, dotnet/runtimelab #2398, 08.09.2023 .NET 9 Runtime Async Experiment, dotnet/runtime #94620 .NET and .NET Core Support Policy Announcing .NET 11 Release Candidate 2, .NET Blog, 13.10.2026 JDK 21, OpenJDK Kotlin Coroutines, KEEP Чтобы расти до уровня Senior C#-разработчика и браться за более сложные задачи, важно не только уверенно писать код, но и понимать, как работает .NET под капотом. На курсе «C#-разработчик. Продвинутый уровень» можно углубить эти знания, научиться находить узкие места в приложениях и принимать более обоснованные архитектурные решения. Пройдите вступительный тест, чтобы оценить свой текущий уровень, выявить пробелы и понять, какие навыки стоит подтянуть для следующего карьерного шага.

  9. Reddit · · r/dotnet · u/shanewas726 · dotnet/core

    CVE-2026-111497: a Brotli decoder bug forced an out-of-band .NET release (10.0.14) on Oct 25 Microsoft released .NET 10.0.14 on 25 October as an out-of-band security update, and the stated reason is CVE-2026-111497, an out-of-bounds write (CWE-787) in the Brotli decoder bundled with .NET. CVSS 8.1, publicly disclosed, exploitation marked more likely. Affected and fixed: - .NET 8 before 8.0.33 → 8.0.33 - .NET 9 before 9.0.22 → 9.0.22 - .NET 10 before 10.0.14 → 10.0.14 (SDK 10.0.114 or 10.0.403) - .NET Framework is not affected The same release completes the October 13 fixes for CVE-2026-70197 (SocketAsyncEventArgs) and CVE-2026-102870 (Blazor WebAssembly debugger), which had shipped incomplete, and adds CVE-2026-114148 for Linux source-build tarballs. Exposure needs a call path that decompresses attacker-controlled Brotli: request decompression with br enabled, HttpClient AutomaticDecompression including Brotli, or BrotliStream/BrotliDecoder on untrusted input. None of that is on by default, and response compression alone does not hit it. Until you update, dropping Brotli from those paths is a workable stopgap. Framework-dependent apps need the host runtime updated and restarted. Self-contained apps need a rebuild and republish, and the Blazor WebAssembly Server/DevServer packages have to be bumped separately because the shared runtime update does not cover them. Advisory: announcements#494 Release notes: github.com/dotnet/core/blob/main/release-notes/10…

    51 points · 11 comments
  10. Brotli CVE-2026-111497: Memory Corruption, Information Disclosure, and RCE via Crafted Compressed Streams 1. Basic Information - Article Name: Wednesday, October 21, 2026 Security Releases - Source: Node.js project - Published Date: October 21, 2026 - Original Source: Node.js project - Related Information Sources: GitHub Advisory: Python Brotli / brotlicffi, Istio Brotli Requirements, Envoy Brotli Advisory, BleepingComputer, JPCERT/CC, Microsoft .NET Advisory, Imperva, OpenAI - Related Malware, Threat Groups, CVEs, Products: CVE-2026-111497, Google Brotli, Node.js, .NET, Python Brotli, Bun, Envoy, Istio - Severity: High 2. Executive Summary The Brotli stream decoder incorrectly treats invalid block switching as a request for additional input rather than a permanent error, writing data beyond the 8-byte internal buffer. Decompressing attacker-controlled Brotli data can cause process crashes or disclose process memory, and may allow remote code execution (RCE) when additional conditions are met. Public proofs of concept and research demonstrations exist, but the reviewed public sources do not report malicious exploitation in the wild. 3. Attack Flow The following are separate paths leading to the same decoder vulnerability. Sending requests to a server and retrieving responses as a client are independent attack vectors and do not form a single mandatory sequence of steps. Server Decompression of Compressed Requests 1. An attacker sends a crafted Brotli stream as an HTTP request or API input. 2. An application or runtime decompresses the data using the vulnerable decoder. 3. Writing to the internal buffer continues even after invalid block switching, causing an out-of-bounds write. 4. This leads to process crashes, memory information disclosure, and, when memory layout and protection mechanism conditions are met, remote code execution (RCE) under the process privileges. Client Decompression of Compressed Responses 1. An attacker induces a backend client, crawler, proxy, AI agent, or similar entity to retrieve an attacker-controlled URL. 2. The client receives a response marked with Content-Encoding: br and automatically decompresses it. 3. The same decoder flaw is triggered inside the client process, potentially leading to crashes, memory information disclosure, and code execution if additional conditions are met. The following table organizes the attack vectors and investigation perspectives. RCE, subsequent child processes, and outbound communications do not occur in all environments. | Attack Stage | Attacker Action / Technique | Tools / Communications | Defender Indicators (Prerequisites / Artifacts) | | --- | --- | --- | --- | | Vector A: Server-Side Request Decompression (Initial Transmission) | Sends a crafted Brotli compressed stream to the server as an HTTP request or API input. | HTTP/HTTPS communication, POST/PUT requests, Content-Encoding: br | Operation of runtimes equipped with vulnerable decoders, and automatic request body decompression settings (such as Express middleware configurations) | | Vector A: Server-Side Request Decompression (Internal Processing) | The server application decompresses a stream containing invalid block switching, triggering an out-of-bounds write. | Internal decoder calls in Node.js, .NET, Python, Envoy, Istio, etc. | Decoder processing errors, process termination (crashes), segmentation faults (segfaults), core dumps | | Vector B: Client-Side Response Decompression (Initial Inducement) | Induces crawlers, backend integration processes, AI agents, etc., to access an attacker-managed URL. | Induced requests to external URLs, external API fetches | Automatic response decompression must be enabled in the component fetching the URL (e.g., built-in fetch()) | | Vector B: Client-Side Response Decompression (Internal Processing) | The client process decompresses the received malicious Brotli response, triggering the same vulnerability. | HTTP responses, Content-Encoding: br, client internal decoders | Abnormal termination of the client process, connection drops, service stoppages, generation of abnormal child processes | | Post-Exploitation (Remote Code Execution & Impact Spread) | Triggers code execution under process privileges or memory information disclosure when memory layout and mitigation conditions are met. | Unauthorized shell commands (e.g., /bin/sh), outbound communication, memory dumps | Suspicious child process launches from web server/client processes, unknown outbound communications | Running an HTTP server on a vulnerable Node.js runtime does not, by itself, cause received bodies to be decompressed with Brotli. A configured request parser or another component must actually decompress the input. Imperva demonstrated RCE on Node.js 24.22.0 (Linux arm64) through both the built-in fetch() response-decompression path and the request-decompression path in a configured Express/body-parser application. The same input was rejected by patched version 24.22.1. These results do not establish successful exploitation on untested builds or architectures. This vulnerability is not specific to HTTP/2. Although nicknames such as "HTTP2Shell," "Node2Shell," and "Broccoli2Shell" exist, the fundamental attack condition is a vulnerable Brotli decoder decompressing data controlled by an attacker. 4. Attacker Position and Execution Location For the server-side vector, the prerequisite is the ability to send compressed requests from the internet or an internal network. For the client-side vector, the requirement is the ability to cause the target to retrieve and decompress an attacker-controlled response. Memory corruption itself occurs inside processes such as Node.js, .NET, Python, or reverse proxies that invoke the Brotli library. 5. Visibility for Victims and Administrators - Victims: May perceive the issue as service unresponsiveness, connection drops, or 5xx errors. - Administrators: May detect decoder process segmentation faults (segfaults), core dumps, unexpected process restarts, or suspicious child process creation and external communications immediately following HTTP request processing. 6. Success and Failure Conditions Success Conditions - The vulnerable Brotli decoder binary is incorporated into the running process. - In .NET environments, this applies to Brotli-enabled request decompression, HttpClient automatic response decompression, or explicit use of BrotliStream / BrotliDecoder. Default configurations of ASP.NET Core and HttpClient do not automatically decompress, and .NET Framework is not affected. Envoy and Istio are also affected when the Brotli decompressor is explicitly enabled. - The target process actually performs decompression processing on attacker-controlled Brotli data. - Whether RCE succeeds also depends on the target version, CPU architecture, memory layout, and OS mitigations such as ASLR. Failure Conditions and Mitigations - Update to patched versions provided by respective vendors, such as Google Brotli (1.0.10, 1.1.1, 1.2.1) or Node.js (22.23.4, 24.22.1, 26.12.1). - Until patching is complete, disable or exclude Brotli request and response decompression processing enabled in existing environments. Microsoft advises against enabling previously unused decompression features as part of a mitigation. - Reject Brotli before decompression, following OpenAI's guidance. Simply removing br from Accept-Encoding or stopping outbound compression on CDNs or web servers does not protect incoming request or client-side response decompression paths. Even if decompression is stopped in Envoy or Istio, compressed data may flow to backend components, so remaining vulnerable decoders must also be protected. - After updating, restart related processes and ensure container images are rebuilt and redeployed. Updating only the operating system's Brotli library may leave vulnerable runtime-bundled copies unchanged. 7. What Happens Upon Success Successful exploitation can crash the decoder process (DoS), disclose process memory, or, when additional conditions are met, execute code with the process's privileges. In .NET environments, this may lead to privilege escalation depending on the application configuration and execution context. 8. Observable Logs The following are investigation ideas (Inference). Available logs vary by product and configuration and do not represent confirmed IOCs observed in active exploitation environments. Normal Content-Encoding: br traffic or data lengths alone cannot be used to conclusively determine an attack. - Email: No email-initiated attack activities specific to this vulnerability have been reported. - Proxy / SWG / DNS: Content-Encoding: br traffic, abnormal responses, and communication histories to backend fetch destinations. - Endpoint / EDR: Segfaults in Node.js, dotnet, Python, or proxies, core dumps, abnormal child process launches, and unexpected allocations of executable memory regions. - Identity / IdP: The reviewed sources do not report authentication IOCs specific to this vulnerability. Investigate possible service account abuse following successful RCE separately. - SaaS / Cloud: Container restarts, Pod CrashLoopBackOff states, spikes in load balancer 5xx errors, and differences in deployed image digests. - Network: Brotli streams chronologically matching decoder errors, repetitive communications from the same source, and request-response flows immediately preceding crashes. 9. Attack Success Determination If the only evidence is the arrival of crafted Brotli data or traffic matching a PoC fingerprint, classify it as an attack attempt. The occurrence of decoder errors or process crashes indicates potential memory corruption but does not immediately signify successful RCE. Confirm initial execution only when there is execution evidence, such as suspicious child processes launched by the decoder process, process telemetry matching attacker commands, or equivalent evidence. Imperva's RCE results were obtained in a research environment using Node.js 24.22.0 (Linux arm64) and specific test configurations; they do not establish reliable reproduction across all environments. 10. Investigation Playbook 1. Trigger: Crashes or segfaults in processes that decompress Brotli data, core dumps, detected PoC fingerprints, or suspicious child processes. 2. Initial Verification: Verify incorporated decoder copies, runtime and package versions, request/response decompression settings, and process restart times. 3. Endpoint Investigation: Preserve and investigate process trees before and after crashes, core dumps, memory faults, launched shells, download tools, and file modification histories. 4. Authentication & Cloud: Check service account and Workload Identity usage history, secret access logs, and cloud API operation logs. 5. Post-Exploitation Tracking: Track traces of credential access, persistence configurations, lateral movement, and data exfiltration. 6. Containment: Temporarily halt suspicious Brotli decompression processing, update to vendor-patched versions, restart processes, and rebuild container images. 7. Judgment Categories: Evaluate distinctly across stages including reachability, decoder errors, crashes, memory information disclosure, RCE, and subsequent compromises. 11. Defense and Detection Ideas - Single Event: Alerts for segfault occurrences in Brotli decompression processes coupled with preceding br encoding communications. - Chronological Correlation: Perform correlation analysis from receiving crafted compressed streams -> decoder errors or process crashes -> process restarts -> generation of suspicious child processes and outbound communications. - Proactive Threat Hunting: Cross-search SBOMs, container image layers, binary dependencies, and runtime-bundled modules to comprehensively identify vulnerable Brotli copies. - Logging Considerations: Identifying causal relationships becomes difficult if TLS-terminated plaintext payloads, client-side response headers, and process crash telemetry are not preserved. - Priority Controls: Prioritize applying vendor-patched versions, restarting processes, rebuilding and redeploying containers, and disabling unnecessary automatic Brotli decompression features. 12. Facts / Inference / Hypothesis Facts - The Node.js project explained the out-of-bounds write mechanism against the 8-byte internal buffer in Brotli and released patched versions. - JPCERT/CC provided guidance on affected versions of Google Brotli and the scope of impact on widespread embedded products. - Imperva demonstrated RCE under specific conditions in a research environment running Node.js 24.22.0 (Linux arm64). - The reviewed public sources do not report malicious exploitation in the wild. Inference - Not only web servers, but also backend clients and crawlers that retrieve data from external URLs and decompress Brotli responses serve as critical attack surfaces. - In asset management, it is necessary to track not only direct package dependencies but also container images, vendor-embedded libraries, and statically linked binaries. Hypothesis No additional hypotheses. Unconfirmed items are listed in "Uncertainties and Additional Investigation." 13. MITRE ATT&CK Mapping | ID | Technique | Confidence | Basis | | --- | --- | --- | --- | | T1190 | Exploit Public-Facing Application | medium | Inference: Mapped to the assumed path where internet-facing applications decompress Brotli requests controlled by attackers. This does not indicate observed active exploitation, and this basis does not apply to paths where clients decompress malicious responses or internal services not exposed externally. | 14. Uncertainties and Additional Investigation - The presence of active exploitation by malicious actors, patch availability status across all embedded products, and detailed RCE success conditions per product and architecture remain unconfirmed. The existence of public PoCs cannot be treated as confirmation of production environment compromise. 15. Impact on SOCs and Organizations Even for organizations that do not directly develop or operate Node.js or .NET internally, API clients, service meshes, proxies, Python packages, and container images bundling the Brotli library may be affected. Create software inventories from both public web services and batch processing tasks retrieving external URLs, and verify patch application and process restarts for each embedded copy. 16. Summary by Target Audience - For SOCs: Monitor process crashes, suspicious child process launches, and outbound communications occurring immediately after Brotli decompression processing by correlating them chronologically with request and response traffic. - For Administrators: Promptly apply patched versions provided by product vendors, and ensure process restarts and container image rebuilds/redeployments are fully completed. - For Users: Follow update guidance and restart procedures provided by service administrators.

    1 reaction

Achievements

Awards earned by the repositories dotnet owns. Select an award to embed its badge.

Gold
111st place finishes
Silver
132nd place finishes
Bronze
43rd place finishes
Awarded repositories
9Owned by dotnet now

Repositories

Ordered by gold, then silver, then bronze.

  1. coreclrBest: 1st by stars, 3,160 stars510
  2. aspnetcoreBest: 1st by stars, 2,140 stars300
  3. corefxBest: 1st by stars, 1,455 stars260
  4. wpfBest: 1st by stars, 880 stars120
  5. roslynBest: 2nd by PR authors, 19 PR authors020
  6. mauiBest: 2nd by stars, 1,021 stars011
  7. machinelearningBest: 2nd by stars, 678 stars010
  8. runtimeBest: 3rd by forks, 83 forks002
  9. winformsBest: 3rd by stars, 568 stars001

Every award

Top-three finishes of these repositories in completed trending days and weeks, newest first.

  1. roslyn
  2. aspnetcore
  3. aspnetcore
  4. aspnetcore
  5. maui
  6. maui
  7. runtime
  8. runtime

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.