Activities of "maliming"

Hi,

We're pulling up your account and the usage records for yesterday, including that plan implementation run. We'll come back to you here once we've been through them.

Thanks

Hi,

Both of these are third-party frontend assets that ABP copies into wwwroot/libs — neither is ABP server-side code, and neither needs an ABP/NuGet change. Here's where each one stands.

Finding 1 — Chart.js "Unchecked Input For Loop Condition" (SAST)

This is a Checkmarx SAST/taint rule, not a CVE. It flags that data flows into a loop condition inside chart.umd.js. That is the library's normal behavior — it iterates over whatever dataset arrays you pass it — so no Chart.js release clears this rule. There is no fixed version to move to; even the latest Chart.js 4.x still loops over datasets.

On version, for completeness: @abp/chart.js declares chart.js as ^4.5.0, and the latest 4.x is 4.5.1. Moving to it is version hygiene, not a fix for this finding. If you do want to refresh the copied asset, update/verify your frontend lockfile first, then re-run abp install-libs — the copy step uses whatever your lockfile has resolved, it doesn't bump versions on its own.

The realistic exposure here is client-side, in the end user's own browser, and only if the app feeds attacker-controlled, unbounded arrays into a chart. So the actionable checks are:

  • Confirm your app doesn't pass untrusted, unbounded datasets straight into Chart.js. If any path does, cap the series/points count at the application boundary (or paginate the underlying API).
  • Close the finding as a false positive / non-exploitable with that justification. If you exclude vendored minified bundles (wwwroot/libs/**) from SAST, keep those files under SCA/SBOM so you don't lose third-party dependency coverage.

Finding 2 — Uppy CWE-843 Type Confusion (SCA)

This is the same CVE as your earlier ticket #10670 — CVE-2025-70023. The public record for it is thin:

  • NVD names only transloadit uppy v0.25.6, a 2017 0.x release: https://nvd.nist.gov/vuln/detail/CVE-2025-70023
  • The GitHub advisory lists affected and patched versions as Unknown, and the only reference is a bare gist with no PoC or attack path
  • Snyk shows uppy@5.2.4 with no direct vulnerabilities

Checkmarx's own AppSec research widened the affected range to reach 5.2.4, which is exactly the "differs from NVD" note in your report. To your specific questions:

  • Upgrade path to a fixed version? There isn't one to move to. @abp/uppy declares uppy as ^5.1.2, and 5.2.4 is already the latest Uppy release — there is no higher or patched version published.
  • Is it used? Uppy comes in through @abp/cms-kit.admin and the Pro upload UIs (File Management, Identity user import, Account profile picture). Whether it's actually reached at runtime depends on which of those screens your app exposes — the dependency being installed doesn't mean the upload UI is in use.
  • Mitigation / closure. Since public records don't currently list the 5.x line as affected and there's no PoC or published fix, the defensible path is to dispute the finding with Checkmarx and ask them to share the specific code path in 5.2.4 and the basis for their expanded affected range. Until they provide that, this is vendor-clarification-pending rather than a confirmed exposure. As structural context you can include: Uppy is a client-side file picker running in the end user's own browser; uploaded files still pass through ABP's server-side authentication, authorization, and BlobStoring layer, so a client-side type-confusion doesn't bypass those.

To keep Uppy tracking the latest 5.x over time, keep your @abp/* npm packages current rather than pinning Uppy yourself — pinning to 5.2.4 wouldn't change what you already have and would only block a future compatible 5.x patch.

Thanks

Hi,

Glad the health check one is sorted.

This second one isn't really a build error, it's a NuGet security audit warning (NU1903). In 10.5, System.Security.Cryptography.Xml gets pulled in transitively at 10.0.7 (through Hangfire), and a recent advisory flags that version as high severity. The Package Manager Console turns that restore-time audit warning into a terminating error, so Update-Database stops right after Build succeeded. That's also why TreatWarningsAsErrors=false and $ErrorActionPreference didn't help, it's a restore/audit warning, not a compiler one.

Pin the package to the patched 10.0.10 (10.0.7, 10.0.8 and 10.0.9 are all still flagged). Add this to your *.Domain project, since that's where it's being flagged, and it flows up to the projects that reference it:

<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.10" />

Then restore and run Update-Database again. If you use central package management, bump the <PackageVersion> for it in Directory.Packages.props instead.

Thanks

Hi,

The Entity dropdown is empty because Company lives in a different module (Master Data). By default Suite only lists entities from your Transaction solution itself, so entities from your other custom modules won't show up until you tell Suite to scan them.

In your Transaction module solution:

  1. Reference the Master Data module (its *.Domain project/package) and build the solution, so the Master Data Domain DLL ends up in the build output under src.

  2. Add a .suite/module-entity-extension.json file at the solution root:

{
  "Modules": [
    {
      "DomainProjectDllFileName": "YourCompany.MasterData.Domain.dll"
    }
  ]
}
  1. Open the navigation property dialog and check Include entities from ABP modules (Suite builds the solution first if it isn't already built). It'll scan that DLL and list Company in the Entity dropdown.

A note on what Suite generates here: on the transaction entity it adds a CompanyId foreign key rather than a Company navigation property on the aggregate itself, which follows ABP's guidance of referencing other aggregate roots by id. It still wires up a real EF Core relationship for that key (HasOne<Company>().WithMany().HasForeignKey(x => x.CompanyId)) and generates a separate ...WithNavigationProperties type that joins to Master Data to show the company fields on the list/detail pages.

Because that relationship is real, both entities have to live in the same database and the Transaction side's DbContext has to map Company (i.e. the Transaction app references the Master Data module). So this fits a modular-monolith setup, not two independently deployed modules with separate databases.

Docs: https://abp.io/docs/latest/suite/generating-crud-page (see the "Extending with Custom Module Entities" section).

Thanks

Hi,

This one comes from the IdentityModel package change in ABP 10. Volo.Abp.IdentityModel used to bring the old IdentityModel package in transitively, and in v10 it switched to Duende.IdentityModel. The HealthChecks UI background collector still needs the old IdentityModel assembly (5.2.0.0) at runtime, so once nothing pulls it into the output anymore it can't find it and keeps throwing that FileNotFoundException. That's also why you no longer see any direct or transitive reference to it.

Add an explicit reference to the old package in the host project(s) where you call AddHealthChecksUI() / MapHealthChecksUiEndpoints() (usually *.HttpApi.Host, and *.Web too if it registers the UI there):

<PackageReference Include="IdentityModel" Version="7.0.0" />

Then clean the bin/obj folders and rebuild. Version 7.0.0 is the last release under the old IdentityModel name and its assembly satisfies the 5.2.0.0 the collector asks for. The newly generated 10.x templates already include this line, so it's only existing solutions upgraded from 9.x that need it added by hand.

Thanks

Great 👍

Hi,

The missing-template error isn't about your NuGet package version. ABP Suite loads the Volo.Abp.Commercial.SuiteTemplates assembly once when it starts and keeps it in memory for the whole process. It doesn't reload it when you clean/rebuild or remove and re-add the solution. So if Suite loaded the templates before your upgrade (when your output still had the older assembly), it keeps using that old one no matter how many times you clean and rebuild. That's why all the steps you tried didn't change anything.

The one step missing is fully restarting Suite:

  1. Build your solution once so the output is on 10.5.0.
  2. Close all ABP Suite processes completely (also close ABP Studio if you launched Suite from it, so no Suite process stays alive in the background).
  3. Reopen Suite and generate the entity again.

If you want to confirm which assembly Suite actually had loaded, check the copy it runs from right after the error (it's the real loaded file, not the one in bin):

$dll = Join-Path ([System.IO.Path]::GetTempPath()) "Suite.dll"
[System.Diagnostics.FileVersionInfo]::GetVersionInfo($dll).FileVersion

If that shows anything below 10.5.0, it's the stale copy, and a full Suite restart clears it.

We'll also make Suite reload the templates when you re-add a solution so a full restart isn't needed for this in a future release.

Thanks

Hi,

You didn't miss anything. The Modern template family currently supports React (or no UI) only, so the Modular Monolith path in the Modern wizard doesn't offer Blazor.

You can build a modular monolith with Blazor using the Classic templates. In ABP Studio: Create new solution → choose Classic → select Application (Single Layer) as the host → pick Blazor as the UI (Blazor WebApp, Blazor Server or Blazor WebAssembly, with Blazorise or MudBlazor as the component library) → later, on the Modularity step, enable Setup as a modular solution and add your modules. The host stays a thin shell and your business code goes into the modules, and you can add more modules from Solution Explorer at any time. MVC and Angular are available on the same path as well.

These two documents compare the template families and explain how to choose the host template for a modular solution:

  • https://abp.io/docs/latest/solution-templates/modern-vs-classic
  • https://abp.io/docs/latest/solution-templates/guide

Whichever UI you pick, the backend capabilities are the same: HTTP APIs, OpenIddict authentication, permissions, modularity. Your existing mobile app can consume the same APIs (it just needs its own client configuration on the auth server), and the web UI runs in modern browsers on Windows and Linux.

So the decision is mainly about your team: Blazor keeps everything in C#, while the Modern template uses React/TypeScript. Since you have dense custom screens ahead, a good way to decide during your proof of concept phase is to build one representative complex screen in the stack you're leaning towards. Blazor is a first-class UI option in ABP, but not using it doesn't cost you any backend capability.

Thanks

Answer

Hi,

Building on Alper's answer: the AI agent helps with the editable files in your solution — the theme override, your own React app, your Razor overrides. The boundary is that in a layered/single-layer solution the Admin Console ships as a package, so its pages aren't generated source you edit directly; you brand it rather than rewrite it. How that splits out:

Branding the Admin Console (colors, surfaces, borders, radius) is one file. Your solution already ships wwwroot/theme-override.css, wired through AdminConsole:ThemeOverrideCssPath; editing it re-skins the whole console, including its account and profile screens (my account, sessions, linked/external logins, security logs) — they share the same shadcn/Tailwind tokens, so there's nothing to redo page by page. App name and logo are separate options (AdminConsole:ApplicationName / LogoUrl).

The boundary is that it's token-level. A few fixed status colors (badges and the like) aren't token-driven — you can still target them with your own CSS selectors, but those rely on internal class names that can change on package upgrades. Genuine structural or content changes aren't CSS at all, and in a layered solution there's no page source to edit for that.

The sign-in flow is separate: the default OIDC sign-in, and the register/forgot/reset pages it links to, are the classic server-side MVC Account module (/Account/*) — the same module you can already pull as source and customize through your MVC theme and Razor overrides. Your main React app (react/) also ships its own Login, Register, Forgot Password and Reset Password pages as editable source, though those are your app's auth pages, not the packaged Admin Console's profile screens.

To answer your last question directly: for layered/single-layer there isn't a separate downloadable React Account UI module like the MVC one — those profile screens live inside the packaged Admin Console. The microservice template is the exception: there the Admin Console is generated as source (apps/react-admin-console/), so you edit its pages directly.

Thanks

Hi,

The AI Management module currently ships three built-in vector store providers: MongoDB, Pgvector and Qdrant. A SQL Server provider is not planned at the moment, but we'll take your request into account for future versions.

The good news is that the vector store side is extensible, so you can plug in SQL Server (2025+ with the native VECTOR type) yourself. You implement two interfaces from the Volo.AIManagement.Abstractions package (IVectorStore and IVectorStoreFactory) in one of your application's modules, then register the factory. AI Management syncs the registered provider names at application startup (enabled by default), so after a restart SqlServer will appear in the workspace's vector store provider dropdown.

  1. Implement IVectorStore using Microsoft.Data.SqlClient. Here are the key parts (table creation, upsert and vector search):
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
using Volo.Abp;
using Volo.AIManagement.VectorStores;

namespace MyCompanyName.MyProjectName.VectorStores.SqlServer;

public class SqlServerVectorStore : IVectorStore
{
    protected readonly string ConnectionString;
    protected readonly Guid WorkspaceId;
    protected readonly ILogger<SqlServerVectorStore> Logger;

    public SqlServerVectorStore(string connectionString, Guid workspaceId, ILogger<SqlServerVectorStore> logger)
    {
        Check.NotNullOrWhiteSpace(connectionString, nameof(connectionString));

        ConnectionString = connectionString;
        WorkspaceId = workspaceId;
        Logger = logger;
    }

    protected virtual string GetTableName()
    {
        return $"AIVectorEmbeddings_{WorkspaceId:N}";
    }

    protected virtual async Task EnsureTableExistsAsync(SqlConnection connection, int dimension, CancellationToken cancellationToken)
    {
        var tableName = GetTableName();
        var sql = $"""
            IF OBJECT_ID(N'{tableName}', 'U') IS NULL
            BEGIN
                CREATE TABLE [{tableName}] (
                    [Id] NVARCHAR(450) NOT NULL PRIMARY KEY,
                    [WorkspaceId] UNIQUEIDENTIFIER NOT NULL,
                    [Embedding] VECTOR({dimension}) NOT NULL,
                    [Metadata] NVARCHAR(MAX) NULL,
                    [CreationTime] DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()
                );
                CREATE INDEX [IX_{tableName}_WorkspaceId] ON [{tableName}] ([WorkspaceId]);
            END
            """;

        try
        {
            await using var command = new SqlCommand(sql, connection);
            await command.ExecuteNonQueryAsync(cancellationToken);
        }
        catch (SqlException ex) when (ex.Number is 2714 or 1913)
        {
            // 2714/1913: the table/index was created by a concurrent call in the meantime.
        }
    }

    public virtual async Task<string> StoreAsync(
        Guid workspaceId,
        string id,
        float[] embedding,
        Dictionary<string, object>? metadata = null,
        CancellationToken cancellationToken = default)
    {
        await using var connection = new SqlConnection(ConnectionString);
        await connection.OpenAsync(cancellationToken);

        await EnsureTableExistsAsync(connection, embedding.Length, cancellationToken);

        var tableName = GetTableName();
        var sql = $"""
            MERGE [{tableName}] WITH (HOLDLOCK) AS [Target]
            USING (SELECT @Id AS [Id]) AS [Source]
            ON [Target].[Id] = [Source].[Id]
            WHEN MATCHED THEN
                UPDATE SET [Embedding] = CAST(@Embedding AS VECTOR({embedding.Length})), [Metadata] = @Metadata
            WHEN NOT MATCHED THEN
                INSERT ([Id], [WorkspaceId], [Embedding], [Metadata])
                VALUES (@Id, @WorkspaceId, CAST(@Embedding AS VECTOR({embedding.Length})), @Metadata);
            """;

        await using var command = new SqlCommand(sql, connection);
        command.Parameters.AddWithValue("@Id", id);
        command.Parameters.AddWithValue("@WorkspaceId", WorkspaceId);
        command.Parameters.AddWithValue("@Embedding", JsonSerializer.Serialize(embedding));
        command.Parameters.AddWithValue("@Metadata", metadata != null ? JsonSerializer.Serialize(metadata) : "{}");

        await command.ExecuteNonQueryAsync(cancellationToken);
        return id;
    }

    public virtual async Task<List<VectorSearchResult>> SearchAsync(
        Guid workspaceId,
        float[] queryEmbedding,
        int topK,
        CancellationToken cancellationToken = default)
    {
        // Return an empty list if the table doesn't exist yet (see the notes below the code).

        await using var connection = new SqlConnection(ConnectionString);
        await connection.OpenAsync(cancellationToken);

        var tableName = GetTableName();
        var sql = $"""
            SELECT TOP (@TopK)
                [Id],
                [Metadata],
                1 - VECTOR_DISTANCE('cosine', [Embedding], CAST(@Query AS VECTOR({queryEmbedding.Length}))) AS [Score]
            FROM [{tableName}]
            WHERE [WorkspaceId] = @WorkspaceId
            ORDER BY VECTOR_DISTANCE('cosine', [Embedding], CAST(@Query AS VECTOR({queryEmbedding.Length})))
            """;

        await using var command = new SqlCommand(sql, connection);
        command.Parameters.AddWithValue("@TopK", topK);
        command.Parameters.AddWithValue("@WorkspaceId", WorkspaceId);
        command.Parameters.AddWithValue("@Query", JsonSerializer.Serialize(queryEmbedding));

        var results = new List<VectorSearchResult>();
        await using var reader = await command.ExecuteReaderAsync(cancellationToken);
        while (await reader.ReadAsync(cancellationToken))
        {
            var metadataJson = reader.IsDBNull(1) ? "{}" : reader.GetString(1);
            results.Add(new VectorSearchResult
            {
                Id = reader.GetString(0),
                Score = Convert.ToSingle(reader.GetValue(2)),
                Metadata = JsonSerializer.Deserialize<Dictionary<string, object>>(metadataJson)
            });
        }

        return results;
    }

    public virtual async Task<int> DeleteAllByDataSourceAsync(
        Guid workspaceId,
        Guid dataSourceId,
        CancellationToken cancellationToken = default)
    {
        // Return 0 if the table doesn't exist yet (see the notes below the code).

        await using var connection = new SqlConnection(ConnectionString);
        await connection.OpenAsync(cancellationToken);

        var sql = $"""
            DELETE FROM [{GetTableName()}]
            WHERE [WorkspaceId] = @WorkspaceId
              AND JSON_VALUE([Metadata], '$.data_source_id') = @DataSourceId
            """;

        await using var command = new SqlCommand(sql, connection);
        command.Parameters.AddWithValue("@WorkspaceId", WorkspaceId);
        command.Parameters.AddWithValue("@DataSourceId", dataSourceId.ToString());

        return await command.ExecuteNonQueryAsync(cancellationToken);
    }
}

The remaining IVectorStore members are straightforward:

  • InitializeAsync: just open a connection to validate the settings. The table itself is created lazily on the first StoreAsync call, because the embedding dimension is not known before that.
  • DeleteAsync: DELETE ... WHERE [Id] = @Id AND [WorkspaceId] = @WorkspaceId, return rowsAffected > 0.
  • DeleteByWorkspaceIdAsync: DROP TABLE IF EXISTS [{GetTableName()}], return 1 (same as the Pgvector and Qdrant providers).
  • SearchAsync and the delete methods should check OBJECT_ID(@TableName, 'U') first and return empty/0/false when the table doesn't exist yet (before the first document is indexed).
  1. Implement the factory. The connectionSettings parameter is the value you enter in the workspace's vector store settings field (the SQL Server connection string):
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.AIManagement.VectorStores;

namespace MyCompanyName.MyProjectName.VectorStores.SqlServer;

[ExposeServices(typeof(IVectorStoreFactory), typeof(SqlServerVectorStoreFactory))]
public class SqlServerVectorStoreFactory : IVectorStoreFactory, ITransientDependency
{
    public const string ProviderName = "SqlServer";

    protected ILoggerFactory LoggerFactory { get; }

    public SqlServerVectorStoreFactory(ILoggerFactory loggerFactory)
    {
        LoggerFactory = loggerFactory;
    }

    public virtual string Provider => ProviderName;

    public virtual Task<IVectorStore> CreateAsync(Guid workspaceId, string? connectionSettings = null)
    {
        var logger = LoggerFactory.CreateLogger<SqlServerVectorStore>();
        return Task.FromResult<IVectorStore>(new SqlServerVectorStore(connectionSettings!, workspaceId, logger));
    }
}
  1. Register the factory in your module's ConfigureServices:
Configure<VectorStoreFactoryOptions>(options =>
{
    options.AddFactory<SqlServerVectorStoreFactory>(SqlServerVectorStoreFactory.ProviderName);
});

After that, restart the application and select SqlServer as the vector store provider when creating/editing a workspace, and put your SQL Server connection string into the vector store settings field.

A few notes:

  • This requires SQL Server 2025 (or Azure SQL Database) for the native VECTOR type and VECTOR_DISTANCE function.
  • The VECTOR type supports up to 1998 dimensions, so pick your embedding model accordingly (e.g. text-embedding-3-small with 1536 dimensions works, text-embedding-3-large with 3072 dimensions won't fit): https://learn.microsoft.com/en-us/sql/t-sql/data-types/vector-data-type
  • VECTOR_DISTANCE performs an exact (brute-force) search. That's fine for small/medium datasets; for large ones check SQL Server's approximate vector search options: https://learn.microsoft.com/en-us/sql/sql-server/ai/vectors
  • The module filters embeddings by the data_source_id metadata key when a data source is removed, that's why DeleteAllByDataSourceAsync uses JSON_VALUE([Metadata], '$.data_source_id').
  • The score returned by SearchAsync is the cosine similarity (1 - cosine distance), which matches the behavior of the built-in providers.
  • This is a minimal sample to get you started, you may want to add better error handling and diagnostics for production use.

Thanks

Showing 231 to 240 of 12912 entries
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on November 02, 2026, 06:56
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.