Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
158227 stories
·
33 followers

Ten Days of Critter Stack Releases

1 Share

Most of the big improvements in this blog post came from JasperFx client engagements. Reach out any time to sales@jasperfx.net and we’ll happily chat with you about how we can help your shop succeed with whatever technical challenges you might have!

Let me put a stake into the ground here and say that you simply cannot (yet) vibe code yourself an equivalent of the “Critter Stack” because so much of our deep quality is the direct result of adapting to real life usages and problems over years of constant usage and continuous improvement. In the past we’ve worked with JasperFx clients or the community on issues caused by database maintenance shutdowns, way too many Kubernetes related things as pods spin up and down, and all kinds of unexpected real like episodes that have all directly led to real improvements in the tools. We’ve faced issues from sudden system surges due to unexpectedly big system inputs like import files. We’ve had to endlessly harden MartenPolecat, and Wolverine against infrastructure hiccups and random disconnects our users have faced in real life usage. You simply cannot get that level of built in quality by “rolling your own” over a long weekend.

The last ten days have been one of the heaviest release stretches in the history of the critter stack. Every repository in the family shipped, and the three headline stories are all about the same thing: what happens when a system is under real load, on real hardware, at real scale, and something goes wrong.

Here’s what moved:

PackageWhere we were on July 17Where we are today
Wolverine6.20.06.23.1
Marten9.16.09.20.0
Polecat5.1.05.7.0
Weasel9.16.49.19.0
JasperFx / JasperFx.Events2.28.02.36.2

That’s 4 Wolverine releases, 5 Marten releases, 6 Polecat releases, 3 Weasel releases, and 9 JasperFx releases in ten days. Below are the three things I most want you to know about, followed by everything else.


1. Marten’s Async Daemon Tells You Why It Stopped

This work was inspired by helping a JasperFx client troubleshoot issues last week

The single most frustrating failure mode in an event-sourced system is the silent one: a projection stops advancing, and the only evidence is a chart that flatlines. The daemon knew perfectly well what happened — it just had nowhere to put it.

Classified shard failures

This will be exposed through the CritterWatch user interface and MCP tools in the 1.0 RC release

The async daemon now classifies why a shard is paused or stopped and persists it, so a monitoring tool polling the database sees exactly what an in-process observer sees:

var states = await store.Storage.Database.AllProjectionProgress();
foreach (var state in states.Where(x => x.Failure != null))
{
    // ApplyEvent, EventSerialization, UnknownEventType, ProgressionOutOfOrder, or Other
    Console.WriteLine($"{state.ShardName}: {state.Failure!.Category} on {state.Failure.Event}");
}

ShardFailure is a plain, serializable record — category, the failing event’s sequence and type, the exception message and detail — deliberately not an Exception, so it survives the trip to a monitoring UI. Extended progression tracking grew four new columns to carry it (failure_categoryfailure_event_sequencefailure_event_typefailure_event_tenant_id), and failure_category stores the enum name rather than its ordinal so reordering the enum in a future release can never silently re-label rows an older deployment wrote.

The distinction between categories is the whole point. EventSerialization means a stored body won’t deserialize — you need a serializer or data fix. UnknownEventType means an event alias resolves to no known .NET type in this deployment — usually a missing registration or a rollback past the point where that event type was introduced. Those are different problems with different fixes, and the daemon now says which one you have. A shard that recovers clears its failure columns on the next successful start, so a supervisor built on this doesn’t keep alerting on something you fixed an hour ago.

Graceful shutdown and the drain timeout

This improvement will also help a great deal when Marten/Wolverine decides to rebalance work across a cluster of nodes as you might be scaling up or down

When a shard is stopped, the daemon doesn’t simply cancel it — it drains: lets the in-flight page of events finish applying, then flushes the progression row so the next start picks up exactly where this one left off. If that drain gets cut short, the shard restarts against a stale progression row and throws ProgressionProgressOutOfOrderException. That’s now bounded, per shard, and configurable:

// The default is 5 seconds
opts.Projections.StopAndDrainTimeout = 30.Seconds();

The motivating case is a database-per-tenant deployment with thousands of (projection × tenant) shards all trying to drain inside a Kubernetes termination grace window. A per-shard bound only helps if the process lives long enough to spend it, so pair a raised StopAndDrainTimeout with HostOptions.ShutdownTimeout and the pod’s terminationGracePeriodSeconds. Full write-up in Graceful Shutdown and the Drain Timeout.

The high-water health check became more effective

This part really only impacts users using the new per-tenant event store partitioning — but that’s going to be one of our answers for extreme scalability needs*

The high-water health check previously probed every database in a multi-tenanted store on every probe — a connection fan-out that gets ugly at a few hundred shard databases, and outright wrong when daemon distribution is spread across nodes and a node ends up probing databases it doesn’t host:

Services.AddHealthChecks().AddMartenHighWaterHealthCheck(
    staleThreshold: TimeSpan.FromSeconds(30),

    // Only probe the databases this node actually owns
    databaseFilter: db => LocallyOwnedDatabaseIdentifiers.Contains(db.Identifier),

    // Assert even under DaemonMode.ExternallyManaged (i.e. Wolverine-managed distribution)
    includeExternallyManaged: true);

Under UseTenantPartitionedEvents the check now evaluates the per-tenant HighWaterMark:<tenant> progression rows too, using the liveness heartbeat signal (the sequence-gap fallback is store-global and can’t be applied per tenant).

More reliable integration testing against asynchronous projections

This gobbledygook should really translate to “automated testing against asynchronous projections just got faster and more reliable”

Two long-tail concurrency bugs went with it: the high-water agent’s lost-wakeup race was closed rather than narrowed (jasperfx#572), and a WaitForShardState race against an already-published state was fixed (jasperfx#568). On the PostgreSQL side, Marten 9.20 added an allocation fence so an idle advisory-lock session can no longer hold gap skips open forever (marten#4953) — a fix that had a direct Wolverine counterpart, more on that below.


2. Wolverine’s Agent Assignment Got Its Hard Lesson

We’ve had confirmation from a JasperFx client that these changes made a dramatic improvement in how Wolverine behaved in a hugely complicated system, but I expect this to be an improvement for plenty of other users as well

This one started as an incident report (from a pretty extremely complicated usage well beyond what most people will ever experience) and turned into a nine-part fix.

The setup: a Wolverine cluster distributing thousands of Marten subscription and projection agents across nodes, under rebuild load. The symptom: Wolverine basically panicked and continuously tried to start, stop, and re-assign agents to diffent nodes because it couldn’t tell if anything was healthy or not. Nodes were being ejected while very much alive, resurrecting under new identities, and the leader re-sent the same assignments forever while nothing actually started. The database was taking roughly 96,000 telemetry inserts an hour from the churn alone — into the very database the rebuild was already saturating.

Nine separate defects fed that livelock. All of them are fixed:

The heartbeat was starved by its own work. The node heartbeat was written as the first step of the health-check loop, which also drained agent commands serially. A leader spending sixty seconds burning reply timeouts while starting thousands of subscription agents therefore delayed its own next heartbeat past StaleNodeTimeout — looking dead to its peers precisely when it was doing the most work. The heartbeat now runs on its own independent loop, so no amount of slow command work can starve it.

Resurrection restored a skeleton, not a node. When a peer deleted a still-live node’s row, every store blindly re-inserted a skeleton: fresh node number, empty capabilities, no assignments. A capability-less node is a candidate for nothing, so a 3-node cluster silently shrank to 2 for event-subscription work. MarkHealthCheckAsync now reports existence without ever inserting, and the controller re-registers with the node’s real number, its captured capabilities, and its agent assignment rows — implemented across all nine persistence stores (PostgreSQL, SQL Server, MySQL, SQLite, Oracle, RavenDB, CosmosDB, and the two in-memory/multi-tenanted wrappers).

A 2,100-agent batch answered by a 30-second timeout. Assignments went out as one mega-batch and were started serially on the receiving node — where each Marten subscription-agent start is a daemon shard spin-up with database round trips. That is hours of serial work answered by a 30-second reply window: the reply can never arrive, so the leader records nothing and re-sends the whole ~300KB batch next cycle. Batches are now chunked, started with bounded parallelism, and the reply timeout scales with chunk size.

The leader re-emitted assignments it had already sent. A leader-side pending-assignment ledger now suppresses duplicate AssignAgent commands for work already in flight, with a TTL so a start that never took still gets re-driven. That also removed the matching telemetry-write flood.

Ejection had no hysteresis. A single stale snapshot read — replica lag, a GC pause, an aggressive StaleNodeTimeout — was enough to delete a live node’s row, its in-flight envelope ownership, and its assignments. The irreversible delete now requires N consecutive stale observations, and a follower may never delete the leader’s row; only a node actually holding the leadership lock can do that.

Shutdown couldn’t finish inside a grace window. The node-shutdown drain stopped every local agent serially, so a node with thousands of shards got SIGKILLed mid-drain, abandoning unflushed daemon progression. It now fans out with bounded parallelism, passes CancellationToken.None deliberately (this is the shutdown path — a cancelled drain leaves agents half-stopped), and contains a wedged agent so it can’t abort its peers’ drain.

Every one of these knobs is on Durability, and the defaults are the ones we’d pick for you:

opts.Durability.AgentStartBatchSize        = 50;   // chunk size for assignment batches
opts.Durability.MaxAgentStartParallelism   = 10;   // bounded fan-out starting a chunk
opts.Durability.MaxAgentStopParallelism    = 10;   // symmetric, on the shutdown drain
opts.Durability.StaleNodeEjectionThreshold = 2;    // consecutive stale reads before ejection

Surfacing a paused shard

CritterWatch will use this new capability to help your systems be more resilient

Wolverine deliberately does not restart a shard the daemon paused on a poison event — restarting would fail on the identical event, so the shard would thrash instead of advance. But “we’re not going to restart it” is only defensible if you know. So Wolverine now surfaces it four ways: the agent’s health check reports the failure category, failing event, and root exception type; a NodeRecordType.AgentPaused record lands in the node-record log; IEventSubscriptionAgent.Failure exposes the ShardFailure directly; and there’s an observer hook:

public class AlertingObserver : IWolverineObserver
{
    public Task AgentPaused(Uri agentUri, ShardFailure? failure)
    {
        // Fires once per transition into the failed state -- not on every health check tick
        _alerts.Raise($"{agentUri} paused: {failure?.Category} on event {failure?.Event}");
        return Task.CompletedTask;
    }
}

Only the Other category — a database outage, a timeout, a transient bug, anything you can’t pin on a single event — is treated as potentially self-healing and auto-restarted by the stall detector. Details and the full category table are in When a Projection Fails; it applies identically to Polecat.

Agent start retries

An agent’s very first assignment can race the subsystems it depends on coming up — a subscription shard evaluated before its store’s high-water detection is running, for instance. Previously the loser of a sub-second startup race idled for a full CheckAssignmentPeriod. Now it retries locally first:

opts.Durability.AgentStartRetryAttempts = 2;                            // default; 0 disables
opts.Durability.AgentStartRetryDelay    = TimeSpan.FromMilliseconds(250); // default, × attempt number

See Agent Start Retries.

And the 6.23.1 follow-ups

Three fixes landed on top, all from running the fixed code against real deployments:

  • A pending assignment is now confirmed on delivery rather than on continued assignment — a 6.23.0 regression where a pause→restart cycle left the ledger entry unconfirmed forever.
  • Advisory-lock session hygiene for Marten’s gap-liveness gate, the Wolverine-side twin of marten#4953.
  • Agent restriction changes are merged and persisted before health detection is kickstarted.

3. Polecat Got Materially Faster

Hey, we’re serious about making Polecat a first class citizen within the greater Critter Stack

Polecat — the SQL Server document database and event store — spent this window on performance, and one of the finds was some serious egg on my face.

A one-word bug that cost 6x on string identities

String identity columns in Polecat (pc_streams.idpc_events.stream_idtenant_id, document ids, progression names, tag values) are varchar(250). String parameters were being bound as nvarchar. SQL Server’s data-type precedence rules then convert the column side, not the parameter — so every single id lookup became CONVERT_IMPLICIT(...) over a full index scan instead of a seek.

The numbers, at 50k streams under a SQL collation: StreamIdentity.AsString appends ran at 53/sec versus 304/sec for AsGuid. Version reads were 7,814µs across 990 reads versus 42µs across 3 reads. That’s not a tuning opportunity, that’s a missing index seek on every string-keyed operation in the store.

Every bespoke site that filters a varchar column now binds through AddVarChar/AddIdParameter helpers with a fixed size for plan-cache stability: version reads, FetchStreamFetchForWriting, document exists/metadata, batched loads, DCB tag queries, natural-key operations, the daemon loader and high-water detector, progression, HiLo, and the rebuild/delete admin paths. If you use string stream keys on SQL Server, upgrade to 5.6.0 or later — this one is free.

Server-side Select() projections

On SQL Server 2025’s native json type, a “simple” Select() projection — an anonymous type or DTO composed only of (optionally nested) scalar member accesses — is now translated to a server-side JSON_OBJECT(...) and streamed with no hydrate/reserialize step at all. Emitted keys honor your serializer’s naming policy and [JsonPropertyName]; numbers stay numbers and strings stay quoted.

Two correctness guards ship regardless of whether the optimization kicks in: a non-translatable Select() falls back to a client-side transform when materialized with ToListAsync() (never a silent drop), and attempting to stream a client-side-fallback projection now throws BadLinqExpressionException instead of silently ignoring the Select and returning raw documents.

Streaming paged JSON in one round trip

Both Marten and Polecat now have the full raw-JSON streaming result family, byte-for-byte compatible with each other so clients are interchangeable:

app.MapGet("/issues/paged/{pageNumber:int}/{pageSize:int}",
    (int pageNumber, int pageSize, IQuerySession session) =>
        new StreamPaged<Issue>(session.Query<Issue>().OrderBy(x => x.Description), pageNumber, pageSize));
{"pageNumber":3,"pageSize":25,"totalItemCount":1207,"pageCount":49,"hasNextPage":true,"hasPreviousPage":true,"items":[...]}

The total row count rides along on every row via count(*) OVER() in the same query that fetches the page — so count and documents both come from a single database round trip — and the documents inside items are the already-persisted JSON, streamed straight through with no deserialize/serialize.

For infinite scroll and export feeds, StreamPagedByCursor<T> does keyset (seek) pagination with an opaque, versioned cursor, at constant cost regardless of depth:

app.MapGet("/issues/feed", (string? cursor, IQuerySession session) =>
    new StreamPagedByCursor<Issue>(
        session.Query<Issue>().OrderBy(x => x.Description).ThenBy(x => x.Id), cursor, pageSize: 25));

The terminal ordering key must be the document identity so the ordering is a total order — that’s enforced, not assumed. See Keyset (Cursor) Pagination and Polecat’s paging docs.

Marten 9.20 rounded the family out with StreamEventState and StreamEvents for streaming a single stream’s metadata and raw events (docs).

Batched event fetches

FetchStreamStatePlan and FetchStreamPlan landed in both Marten and Polecat, and Polecat gained a batched event surface it simply didn’t have — batch.Events, with FetchStreamState and FetchStream in Guid and string overloads. Both new batch items compose their SQL from the same canonical column projections and hydrate through the same readers as the standalone path, so batched and standalone can’t drift apart across a schema migration. See Batched Queries.

Native SQL Server 2025 JSON indexes

One JSON index covers many paths at once and accelerates JSON_VALUE equality, JSON_PATH_EXISTS, and JSON_CONTAINS — with no per-path computed columns:

opts.Schema.For<User>().JsonIndex(x => new { x.UserName, x.Department });
opts.Schema.For<Document>().JsonIndex();                                    // whole-document
opts.Schema.For<Article>().JsonIndex(x => x.Tags, i => i.OptimizeForArraySearch = true);

This is the SQL Server counterpart to Marten’s GinIndexJsonData(), and it requires the native json column type (SQL Server 2025) — Polecat throws a clear error rather than emitting invalid DDL if you configure one against nvarchar(max). Covering indexes landed alongside it: Index(..)/UniqueIndex(..) now carry extra members as non-key INCLUDE columns so a query can be satisfied from the index alone. JSON Indexes docs.

Polecat also picked up per-tenant managed partitioning for documents and streams, AggregateToManyAsync(), the HasTag DCB tag operator in LINQ Where(), and tenant-scoped event/tag explorer reads.


4. Weasel Generates EF Core Migrations Now

I myself strongly prefer the “it just works” style of migrations that Marten and later Wolverine and Polecat do, but hey, a large plurality of the .NET community is probably very used to EF Core migrations, so we’re allowing our users to jump on board that train too!

This is a bigger deal than its version number suggests. Weasel 9.18 can emit standard, compilable EF Core migration files from its own schema model — the reverse of the mapping direction it already had. Instead of Weasel applying schema changes itself via db-patch/db-apply, your team applies them with the tooling it already standardized on: dotnet ef database update, idempotent SQL scripts, migration bundles, and versioned migration files a DBA can review.

dotnet run -- db-ef-migration add AddOrderProjection

That writes migration classes with real Up() and Down() bodies, a stub DbContext per database (with __EFMigrationsHistory relocated into the critter-stack schema so it can’t collide with your application’s own EF context), and a weasel-schema-snapshot.json that the next add diffs against. Everything flows through one door — IDatabase.AllObjects() — so Marten system tables, Wolverine envelope storage, Polecat event storage, and EF-projection tables all generate the same way. Verified end-to-end on both EF 9 and EF 10.

Docs: EF Core Migration Generation and Migration Coexistence.

Weasel 9.19 also gave Oracle a first-class command builder with real statement splitting — which is what unblocked Wolverine’s Oracle durability agent running through the shared batching mechanics. ODP.NET has no DbBatch support and won’t execute ;-separated statements in a single command, so this had to be solved at the Weasel layer.

One consistent finding across the decade plus of Critter Stack development is that database query batching is very frequently advantageous for performance, and we’ve taked that very seriously over the years


5. JasperFx: The Shared Core

Nine JasperFx releases in ten days, because it’s where the shared event-store and daemon abstractions live. The highlights, most of which you’ve already met above through Marten and Polecat:

  • ShardFailure + ShardFailureCategory (jasperfx#565/#567) — the classified reason a shard paused, exposed on ISubscriptionAgent and persisted through extended progression.
  • StopAndDrainTimeout (jasperfx#564) — the configurable per-shard drain bound.
  • HighWaterAgent liveness heartbeat (jasperfx#539) — a staleness surface and a local restart seam, which is what the improved health check reads.
  • Lost-wakeup race closed (jasperfx#572) and the WaitForShardState race against an already-published state (jasperfx#568).
  • Batched extended-progression writes (jasperfx#553/#554) — per-database-flush-interval batching, so the telemetry write path stopped being a per-shard-per-tick insert.
  • Natural key extraction widened to bind IEvent<T> sources, stop fabricating aggregates, and fail loudly rather than silently (jasperfx#569).
  • DCB workIDcbAggregateRegistry for runtime discovery, serializable rich EventTagQuery as a DCB source, and a step-instrumented aggregation fold with MultiAggregateProjectionResult.
  • F# support got more robust tuple/record handling and a DerivedVariable reference-propagation fix.

6. Everything Else

A partial list, because the window was busy:

Wolverine

  • Claim checks got size-threshold auto-offload, per-message/per-endpoint store selection, and honor a DI-registered IClaimCheckStore.
  • GCP Pub/Sub: named-broker support for sharded/partitioned topics, plus ListenToPubsubSubscriptionOnNamedBroker.
  • Conventional routing no longer ignores named brokers.
  • Oracle: the durability agent runs through the shared batching mechanics; the durable inbox binds RAW(16) Guids correctly; the message store URI uses the registered wolverinedb agent scheme.
  • Redis: scheduled retries no longer vanish on an unreadable timestamp, and entries that repeatedly fail to deserialize get dead-lettered instead of looping.
  • NServiceBus interop: the EnclosedMessageTypes header is split before resolution, shared across Azure Service Bus, SNS, SQS, and the database transports.
  • HTTP: a raft of OpenAPI and binding fixes — [FromQuery] on arrays and collections, case-insensitive enum array parsing, 415 instead of 404 when no Content-Type reaches an [AcceptsContentType] route, no duplicate description of route-bound [FromQuery]/[FromHeader] parameters, fail-fast when an endpoint advertises a body its HTTP method can’t carry, and explicitly-routed chains mapped inside the constructor so PublishMessage/SendMessage endpoints get their metadata.
  • IHost.ClearAllWolverineStorageAsync(), and resources setup provisions message storage even under AutoCreate.None.
  • Exclusive listener inboxes are now recovered on the listening node.

Marten

  • TimescaleDB support — projection and document hypertables, folded into core Marten.
  • Natural keys hardened: the previous key row is retired when the key changes, and the foreign key guard is scoped to its own table.
  • Simple LINQ Select() projections translate to jsonb_build_object (the Postgres side of the same optimization Polecat got).
  • ETag / If-None-Match (304) support on StreamOne and StreamAggregate.
  • Tenant-scoped event and tag explorer reads.

Upgrading

In this case, everything in the critter stack moved in lockstep — Wolverine 6.23.1 pins Marten 9.20.0, Polecat 5.7.0, and JasperFx 2.36.1+, so upgrading Wolverine pulls the rest forward for you. If you’re on a Marten-or-Polecat-only application, take Marten 9.20.0 / Polecat 5.7.0 directly.

Nothing here is a breaking change. The agent-assignment work is entirely behavioral and needs no configuration to benefit from; the new Durability knobs exist for tuning, not for opting in. The classified shard-failure columns require extended progression tracking, which is still off by default — turn it on with Events.EnableExtendedProgressionTracking if you want database-visible per-shard health, and note that the per-tenant high-water health check needs it too.

If you’re running the critter stack at any real scale, CritterWatch consumes all of the new failure surfacing described above without any work on your part.


Closing Thoughts

I would dearly appreciate it if the world could slow down a bit in the next couple weeks so that release cadence can come back to Earth. I’d also appreciate it if everybody else could chill out a bit in their OSS activity so GitHub actions can be more performant and responsive for me and the Critter Stack community!





Read the whole story
alvinashcraft
55 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

GitLab 19.2 Adds CLI

1 Share
Read the whole story
alvinashcraft
55 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Top Common Handlers in .NET MAUI

1 Share

Connect your .NET MAUI cross-platform controls with corresponding platform-specific controls using handlers. Here are some example use cases where handlers are most useful.

Handlers in .NET MAUI are the way to connect cross-platform controls with their native views on each platform. They offer a clean, decoupled and high-performance design. In this article we will analyze some scenarios where they are especially useful to solve certain problems. Let’s go!

Understanding Handlers in .NET MAUI

A handler in .NET MAUI is a special class that maps a cross-platform control to its native control. When we create the user interface in XAML, we use these cross-platform controls to build interfaces inside content pages, which are translated to native controls. For example, a .NET MAUI Entry control is mapped to an Android AppCompatEditText control, an iOS / Mac Catalyst UITextField control and a TextBox control on Windows.

At this point, you may wonder about the usefulness of handlers in your projects. The answer is that you can use them to customize controls and solve a range of problems: changing the default appearance of controls that cannot be done from XAML controls, accessing the properties of each native control, allowing you to change behavior to perform validations, etc.

Handlers can be used to:

  • Remove or modify elements of a control (like an underline, border, etc.)
  • Add functionality to a control (such as search capabilities)
  • Create custom controls

Knowing about handlers is going a step further in your technical skills in .NET MAUI, so I highly recommend learning about them in depth.

Ways to Customize Handlers in .NET MAUI

In .NET MAUI, there are several ways to customize a handler, including:

  • Mapper.AppendToMapping(): To perform customizations after the default mapping
  • CommandMapper.AppendToMapping(): To intercept actions
  • HandlerChanged: For a specific instance

In the above list you may have noticed the term Mapping, which refers to a dictionary of configurations that define how a control is rendered.

The way we connect to the handler will define which instances in the application we will affect through the handler. Let’s do some practical exercises to better understand them, solving common but simple problems in customizing native controls.

For these demonstrations, I created a new project using the .NET MAUI App template without sample content. Next, I created a folder called Handlers and inside a static class called HandlerCustomizations:

public static class HandlerCustomizations
{
    public static void RegisterHandlers()
    {
        
    }
}

Finally, you need to register the new class in Program.cs as follows:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureMauiHandlers(_ =>
            {                
                HandlerCustomizations.RegisterHandlers();
            })
            ...
    }
}

With the above ready, we are now prepared to analyze common use cases for handlers.

Removing Underline or Border from an Entry

Let’s start with the most used handler across the .NET MAUI ecosystem. The problem is that .NET MAUI does not currently expose a property that allows removing the default lines on each native platform. For example, on Android a line appears under the text, on iOS a gray rectangle is shown and on Windows a border.

The handler code looks like the following:

        Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("BorderlessEntry", (handler, view) =>
        {
#if ANDROID
            handler.PlatformView.BackgroundTintList =
                Android.Content.Res.ColorStateList.ValueOf(Android.Graphics.Color.Transparent);
#elif IOS || MACCATALYST
            handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
#elif WINDOWS
            handler.PlatformView.BorderThickness = new Microsoft.UI.Xaml.Thickness(0);
#endif
        });

In the code above, we access the control mapper Entry, which is the configuration dictionary, and add an extra customization called BorderlessEntry. You can also notice a callback that handles two parameters: handler, which is the access to the native control, and view, which is the Entry of .NET MAUI.

In each conditional compilation directive, a different behavior is specified according to the platform, using handler.PlatformView, and the native properties of each control. In the case of Android, we change the border color to transparent using BackgroundTintList. On iOS, the border is completely removed using the native property BorderStyle. And on Windows, BorderThickness is used to indicate a border thickness of zero.

Borderless input control displayed on iOS and Android

In the image above you can see the Entry control used without an underline on Android, which allows combining with other controls to improve the visual appearance.

Modifying Properties of a Button

Sometimes applications require a controlled visual style for buttons. For example, on Android the normal behavior is to add elevation and a ripple effect to all buttons. In .NET MAUI, there is no way to control these values natively directly on the control, so we can choose to use a handler again:

Microsoft.Maui.Handlers.ButtonHandler.Mapper.AppendToMapping(
    nameof(Microsoft.Maui.IButton.Background), (handler, view) =>
{
#if ANDROID
    handler.PlatformView.StateListAnimator = null;
    handler.PlatformView.Elevation = 0;

    var rippleColor = Android.Graphics.Color.Argb(80, 255, 0, 0);
    var colorStateList = Android.Content.Res.ColorStateList.ValueOf(rippleColor);

    if (handler.PlatformView.Background
                        is Android.Graphics.Drawables.RippleDrawable rippleDrawable)
        rippleDrawable.SetColor(colorStateList);
#elif IOS || MACCATALYST
            handler.PlatformView.Alpha = 1.0f;
            handler.PlatformView.AdjustsImageWhenHighlighted = true;
#endif
});

In the code above, these Android properties are modified:

  • StateListAnimator: Removes the elevation animation when pressed
  • rippleDrawable.SetColor: Overrides the ripple effect color

When running the application, we can see a customized ripple effect according to the defined ink color:

Native button with customized ripple effect

The previous handler demonstrates how to change default control effects using native properties.

Select All Text When Receiving Focus in an Entry

The following handler, which proves extremely useful, allows users to select all text in a Entry control when focusing, to then perform actions such as deleting the text, copying it, etc.

Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(
    "SelectAllOnFocus", (handler, view) =>
    {
#if ANDROID        
        handler.PlatformView.FocusChange += (_, e) =>
        {
            if (e.HasFocus)
                handler.PlatformView.Post(handler.PlatformView.SelectAll);
        };
#elif IOS || MACCATALYST        
        handler.PlatformView.EditingDidBegin += (_, _) =>
            Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(
                () => handler.PlatformView.SelectAll(null));
#endif
    });

The method Post on Android allows queuing the select-all action so the text selection is not overwritten. On the other hand on iOS the method SelectAll is invoked to carry out the selection, in the subscription to the EditingDidBegin event. This is because there is no FocusChange event similar to Android’s.

Text inside input automatically selected when focused.

Disabling the Keyboard of an Entry Control

The following useful .NET MAUI handler is the one that allows disabling the keyboard of an Entry control. Disabling keyboards is essential when creating applications where we will have a custom keyboard, such as a calculator app or a PIN input, and we want to show text input controls without the on-screen keyboard appearing.

To implement it, we will do it as follows:

Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(
    "DisableKeyboard", (handler, view) =>
    {
        if (view.AutomationId != "no-keyboard") return;

#if ANDROID        
        handler.PlatformView.ShowSoftInputOnFocus = false;
#elif IOS || MACCATALYST        
        handler.PlatformView.InputView = new UIKit.UIView();
#endif
    });

In the previous code, in the Android section the property ShowSoftInputOnFocus with a false value prevents the keyboard from appearing when focusing the field, while on the iOS side, InputView replaces the keyboard with an empty view, preventing the keyboard from appearing.

DatePicker/TimePicker: Text Alignment

A common case that users want in mobile applications is to center the text in the DatePicker and TimePicker controls, which is not possible to do natively from XAML code in .NET MAUI.

To achieve this, we can once again resort to handlers. On this occasion, the code used will be the following:

Microsoft.Maui.Handlers.DatePickerHandler.Mapper.AppendToMapping(
    "CenterDateText", (handler, view) =>
    {
#if ANDROID
        handler.PlatformView.Gravity = Android.Views.GravityFlags.Center;
#elif IOS || MACCATALYST                    
        handler.PlatformView.TextAlignment = UIKit.UITextAlignment.Center;
#endif
    });

Microsoft.Maui.Handlers.TimePickerHandler.Mapper.AppendToMapping(
    "CenterTimeText", (handler, view) =>
    {
#if ANDROID
        handler.PlatformView.Gravity = Android.Views.GravityFlags.Center;
#elif IOS || MACCATALYST
        handler.PlatformView.TextAlignment = UIKit.UITextAlignment.Center;
#endif
    });

In the handler, the property Gravity is used on Android for both DatePicker and TimePicker, which allows aligning the text to the position we need. In the case of iOS, the TextAlignment property is used, which allows the same result:

Centered text within DatePicker and TimePicker controls

Without a doubt, it is a small handler that can solve a common visual problem.

Entry with Native Validation

Let’s move on to a more complex case related to the usefulness of handlers. Imagine a scenario where you need to apply some kind of validation to all controls in the application. To achieve this, you can combine the use of events like TextChanged on Android and EditingChanged on iOS:

Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(
    "NativeEmailValidation", (handler, view) =>
    {                    
#if ANDROID
        handler.PlatformView.TextChanged += (_, e) =>
        {
            var text = e.Text?.ToString() ?? string.Empty;
            var isValid = System.Text.RegularExpressions.Regex.IsMatch(
                text, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");

            handler.PlatformView.Error =
                (isValid || text.Length == 0) ? null : "Invalid Email";
        };
#elif IOS || MACCATALYST
                    handler.PlatformView.EditingChanged += (_, _) =>
                    {
                        var text = handler.PlatformView.Text ?? string.Empty;
                        var isValid = System.Text.RegularExpressions.Regex.IsMatch(
                            text, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");

                        handler.PlatformView.Layer.CornerRadius = 4f;
                        handler.PlatformView.Layer.BorderColor = (isValid || text.Length == 0)
                            ? UIKit.UIColor.Clear.CGColor
                            : UIKit.UIColor.SystemRed.CGColor;
                        handler.PlatformView.Layer.BorderWidth =
                            (isValid || text.Length == 0) ? 0f : 1f;
                    };
#endif
    });

Within the lambda expression that handles the text change, a regular expression validation is performed. In this example, an email address is validated to be correctly written, but any other validation can be carried out.

Finally, in the Android case, the Error property is used to indicate there is an error, while on iOS the control is colored in a more customized way:

Server handler validating an email address input

The above approach leads us to imagine scenarios with more complexity and related to events, such as saving to a database after editing a text, preventing prohibited words from a dictionary, etc.

Conclusion

Throughout this article, you have been able to see the usefulness of handlers in real, everyday scenarios. They are a controlled way to modify native properties of controls without having to wrestle with native classes on each platform.

As a final point, in my experience, if you need a higher degree of customization and want something that has been tested across all platforms, I recommend that instead of creating handlers you use prebuilt controls that have the capabilities you need, such as those from the Progress Telerik UI for .NET MAUI suite of controls, which include the functionalities mentioned in the article and many more, without the need to create custom handlers. See you in the next article!

Read the whole story
alvinashcraft
55 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

From research to reality: An interview with Microsoft VP of Security Research Taesoo Kim

1 Share

More than 100 billion tokens. That’s what Team Atlanta spent using a single OpenAI model during DARPA’s Artificial Intelligence Cyber Challenge (AIxCC)—and they didn’t limit themselves to just that model, so the total number of tokens the team used during the two-year competition was well north of that figure.

AIxCC saw some of the best and brightest minds in cybersecurity designing, testing, and improving new AI systems to identify and patch software vulnerabilities in record time. In keeping with the challenge’s goal of advancing the field of cybersecurity, all seven finalist teams open sourced their projects to benefit researchers anywhere.

But it was Taesoo Kim and other members of Team Atlanta who would join Microsoft to turn that research into enterprise-grade product reality.

We sat down with Kim, now a VP of Security Research at Microsoft and leader of the new Microsoft Security FORGE (Frontier Offensive Research & Generative Exploration) Labs, as well as a professor at Georgia Tech, to learn how the research concepts Team Atlanta explored during AIxCC went on to inform the Microsoft Security multi-model agentic scanning harness (codename MDASH), the role that MDASH plays in Microsoft’s broader DevSecOps story, and what comes next.

CL Command Line

Let‘s jump right into it. What brought you to Microsoft?

TK Taesoo Kim

Oh, thats a great question to start. I joined Microsoft about six months ago. After our team, Team Atlanta, won DARPAs AIxCC competition, we thought about which company to join, and Microsoft was one of those companies where we found more opportunity for the future, particularly in the context of finding vulnerabilities, right? Microsoft has a huge assetnot just proprietary software like Windows. They also have a platform like GitHub thats literally one of the worlds biggest code repositories that you can get access to. So enabling any technologies in that context would be a huge career achievement for our team.

Myself and several members of the team joined Microsoft together. Our team consists of many of my PhD students at Georgia Tech, as well as former PhD students and professors there. We all joined as a single team and competed for the DARPA Grand Challenge.

CL Command Line

Oh, very cool.

TK Taesoo Kim

We’re really building a team that enables AInative vulnerability research at Microsoft. And we’re launching the Microsoft Security FORGE (Frontier Offensive Research & Generative Exploration) Labs so we can focus more, take advantage of frontier AI models, with the goal of completely automating the process as researchers, so that we can ultimately help advance the work. We’re really pushing the boundaries of finding and fixing zero-day vulnerabilities.

CL Command Line

Following your PhD in computer science at MIT, you joined Georgia Tech as a professor, and then you branched out to the corporate world with Samsung and now Microsoft. What differences have you come across, working for large corporations compared to working in academia? And are there any similarities that people would find surprising?

TK Taesoo Kim

So I would say 20, 30 years ago, universities were at the frontier. They did the latest research, and then industry was following. But lately, the gap between this frontier research and what industry can do has become extremely slim. For example, at Samsung, I was developing integrated SIM cards, which is one of the operating system layers in Galaxy devices, so that they can host multiple SIM card operations inside the kernel. We took advantage of the latest research, like instead of using C, we used Rust because, at that time, this was state of the art. We brought those technologies together and applied them in the context of industry.

Similarly, with AI, this gap between academia and industry, it’s almost none. Our industry often has incredible resources, more than you can imagine, right? Data, compute power, engineering, the backend. That actually makes us excited about the opportunity. In just six months, we could design the project that we’d like to pursue, conduct the research, make a prototype, and then finally arrive at a production-ready system. That’s because of all the support that Microsoft can provide. As long as you do great research, you can convince the people inside, your colleagues. And if they see the opportunity for industry, then they’ll pour all these resources into the effort so it’s extremely successful.

In terms of finding the proper problem that motivates you, what we call an intellectually challenging problem, we’re never short of those in industry or in academia. But here, were ready to tackle them if necessary. And we don’t worry too much about funding. But the problem is what’s really important. 

CL Command Line

Let’s switch gears to talk about MDASH. For those who are unfamiliar, whats the elevator pitch?

TK Taesoo Kim

MDASH is what we call Microsoft’s multi-agent, multi-model vulnerability discovery and remediation agent. Given a huge repository, it’s going to find vulnerabilities and propose fixes. But it can take advantage of multiple models.

It’s not bound to a particular model like Mythos, so we take advantage of the latest and greatest models all together. Not only that, we have more opportunity for token efficiency because of the way we constructed the system. We have more opportunity to take advantage of certain models in specific ways.

CL Command Line

Right.

TK Taesoo Kim

We can use the right model for the right job. Not only that, one of the key ideas of MDASH is to have a dynamic validation model, or proving stage. So we’re not just finding the vulnerability in a static wayyou can just ask an LLM to find the bugs. We actually validate them through discussions among multiple personas we created. They debate whether its a bug or not. And then we also pushed the capability to generate an exploit at the end, so that, when we say there’s bugs, theres proof associated with it, so that the security auditor and developer can verify the existence of the vulnerability by actually reproducing it.

CL Command Line

Thats a great segue to my next question. MDASH is orchestrating these 100+ specialized agents that are challenging each other. Tell us more about the decision to have multiple models in the room, so to speak, working together, debating, and validating the what and the severity of what they find. And Im curious if that was your first approach or if you arrived at it through trial and error.

TK Taesoo Kim

The design behind the 100+ specialized agents is tightly related to our lessons learned when we’re interacting with agents. When youre working with Copilot or any type of coding agent, you can say, ”Hey, find bugs. And it does find bugs, but it takes a long time. It doesnt know what type of bugs to look for. It doesn’t know what I want, and it spends a lot of time figuring out what a bug even is, right?

But you can specify what you want in much finer-grained detail and just be very specific about what you want to find. For example, you could say, “Hey, given the past vulnerability A, can you find a similar bug? That’s the best way to formulate what you want to achieve out of a single agent, but MDASH has more than 100 specialized agents, and it follows a similar philosophy. Each one of the agents has very specific guidelines. I dont care about the rest of the bugs, but I really care about this one particular type of vulnerability.

When you can provide fine-grained controls and scope, it accelerates the performance significantly. But unfortunately, if you combine all of them as a single prompt, it has what we call cognitive load around the work that it has to do.

CL Command Line

Right, right.

TK Taesoo Kim

The model gets distracted, you know? ”Hey, is that really a bug in terms of other bug contexts? Its self-debating all that stuff in a way where it couldnt find the bug in the end. We like to make sure that the scope that each model is working on is well isolated, but at the same time, the models don’t have tons of cognitive load in their context windows so that they can really focus on and deep dive into it.

CL Command Line

Thats really cool. And then what did the process of designing MDASH teach you about decomposition? Are there parts of vulnerability discovery that really benefit from that specialization, or are there parts that still require more of a general reasoning model?

TK Taesoo Kim

We still have a general reasoning model behind the scenes as well, which is activated all the time. In fact, more than 50% of the total bugs are caught by that non-specialized model, but these are relatively shallow types of vulnerabilities or one of the weakest points in the code repository. With a specialized agent, youre looking for a very particular vulnerability that otherwise youre going to miss it.

Our system is designed in such a way that we can assemble all these results together, so you can just unite all these reports and findings in a way that we can push back or push on to the next stage of validation at the end of the scanning. One nice thing about this is that we provide a system where we can assemble everything, where each models’ job is to find as many vulnerabilities as possible, but then we have a nicer system to deduplicate and combine them together in a way that we can enable the rest of the pipeline very seamlessly.

When were designing those specializations, we take advantage of the past vulnerabilities and corresponding patches. We study any missing gaps between what we can find with the existing vulnerability as a specialized agent and what type of bugs that we observe in the wild but couldn’t find with the agent. Then we create a research agent so we can extract the specialized agent out of that data. Our system supports this accumulation. You can just add another agent, and our system works fine.

CL Command Line

A lot of AI coding and security demos stop at generating a plausible finding, but MDASH is emphasizing proof of exploitability before it gets to a human. What does proof mean in the system, and how did you design the pipeline to avoid mistaking plausible reasoning for a real vulnerability?

TK Taesoo Kim

Most static scanning merely complains about, ”Hey, theres a potential bug in this code snippet.But unfortunately, it doesnt necessarily mean that an attacker can reach those locations or that you can formulate the constraint in a way that you can trigger those buggy conditions. So in order to achieve this actual exploitability, we like to solve these two problems: reachability and constraint.

Our proving agent provides an input to the program that resolves the reachability issue and triggers the vulnerability at the end. What I mean by this is that, if theres a PDF parser render, then we actually create the PDF document in a way that the PDF viewer crashes because of the input that we created, then craft the PDF file that we created as a proof. By crashing your software, by launching the PDF viewer, you can say, ”Hey, this bug exists. I dont know exactly what it is, but because of the fact that the program crashed, I know that indicates the existence of that vulnerability.

CL Command Line

Were there any really challenging or non-obvious architectural decisions that you had to make along the way, or any interesting or difficult tradeoffs that you can talk about?

TK Taesoo Kim

In the validation phase, we came up with the idea of debate. At that time, it was kind of a novel at the end. It was also based on our lessons as security operators. What I mean by this: When we discover a vulnerability, when we say theres a bug, we create a bug report and show it to the developer. The very first response from the developer is often, ”Hey, that’s probably not a bug. Thats the intended behavior because I documented it.“ Even though the program crashed, they documented it, right? This is common behavior. So we created an agent that mimics the behavior of a software developer. It can be defensive.

CL Command Line

Right.

TK Taesoo Kim

So that agent defends against the argument that, according to the design, this is a bug. It might say that the other agent made a mistake and didnt use it in the right way. But at the same time, another agent takes the offensive perspective, the offensive research perspective. It tries to convince them, “This is still worth fixing because it has a security implication.

So we created a persona that represents the perspective of the exploit writer and another that represents the perspective of the software developer, and we make sure that they reach a consensus at the end, so that were validating certain findings that we have. Either all of the personas that we create reach the consensus, or they feel less severe, less confident about the bug.

CL Command Line

So when agents disagree, theres like a voting mechanism that takes place and its sort of majority rules. Is that right?

TK Taesoo Kim

Yup. Exactly. So at least we can say this is debatable, but we’re not super confident. We can then mark the finding as less confident, so the rest of the pipeline spends more energy to validate those findings after the validation phase.

CL Command Line

If you were explaining the architecture to other security researchers, where do traditional program analysis techniques still matter the most? And where do you see LLMs or agentic reasoning really changing the game?

TK Taesoo Kim

Many of the traditional techniques, like analyzing core graph, like CodeKL tools, they understand that some of the data flow in program analysis perspective. Whenever we can take advantage of them, we have to because its much faster. In order to resolve the core graph, LLMs consume every single piece of source code and analyze it. Its extremely expensive, but traditional tools are extremely fast and cheap. So whenever possible, we have to take advantage of them, including core graph generations and data flows and stuff. We provide them as a database and tool in a way that the agent can decide whether we need those analyses separately from the LLM itself.

CL Command Line

Are there any classes of bugs or environments that are still really difficult for agentic systems today? And if so, what would need to change for AI systems to handle them well?

TK Taesoo Kim

There are many types still. For example, side channel is one thing because they have to reason about potential performance characteristics and the codebase’s actual implementation. This is a very difficult area. Another one, I would say spec-driven vulnerability is a logical error, but tightly bound to the certain spec. For example, if you didnt correctly implement the spec, theres a potential vulnerability there. The LLM has to reason all these together. Theyre not really good at it because the spec itself is extremely formal.

LLMs are designed to be less formal and handle this, I would say, unstructured input, unstructured code as your input, but you have to juggle between this very formal wording and unstructured wording at the same time.

CL Command Line

Jumping around a bit, how do you see MDASH fitting into today’s broader Project Perception announcement? Is it one layer of a larger system? How does it relate to the overarching vision for cybersecurity in the agentic AI era here at Microsoft?

TK Taesoo Kim

MDASH can be plugged in at a very early phase of Project Perception, meaning that we identify potential vulnerabilities in the repository, and then Project Perception can take advantage of this information. So if MDASH says, ”Hey, theres a potential bug in here because we know those from the code repository,” Project Perception makes sure that it actually exists in the actual environment that they deploy in and understand the whole network topologies. It has multiple sources of information that theyre taking advantage of, and MDASH‘s information is one of the very high-confidence, high-fidelity signals to Project Perception.

CL Command Line

And how, if at all, did the insights generated by your work on MDASH help inform the development and training of MAICyber1-Flash, which is Microsoft’s first cybersecurity specialized model?

TK Taesoo Kim

We’re closely collaborating with the Microsoft AI team in terms of how to construct the reinforcement learning environment. And also we provided the datasetthis is the past scanning information from internal projects. We discussed how to best utilize a cyber model in a way that can reduce certain inefficiencies in certain bases of MDASH so that we can take advantage of Microsofts internal model as well.

CL Command Line

And then I‘m just curious, from a broader perspective, how would you characterize Microsofts DevSecOps story and vision, and how do you see your work fitting into that larger puzzle?

TK Taesoo Kim

MDASH started as an internal project, so we designed everything for our internal developers. We tightly integrated together with the Copilot SDK so that all the full-time employees at Microsoft can launch MDASH as a project on their laptops. Its great, and the Windows developers realized that when they tried to scan their codebases in MDASH, they significantly increased the pace at which they discovered vulnerabilities with high fidelity. And then they really saw the potential of MDASH and started integrating it in the CI/CD pipeline. Were also using ADO, Azure Development Ops, inside of most of the projects at Microsoft. So were exhaustively scanning ecosystems and infrastructures across Microsoft by using MDASH. In other words, for certain releases, very frequently, were enabling an MDASH-like scanning tool so that developers can get automated pen testings out of their repository before releasing the software.

CL Command Line

Whats next for you? Are there any interesting research questions that youre exploring at the moment?

TK Taesoo Kim

We’re focusing on how to automate exploit generations given a finding, which should stop whenever we prove the existence of a vulnerability. But the existence of a vulnerability is different from its exploitation, meaning, whats the implication of this bug? Can we actually take control of the program, and what are the impacts after? There‘s a huge difference between, ”Hey, I can trigger this vulnerability and the program actually crashing. So were enabling the last milestone. This is very, very important in the journey of fully automated remediation.

What I mean by this: We discovered the vulnerability, estimated the severity by creating the exploitation, and by using this, we can actually test the generated patch and prevent those exploitations. This is one of the very objective metrics in terms of measuring the validity of the patch that were generating. So end-to-end, from discovery all the way down to the exploit generations, and by using it, we like to enable the pipeline for automated remediations as well. This is one of the big journeys that were taking.

Another big area is what we call binary support. Certain programs, like many Windows device drivers, dont have symbols, like even source code. Given that, can you enable MDASH-like systems in those contexts for a broader set of programs, beyond a repository?

CL Command Line

Interesting. If a PhD student or a security engineer wanted to push this field forward, what open research problems would you point them toward?

TK Taesoo Kim

I think one interesting areaIve pitched this idea many times to my students—is that, in the software world, theres a technique called obfuscation. In order to prevent an attacker from analyzing my software, I obfuscate the binary, obfuscate the source code. Many agentic systems minimize their source code, right? They create a bundle. And AI systems are extremely good at unpacking those and understanding them, but what is the technique to influence the analysis that an LLM can perform?

For example, instead of creating just a minimized version, should I introduce a more complex control flow, or is there a technique that we can play with the symbol name in such a way that the LLM can confuse you about the existence of the source code? I think these are pretty interesting new areas of how to defend my software from an LLM power attacker in terms of software analysis. Is there any technique that can slow down that analysis so that we can enable that packaging before releasing my software with an additional layer of mitigation?

CL Command Line

Going back to the beginning and being part of the winning team for DARPA‘s AIxCC competition: Tell me a little bit more about what that experience was like.

TK Taesoo Kim

A DARPA Grand Challenge is one of the biggest competitions in your career—not just me, but for any computer science professor or researcher. DARPA only announces them very occasionally, maybe every 10 years? It’s a huge opportunity.

When they announced the AI Cyber Challenge, thats my area that I’ve been working on my whole lifetime. So my clear goal was to demonstrate that we are the best. We are very competitive people. And we’re not just doing this competition. We’ve won multiple other competitions, but we thought, “We’re not gonna win this competition. People around the world brought their best knowledge and best effort to compete. That was an amazing experience. I never thought of working that hard over two years in my entire career.

CL Command Line

That’s pretty amazing. So how did your work in that competition ultimately inspire or inform MDASH?

TK Taesoo Kim

Every single activity that weve done at Microsoft was influenced by AIxCC, even like at the idea level and lessons level. Because after spending 100 billion tokens during the competitionactually more. So on one OpenAI model alone, we spent more than 100 billion tokens.

CL Command Line

Wow.

TK Taesoo Kim

And we had all these lessons that we had learned. So that was a pretty interesting exercise after the competition. Now we have a huge amount of engineering resources, so lets tackle this domain of the problem with the insights that we have.

CL Command Line

Right.

TK Taesoo Kim

And thats why its worked very well in practice.

CL Command Line

Thats a crazy stat. I know we’ve covered a lot of ground. But if people reading this story take one thing away from it, what do you hope that would be and why?

TK Taesoo Kim

The world has already changed because of highly intelligent models. Say you’re a software developer or a software company. I think this is a very important moment to think about what an AI power attacker could do to your organization. It’s just scary at first, but if you think through what’s going to happen in the longer-term future, this is the first time where the defender might win this game.

Because before releasing new software, we have a huge chance of finding and eliminating all these possible vulnerabilities ahead of time. I‘m one of the true believers that an MDASH-like system can help us eliminate all of them so that we can win the game. And this is the first time in my career in security where it’s actually possible—or even probable—that the attacker might lose the game in the end.


Learn more about codename MDASH: 

Learn more about Project Perception: 

The post From research to reality: An interview with Microsoft VP of Security Research Taesoo Kim appeared first on Command Line.

Read the whole story
alvinashcraft
55 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Enforce Access Token Expiry Policies in Pulumi Cloud

1 Share

Pulumi Cloud organizations can now enforce a maximum expiry on the access tokens used against them. Organization admins can set a cap in days, and from that point on, personal, organization, and team tokens operating on resources in the org must carry an expiration within the cap for requests to succeed. Tokens that never expire, or that have too much lifetime remaining, get rejected with an error that tells the user exactly how to regain access.

Why cap token lifetimes

Many organizations already have a credential rotation policy that says tokens must expire, but until now, Pulumi Cloud could only recommend an expiry at creation time. Nothing stopped a member from creating a never-expiring personal token, and nothing aged out the long-lived tokens created before your policy existed.

That gap matters because a leaked token is only as dangerous as its remaining lifetime. A token that never expires is a standing liability.

By adding support for access token expiry policies, Pulumi Cloud now closes the gap at the platform level. Once you’ve set the cap, Pulumi Cloud enforces it immediately for your organization, including for tokens that already exist.

How it works

In your organization’s settings, navigate to Settings > Access Management > Other and scroll to Access token expiry policy:

The access token expiry policy card in Pulumi Cloud organization settings, with a 14-day maximum entered and buttons to preview affected tokens and save the policy.

You can also get there from the Access Tokens tab, where a banner shows whether a policy is in effect — select Edit policy:

The banner on the Access Tokens tab stating that the organization caps access token expiry at 14 days, with an Edit policy link.

The policy is a single number: the maximum expiry, in days, for tokens used against your organization. Compliance is checked on every request, and a token complies when both of these are true:

  1. It has an expiration date. Never-expiring tokens violate any policy.
  2. Its remaining lifetime — the time between now and its expiration — is within the cap.

Because compliance is based on remaining lifetime rather than the expiry chosen at creation, the policy is pragmatic about existing credentials: a token created a year ago with a two-year expiry becomes compliant once it has less than the cap remaining. You’re enforcing exposure going forward, not retroactively punishing old tokens that are already near the end of their life.

Enforcement is tailored to each token type:

  • Organization and team tokens can’t be created out of compliance: the creation dialog caps the expiry picker at your policy maximum, and the API rejects requests that exceed it. Existing machine tokens that violate the policy stop authenticating and need to be recreated with a compliant expiry.
  • Personal tokens span all of a user’s organizations, so they can’t be blocked at creation. Instead, a non-compliant personal token is rejected when it’s used against your organization, and the member sees an error explaining the policy and how to fix it. The personal token creation dialog also warns members when a chosen expiry doesn’t meet a policy in one of their organizations, steering them toward a compliant choice up front.
  • Web console sessions are unaffected, as are the short-lived tokens issued through OIDC token exchange — those are already bounded by their issuer.

Once a policy is active, the creation dialog does the steering for you — the expiry picker tops out at the policy maximum:

The New Access Token dialog with the expiration picker set to “14 Days (org policy max)” and helper text noting the 14-day policy maximum.

Rolling it out without breaking CI

The riskiest moment for any new enforcement policy is the moment you turn it on. Two things make that safe here.

First, Preview affected tokens shows you the blast radius before you save: the organization and team tokens that would stop authenticating under the proposed cap, by name and creator. Recreate those credentials with compliant expiries first, then save the policy.

The preview listing one machine token that would fail to authenticate under a 14-day policy, with a note that non-compliant personal tokens are rejected at request time.

Second, rejections are designed to be self-explanatory. A blocked request fails with a 403 Forbidden that names your organization and its policy maximum, so a member whose personal token no longer complies knows immediately what happened and what to do: generate a new token that meets the policy. Policy changes are also recorded in your organization’s audit logs.

A pulumi up run rejected with a 403 error stating that the acme-corp organization enforces a max access token expiry of 14 days that the current token does not meet.

A reasonable rollout looks like:

  1. Decide on a cap that matches your rotation policy. 90 days is a common choice for CI credentials.
  2. Use Preview affected tokens and recreate any non-compliant machine tokens.
  3. Socialize the change in your organization: personal tokens without a compliant expiry will stop working against the organization.
  4. Save the policy. From here on, the platform enforces it for you.

Get started

The access token expiry policy is available now in your organization’s access settings. For the full reference — compliance rules, per-token-type behavior, and exemptions — see the access tokens documentation.

If you have feedback, we’d love to hear it in the Pulumi Community Slack or on GitHub.

Read the whole story
alvinashcraft
55 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Making an agile version of a Windows Runtime delegate in C++/WinRT, part 6

1 Share

It looked like we were done when we fixed the problem of releasing a non-marshalable delegate on the correct thread.

But we missed something.

Again.

    if (d.try_as<::INoMarshal>()) {
        void* p;
        if constexpr (std::is_reference_v<Delegate>) {
            p = winrt::detach_abi(d);
        } else {
            winrt::copy_to_abi(d, p);
        }
        return
            [p = std::unique_ptr<void, in_context_deleter>(p),
            token = get_context_token()](auto&&...args) {
                if (token == get_context_token()) {
                    std::remove_reference_t<Delegate> d;
                    winrt::copy_from_abi(d, p.get());
                    d(std::forward<decltype(args)>(args)...);
                } else {
                    throw winrt::hresult_error(CO_E_NOT_SUPPORTED);
                }
            };
    }

The first part gets a raw ABI pointer, either by moving it out of the inbound delegate if we can, else by copying it from the inbound delegate. The reference count is owned by the raw pointer.

The second part wraps the raw ABI pointer inside a std::unique_ptr with our custom deleter. The unique pointer now owns the reference count, and the custom deleter will release it.

The problem is that one of the requirements for a custom deleter is that if you use the unique_ptr(p) constructor, the custom deleter must not throw an exception at construction.

[unique.ptr.single.ctor]

constexpr explicit unique_ptr(type_identity_t<pointer> p) noexcept;

Constraints: is_pointer_v<deleter_type> is false and is_default_constructible_v<deleter_type> is true.

Preconditions: D meets the Cpp17DefaultConstructible requirements, and that construction does not throw an exception.

But our custom deleter could throw an exception if Co­Get­Object­Context fails. So it doesn’t meet the preconditions.

We can fix that by using the constructor that takes an explicit deleter from which the stored deleter can be move-constructed. If an exception occurs, it happens during the creation of the parameter and not inside the unique_ptr constructor.

    if (d.try_as<::INoMarshal>()) {
        void* p;
        if constexpr (std::is_reference_v<Delegate>) {
            p = winrt::detach_abi(d);
        } else {
            winrt::copy_to_abi(d, p);
        }
        return
            [p = std::unique_ptr<void, in_context_deleter>(p, {}),
            token = get_context_token()](auto&&...args) {
                if (token == get_context_token()) {
                    std::remove_reference_t<Delegate> d;
                    winrt::copy_from_abi(d, p.get());
                    d(std::forward<decltype(args)>(args)...);
                } else {
                    throw winrt::hresult_error(CO_E_NOT_SUPPORTED);
                }
            };
    }

Okay, so now we’re done?

Nope, still broken.

More next time.

The post Making an agile version of a Windows Runtime delegate in C++/WinRT, part 6 appeared first on The Old New Thing.

Read the whole story
alvinashcraft
55 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories