Activities of "maliming"

Hi,

Thanks for the clarification — my previous answer was aimed at the SP direction, so it doesn't apply to your scenario. Before I give a concrete wiring suggestion for the IdP side, a few quick questions so I don't guess wrong on the ABP integration point:

  1. Which ComponentSpace integration mode are you using?

    • AddSamlMiddleware(...) + app.UseSaml() — the middleware handles the SP-initiated SSO entry, the login redirect, and the SAMLResponse generation automatically, and you only implement the Events.OnInitiateSso callback.
    • Or are you injecting ISamlIdentityProvider directly and calling SendSsoAsync / ReceiveSsoAsync from your own controller / Razor page?

    The two modes are mutually exclusive in ComponentSpace, and the "post-login wire-up" looks completely different in each — that's why I need to know which one you've gone with.

  2. About your SamlApplication entity — what's the goal?

    • Multi-tenant dynamic SP registration (each tenant registers its own SPs at runtime)?
    • A management UI for admins to add SP partners without editing appsettings.json?
    • Something else?

    ComponentSpace's default partner configuration lives in JSON (via AddSaml(Configuration.GetSection("SAML"))). They also ship ComponentSpace.Saml2.Configuration.Database for dynamic DB-backed partner configuration, which may already cover what you're building.

  3. Can you share the current shape of your SAML controller / endpoint (the one that receives the SP's SAMLRequest) and the place you're trying to "wire post-login" from? Even pseudo-code is fine — I just need to see whether you're inside an MVC controller action, a Razor Page handler, or middleware so I can point at the right ABP extension point.

  4. Project setup (the Question Details section was left blank):

    • ABP version
    • UI type (MVC / Blazor / Blazor WebApp / Angular)
    • Tiered, i.e. separate AuthServer host? Or single-host setup?

    This matters because in Tiered the SAML IdP endpoints have to live on the AuthServer (where /Account/Login runs), not on the business Web/HttpApi host.

Once I have these four pieces, I can give you a single concrete wiring path instead of two parallel "if this then that" answers.

Thanks

ok If you run into any problems, feel free to contact us anytime.

Hi,

You don't need to replace any application service to integrate SAML — that's not where ABP plugs external auth in. SAML is a standard ASP.NET Core remote authentication handler, and ABP's Account module (Razor Pages, runs on AuthServer in Tiered or on the Web/HttpApi host otherwise) already handles external sign-in, user creation, and link-by-email out of the box. Your job is to register the SAML scheme; ABP picks it up automatically.

Where to register the SAML scheme

  • Tiered (separate AuthServer) → register inside the AuthServer module (MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs), next to where Google/Microsoft/Twitter providers are configured. The Account UI (login page + external login callback) and OpenIddict both live on AuthServer, so SAML must terminate there as well.
  • Non-tiered → register inside whichever host runs the Account module (*.Web for Blazor Server/WebApp/MVC, or *.HttpApi.Host for Angular without a separate AuthServer).

Minimal wiring with ComponentSpace.Saml2 (verified against package version 6.1.0):

public override void ConfigureServices(ServiceConfigurationContext context)
{
    var configuration = context.Services.GetConfiguration();

    // 1) Load ComponentSpace SAML configuration (partners, certs, mapping rules).
    context.Services.AddSaml(configuration.GetSection("SAML"));

    // 2) Register SAML as an external auth scheme alongside Google / Microsoft / etc.
    context.Services.AddAuthentication()
        .AddSaml("saml", "SAML (Salesforce)", options =>
        {
            options.SignInScheme = IdentityConstants.ExternalScheme;
            options.SignOutScheme = IdentityConstants.ApplicationScheme;
            options.AssertionConsumerServicePath = "/SAML/AssertionConsumerService";
            options.PartnerName = ctx => "https://salesforce.com"; // your IdP entity ID
        });
}

Three details worth being explicit about:

  1. SignInScheme = IdentityConstants.ExternalScheme — this is the contract ABP's Login.cshtml.cs > OnGetExternalLoginCallbackAsync reads from. Don't rely on defaults from a third-party handler.
  2. ACS / callback URL must point to AuthServer (e.g. https://auth.myapp.com/SAML/AssertionConsumerService). When you configure the SP entry inside Salesforce, set the assertion consumer URL to the AuthServer domain, not the business Web/Angular app. This is the Tiered detail you hinted at — AuthServer owns the entire external sign-in flow.
  3. SAML attribute → ABP claim mapping — ComponentSpace doesn't use the ASP.NET Core ClaimActions pipeline (its assertions are XML, not JSON). The cleanest way is to declare SamlMappingRule entries in your SAML JSON configuration so each <saml:Attribute> becomes a standard ClaimType (e.g. map SAML EmailAddress → http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress). After that, ABP's OnGetExternalLoginCallbackAsync reads loginInfo.Principal.FindFirstValue(AbpClaimTypes.Email ?? ClaimTypes.Email) and the rest just works. You do not need to override IAbpClaimsPrincipalFactory for this — that hook fires for every login (local password, SAML, social) and is overkill for attribute mapping on a single provider. Getting SAML attributes into the OpenIddict access token

This is the part most SAML integrations get wrong, so worth calling out. After SAML callback, the user is signed into the IdentityConstants.ApplicationScheme cookie on AuthServer. When the SPA / Blazor / Angular client then hits /connect/authorize, OpenIddict's TokenController rebuilds the principal via SignInManager.CreateUserPrincipalAsync(user) — so transient SAML claims do not automatically appear in the access token.

If you need Salesforce attributes (role, department, employee ID, …) inside the token:

  • Persist them onto the ABP user on first/each external sign-in (extra columns via ObjectExtensionManager.ConfigureIdentity(...), or rows in AbpUserClaims) — they'll be issued naturally on every subsequent token.
  • Or implement IAbpOpenIddictClaimsPrincipalHandler to inject claims into the OpenIddict pipeline directly.

Login page button shows up automatically

Once the SAML scheme is registered, the Account login page enumerates it on its own — it filters auth schemes whose HandlerType is RemoteAuthenticationHandler<>, which ComponentSpace satisfies. You only need AbpAccountOptions.ExternalProviderIconMap to set a custom icon. If you later want to enable/disable SAML from the database (without redeploy), plug it into IAccountExternalProviderAppService, but that's optional.

Coexistence of SAML + OpenIddict + Social

Fully supported. OpenIddict is your local token issuer; SAML and the social providers are just external schemes living alongside each other. One behavior to be aware of for UAT: ABP auto-links an external sign-in to an existing local user by email (UserManager.AddLoginAsync). So if a vendor first registers with username/password and later signs in via Salesforce SAML using the same email, the SAML login attaches to the existing account. Depending on the VerifyPasswordDuringExternalLogin setting on the Account admin page, the user may be asked to confirm their local password the first time. Worth flagging to your team before go-live.


On forking Volo.Abp.Identity

I'd advise against it. The Pro Identity / Account modules (Volo.Abp.Identity.Pro.*, Volo.Abp.Account.Pro.*) are built on top of the open-source Volo.Abp.Identity package and reference the matching version that ships in the same ABP release. If you fork the open-source code, repackage it, and try to reference it side-by-side with Pro NuGet packages, you're likely to hit assembly version mismatches, duplicate type registrations, and EF Core model conflicts at runtime. If you absolutely need to try, do it in a throwaway project first — but I wouldn't ship it.

With Team license, you have all the extension points you need without forking:

  • Replace a service: derive from any Pro or open-source service and register with [Dependency(ReplaceServices = true, TryRegister = true)]. Works for SignInManager, UserManager, UserStore, *AppService, etc.
  • Extend entities: ObjectExtensionManager.Instance.Modules().ConfigureIdentity(...) adds columns to AbpUsers without touching the entity class.
  • Inject claims: implement IAbpClaimsPrincipalContributor for global claim injection, or IAbpOpenIddictClaimsPrincipalHandler specifically for OpenIddict tokens.
  • React to user lifecycle: subscribe to the framework's user created / updated events via the distributed event bus (good place to push Salesforce attributes onto AbpUserClaims on first sign-in).

These cover the "customize authentication flow" surface area for Team license and stay upgrade-safe across ABP versions.

Two reference files worth opening when you start wiring:

  • Volo.Abp.Account.Pro.Public.Web/Pages/Account/Login.cshtml.cs (OnGetExternalLoginCallbackAsync) — the canonical external login flow that you do not need to override.
  • The Pro AuthServer template's MyProjectNameAuthServerModule.cs — already has Google / Microsoft / Twitter registered exactly the way you'll add SAML.

Thanks

Hi Pooja,

Thanks for the detailed write-up. One clarification first, because it changes the answer:

Are you configuring these rules at design time (you, as the developer, once per entity), or at runtime (a tenant admin builds/edits rules from a UI like the screenshot)?

The screenshot looks like a runtime rule builder, but "I want to build something like this for each object" sounds more design-time. Both paths below.

About AddResourcePermission(...)

That API isn't the right tool here. ABP's resource-based authorization is for per-record grants ("user X is allowed to edit this specific order"), stored in AbpPermissionGrants and checked via IAuthorizationService.AuthorizeAsync(resource, policyName). There's no operator field because it's a per-row ACL, not a rule DSL.

Your PHP rules ($order->user_id === $user->id) aren't per-record grants — they're ownership checks, which is a different mechanism.

Design-time approach (recommended for the MVP)

Your PHP rules collapse into two concerns ABP already handles centrally:

1. Tenant isolation — implement IMultiTenant on your entities; ABP auto-filters every query by TenantId, no Where(x => x.TenantId == ...) anywhere.

2. Ownership ("self") rules — add CreatorId (already on FullAuditedEntity) or a domain field like CustomerUserId / VendorUserId, then enforce it once in the application service:

public async Task UpdateAsync(Guid id, UpdateOrderDto input)
{
    var order = await _orderRepository.GetAsync(id);

    if (!await AuthorizationService.IsGrantedAsync(MyPermissions.Orders.Manage) &&
        order.CustomerUserId != CurrentUser.GetId())
    {
        throw new AbpAuthorizationException();
    }

    // update order...
}

That single check replaces your PHP if. The rule lives in one place, which is the centralization you actually wanted — the PHP pain wasn't "hardcoding", it was rules scattered across controllers/views/models.

For list endpoints, filter at query level so users don't even see other people's rows:

var query = await _orderRepository.GetQueryableAsync();
if (!await AuthorizationService.IsGrantedAsync(MyPermissions.Orders.Manage))
{
    query = query.Where(x => x.CustomerUserId == CurrentUser.GetId());
}

Runtime rule-builder UI (if you really need it)

ABP doesn't ship one, and nothing in the framework (Settings, Features, SaaS, resource-based authorization) can be repurposed for it. If you must have it, you'd build the full stack yourself:

  • JSON schema to store rules per entity in DB
  • Querybuilder UI (jQuery QueryBuilder / react-querybuilder / etc.)
  • Backend translator that compiles saved rules into Expression<Func<T, bool>>
  • Hook to apply those expressions in repositories / app services

That's significant custom work — worth doing only if rules genuinely need to change without a code deploy. For an MVP I'd start with the design-time approach and only escalate if a real customer asks for runtime configuration.

Let me know which scenario you're on and I can go deeper.

Thanks,

Great 👍

: )

Hi,

This is a known regression in LeptonX 5.3.x (Blazor) and we already shipped a fix in ABP 10.4.0 / LeptonX 5.4.0 (released a few days ago). The simplest path forward is to upgrade.

What's going on

In 5.3.x, BaseSideMenu binds click handlers only once, in its constructor. ABP's IMenuContributor is async, so the sidebar <ul>/<li> DOM may appear after initSideMenu already ran (or get replaced later when Blazor re-renders the sidebar, e.g. on impersonation / permission refresh). When that happens menuItems ends up empty — exactly what your DevTools shows. Calling createMenuItems() manually rebinds against the final DOM, which is why your manual test works.

So your "CDN cache miss vs hit" observation is correct about direction (timing-related), but the root cause is the single-shot binding, not network speed. CDN speed just changes which side of the race you land on.

Fix — upgrade to ABP 10.4.0

5.4.0 makes createMenuItems() idempotent (skips items already bound via a data-lpx-bound marker) and adds a MutationObserver on #lpx-sidebar that re-binds whenever the menu DOM changes. After upgrading you don't need any extra script.

Related cases with the same root cause:

  • https://abp.io/support/questions/10622
  • https://abp.io/support/questions/10642

Workaround if you can't upgrade right now

Add a small script in your Blazor Server project (e.g. wwwroot/lpx-sidemenu-rebind.js):

(function () {
  const sidebar = document.querySelector('#lpx-sidebar');
  if (!sidebar) return;

  function rebind() {
    if (window.leptonx?.sideMenu?.createMenuItems) {
      // Clear so we don't push duplicates into menuItems[]
      window.leptonx.sideMenu.menuItems = [];
      window.leptonx.sideMenu.createMenuItems();
    }
  }

  const observer = new MutationObserver(() => {
    if (sidebar.querySelector('.outer-menu-item > .lpx-menu-item')) {
      rebind();
    }
  });

  observer.observe(sidebar, { childList: true, subtree: true });
  rebind();
})();

Reference it after the LeptonX bundle in your host page:

<abp-script-bundle name="@typeof(Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Bundling.BlazorLeptonXThemeBundles.Scripts.Global).FullName" />
<script src="~/lpx-sidemenu-rebind.js"></script>

This is essentially the same approach we took in 5.4.0, just done from your project.

About your TelerikSideMenuLayout

Not the cause of this bug — your override inherits OnAfterRenderAsync from SideMenuLayout and keeps #lpx-sidebar intact, so LeptonX init still runs the same way. You can keep it as-is for this issue.

One unrelated note though: Telerik recommends placing <TelerikRootComponent> at the top of App.razor / Routes.razor (wrapping <Routes>), not inside a layout around @Body. Putting it inside the layout means it gets remounted on every navigation and can cause subtle problems with Telerik popups/dialogs/portals later. Worth fixing when convenient, but it's not related to your menu issue.

Thanks

Hi,

The React UI is still in RC, so there isn't a full end-to-end "add a new microservice + page" tutorial yet. Here's the workflow validated against the 10.4 microservice + React UI template you generated:

The 3 React apps in your solution

apps/ contains three independent Vite-based React apps with different roles:

  • apps/react/ — your business SPA. This is where your own pages go.
  • apps/react-admin-console/ — the embedded ABP administration panel served at /admin-console/, wired to the Volo.Abp.AdminConsole package. It hosts the Identity / Setting / SaaS / Audit / OpenIddict / GDPR / AI screens. Don't put your own features here — it's meant to stay as a drop-in admin shell.
  • apps/react-public-web/ — optional Next.js public website (only present when you generated the solution with --public-website).

Adding a new microservice (backend)

Use ABP Studio's Add New Microservice wizard (right-click services/ in the solution explorer) or abp new-service on the CLI. The wizard automatically handles:

  • Web gateway appsettings.json — adds the YARP Routes + Clusters entries and appends the new scope to the swagger OAuthScopes(...) list.
  • Identity service OpenIddictDataSeeder.cs — adds CreateScopesAsync("<YourService>"), appends "<YourService>" to the React/AdminConsole client's commonScopes.Union(...), and registers the swagger client scope.

After the wizard finishes, run the DbMigrator project once so the new OpenIddict scope is seeded into the database.

The one piece the wizard does NOT do for React (manual step)

The wizard does not touch apps/react/dynamic-env.json. After it completes, open that file and append your new scope to oAuthConfig.scope:

{
  "oAuthConfig": {
    "scope": "offline_access openid profile email phone AuthServer IdentityService AdministrationService SaasService AuditLoggingService GdprService LanguageService YourNewService"
  }
}

Without this, the React app's access token won't include YourNewService and every call to the new endpoints will return 401. Apply the same edit to apps/react-admin-console/dynamic-env.json only if the admin console needs to call the new service.

Adding a new page in apps/react/

The frontend follows a hand-written TypeScript API layer pattern (more on that below). Six small files to touch:

1. API client — src/lib/api/<feature>.ts

import { api } from './axios'

export interface MyDto { id: string; name?: string }
export interface CreateUpdateMyDto { name: string }
export interface PagedResultDto<T> { items: T[]; totalCount: number }

export async function getItems(
  params: { maxResultCount?: number; skipCount?: number; sorting?: string } = {}
): Promise<PagedResultDto<MyDto>> {
  const { data } = await api.get<PagedResultDto<MyDto>>('/my-service/my-entity', { params })
  return data
}

export async function createItem(input: CreateUpdateMyDto): Promise<MyDto> {
  const { data } = await api.post<MyDto>('/my-service/my-entity', input)
  return data
}

The shared api axios instance (src/lib/api/axios.ts) automatically attaches the Bearer token, the __tenant header for multi-tenancy, Accept-Language from i18next, resolves the baseURL from dynamic-env.json at runtime, redirects to login on 401, and redirects to /403 on 403. Paths are written relative to /api, so '/my-service/my-entity' resolves to https://<gateway>/api/my-service/my-entity through the web gateway. To check the exact controller route for your new service, open Swagger at the service's HTTPS port.

2. Page — src/pages/<feature>/MyFeaturePage.tsx

Use TanStack Query for fetching and caching. The most complete sample in the template is BooksPage.tsx — to get the full books CRUD reference (list + create + update + delete + dialog + permissions) regenerate your solution with the -scp (sample CRUD page) flag, then copy the relevant parts.

import { useQuery } from '@tanstack/react-query'
import { getItems } from '@/lib/api/my-feature'
import { usePermissions } from '@/lib/auth/permissions'

export function MyFeaturePage() {
  const { isGranted } = usePermissions()
  const canEdit = isGranted('MyProjectName.MyFeature.Edit')

  const { data } = useQuery({
    queryKey: ['items'],
    queryFn: () => getItems({ maxResultCount: 10, skipCount: 0 }),
  })

  return <div>{/* render list + form */}</div>
}

3. Route — src/routes/router.tsx

import { MyFeaturePage } from '@/pages/my-feature/MyFeaturePage'
import { createPermissionGuard } from '@/lib/routing/guards'

const myFeatureRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/my-feature',
  component: MyFeaturePage,
  beforeLoad: createPermissionGuard('MyProjectName.MyFeature'),
})

const routeTree = rootRoute.addChildren([
  // ...existing children...
  myFeatureRoute,
])

createPermissionGuard reads granted policies from ABP's application-configuration endpoint, so as long as you declare the permission in your backend PermissionDefinitionProvider, the frontend picks it up automatically. There's no separate permission registration on the React side.

4. Sidebar menu — src/lib/routing/route-config.ts

import { ListChecks } from 'lucide-react'

export const routeConfig: RouteConfigItem[] = [
  // ...
  {
    path: '/my-feature',
    nameKey: 'Menu:MyFeature',
    icon: ListChecks,
    order: 10,
    requiredPolicy: 'MyProjectName.MyFeature',
  },
]

Items with requiredPolicy are hidden automatically when the user lacks that permission.

5. Localization — src/locales/en.json

{
  "Menu:MyFeature": "My Feature",
  "::MyFeature": "My Feature"
}

Key convention matches ABP's localization resource layout: <ResourceName>::<Key> (e.g. AbpUi::SavedSuccessfully, AbpIdentity::Users) for namespaced resources, and ::<Key> for the project's default resource. Menu items use the Menu:<Name> convention.

6. Button-level permissions inside the page

const { isGranted } = usePermissions()
const canEdit = isGranted('MyProjectName.MyFeature.Edit')

Supports compound policies too: isGranted('A || B'), isGranted('A && B').

Things to watch out for

  • Ports are randomized per abp new. Your .env.development and dynamic-env.json were generated with the actual web gateway / auth server / app ports for this solution. If you change any of them, restart npm run dev — Vite reads the proxy target at startup only.
  • README.md in apps/react/ mentions npm run generate-proxy, but that script is not in package.json. This is a docs/scripts mismatch in the RC template that we're fixing. The API layer is hand-written by design — abp generate-proxy -t js produces vanilla JS proxies that don't plug into the shared axios.ts interceptors (auth, tenant, i18n, 401/403 redirects), so it's only useful as a reference for endpoint shapes.
  • If you generated without -scp, you may see apps/react/src/pages/books/BooksPage.tsx referencing @/lib/api/books which doesn't exist. That's a known template issue when books_sample is off — the BooksPage file should be deleted alongside lib/api/books.ts but currently isn't. You can safely delete apps/react/src/pages/books/ and the dead BookOpen import in route-config.ts. We're fixing this on the template side.
  • The shared axios interceptor handles 401 → login and 403 → /403 for you. To opt out on a specific call, pass skipAuthRedirect: true / skip403Redirect: true in the request config.

Docs

  • React UI framework docs: https://abp.io/docs/10.4/framework/ui/react
  • Adding new microservices: https://abp.io/docs/latest/solution-templates/microservice/adding-new-microservices
  • React UI architecture introduction: https://abp.io/community/articles/7rfmgb2v
  • The apps/react/README.md in your solution covers the dev/build/test scripts.

More documentation will land as React UI moves from RC to stable.

Thanks

Hi,

Thanks for the CVE number. Looking at the public record, the picture is different from what your scanner is showing — this is very likely not an actual exposure for your uppy@3.27.3:

  • NVD record for CVE-2025-70023 names only transloadit uppy v0.25.6 (a 2017–2018 0.x release): https://nvd.nist.gov/vuln/detail/CVE-2025-70023
  • GitHub advisory GHSA-pw2f-q928-88j2 lists affected versions: Unknown and patched versions: Unknown: https://github.com/advisories/GHSA-pw2f-q928-88j2
  • The only reference is a sparse gist with no PoC, no code path, no attack vector: https://gist.github.com/zcxlighthouse/27926a85371ac5d2291f44903254753e
  • Snyk explicitly says uppy@3.27.3 has no direct vulnerabilities: https://security.snyk.io/package/npm/uppy/3.27.3
  • The Uppy upstream repo has no issue, PR, or advisory acknowledging this CVE
  • The CVSS 9.8 score on the CVE was auto-assigned by CISA-ADP from the description string, not from an analyzed attack chain

Even setting the version-range question aside: uppy is a client-side file picker / uploader library that runs in the end user's own browser. A CWE-843 (type confusion) bug in that context, at worst, lets a malicious page or local script crash or misbehave inside the user's own tab during an upload. It is not a server-side RCE, not a cross-user attack, and it does not bypass the ABP server's authentication, authorization, or BlobStoring layer that the file ultimately goes through. The 9.8 Critical rating is a description-pattern artifact, not a real-world severity for this context.

To your questions:

1. Is mitigation needed from ABP's side?

There is no evidence uppy@3.27.3 is affected, and even if it were, the realistic blast radius in a client-side uploader is small. The most defensible action is to push back on the finding with your scanner vendor as a likely false positive and cite the six points above. Most SCA vendors accept disputes when the CVE description itself does not cover the version they are flagging.

2. Should we remove @abp/uppy entirely?

We do not recommend removing it. @abp/uppy is the resource source for several ABP upload features that ship with the framework: File Management module, CMS Kit blog/page editor image upload, Identity user bulk import, and Account profile picture. Removing the package will break those UIs as soon as anyone hits them.

3. Safe workarounds / compensating controls.

Realistically, there isn't a targeted one. The CVE itself describes no exploit path, so there is no specific configuration to harden. And several ABP upload surfaces are intentionally open to all authenticated users (Account profile picture, CMS Kit editor image upload for content authors), so permission revocation isn't a generic answer either.

The only meaningful server-side knob in 8.2.3 is FileManagement.FileDescriptor.Create permission, which you can revoke per role for the File Management module. The Account profile picture and CMS Kit editor flows don't have an equivalent ABP-level whitelist switch. If you wanted to apply a hard whitelist at the HTTP layer (allowed extensions / MIME types) you'd implement that yourself in a middleware or action filter — it is not built in.

In short: don't expect to find a meaningful "ABP-side mitigation" for this CVE, because the CVE doesn't describe what we'd be mitigating against.

4. ABP's plan around this CVE.

ABP has already moved uppy forward in newer releases on its normal cadence:

  • ABP 8.3.x → uppy ^3.27.0
  • ABP 9.x → uppy ^4.4.1
  • ABP 10.x (current 10.4) → uppy ^5.1.2

Since uppy@3.27.3 is not confirmed vulnerable by any public record, there is no planned 8.2.x security patch specifically for CVE-2025-70023. If the CVE ever gets a real entry with a confirmed 3.x range and a fixed version, we will follow up.

If your compliance process still requires a recorded action on the finding, here are two low-impact options that don't touch ABP server-side at all:

Option 1 — refresh your lockfile. Your uppy@3.27.3 is whatever your package-lock.json froze at install time. The 3.x line has one final patch after that, uppy@3.27.4 (2024-08-21). A clean reinstall picks it up:

rm -rf node_modules package-lock.json
npm install
npm ls uppy

Option 2 — pin the version explicitly. Some scanners prefer to see an explicit pin as a compliance signal. Add an override in your package.json:

{
  "overrides": {
    "uppy": "3.27.4"
  }
}

For yarn, use resolutions with the same value. Do not pin to 4.x or 5.x via overrides — Uppy 4.x went ESM-only and the bundle/plugin internals changed; ABP 8.2.3's MVC pages call Uppy.Dashboard / Uppy.XHRUpload via the global bundle, and crossing majors here will break the upload UIs.

Thanks

Hi,

Quick context: in ABP 8.2.3, the @abp/uppy npm wrapper declares "uppy": "^3.0.0", and npm resolves that to the latest 3.x — which is 3.27.3. The wrapper has no JS code of its own; it just copies node_modules/uppy/dist/* to wwwroot/libs/uppy/ via abp.resourcemapping.js. So you can change the resolved uppy version without touching ABP server-side (NuGet) packages.

Two important signals before we pick a fix:

  1. Your report shows uppy@3.27.3 (Transitive). In most SCA tools, (Transitive) means uppy is brought in through another package (here, @abp/uppy). The finding can either be in uppy's own code or in one of uppy's own sub-dependencies, and scanners often display the top-level package as the "vulnerable" one.
  2. The public databases we can check do not flag uppy@3.27.3 itself:
    • Snyk: https://security.snyk.io/package/npm/uppy/3.27.3 — "No direct vulnerabilities have been found for this package."
    • GitHub Advisory Database / NVD: no advisory matching uppy + CWE-843.

This strongly suggests the actual vulnerable package is a sub-dependency of uppy, not uppy itself. To pinpoint it, please share:

  • Output of npm ls uppy (full chain, so we can confirm whether it comes through @abp/uppy or something else)
  • The exact advisory ID from your scanner (CVE / GHSA / SNYK-ID), and ideally the "vulnerable package" field — that one is the package you actually need to bump, not necessarily uppy

While you collect those, here are the upgrade paths in order of safety:

Option 1 — Bump only the @abp/* npm packages to ~8.3.x (recommended). Keep ABP NuGet at 8.2.3. @abp/uppy@~8.3.x declares "uppy": "^3.27.0", so you stay on the 3.x line and just get the latest 3.27 patch. This is the smallest change and may already pull a fresh enough sub-dependency to clear the finding.

# In your client project, change @abp/* packages to ~8.3.x in package.json, then:
rm -rf node_modules package-lock.json
npm install
npm ls uppy

Option 2 — npm overrides to pin the actual vulnerable package. Once you know which sub-dependency the scanner is flagging, override that one directly (not necessarily uppy). Example shape:

{
  "overrides": {
    "<the-actual-vulnerable-package>": "<fixed-version>"
  }
}

If the scanner really is flagging uppy itself, pin uppy to the latest 3.x patch and stay on the 3.x major.

Don't blindly jump to uppy@4.x or 5.x via overrides. Uppy 4.x went ESM-only and 5.x changed bundle/plugin internals further. ABP's MVC pages call Uppy.Dashboard, Uppy.XHRUpload, uppy.use(), etc. via the global bundle, and although ABP itself moved to uppy 4 in v9.0 and uppy 5 in v10.0, that came with corresponding npm-pack updates and library-refresh runs (abp install-libs). Overriding the major across a wrong @abp/uppy version risks runtime errors in upload UI (file-management, cms-kit-admin, identity user import, account profile picture, etc.).

For reference, this is how ABP itself has moved uppy:

| @abp/uppy version | uppy range | | --- | --- | | 8.0.x – 8.2.x | ^3.0.0 | | 8.3.x | ^3.27.0 | | 9.x | ^4.4.1 | | 10.x | ^5.1.2 |

Also note: there is no ABP configuration switch that mitigates an npm-level vulnerability. It is purely a dependency-version question. If your client app does not actually use any Uppy-based upload UI at runtime, removing the package that pulls it in is also an option, but again npm ls uppy is the first step to know what to remove.

Thanks

Showing 471 to 480 of 12915 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.