Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import com.alipay.sofa.ark.api.ArkClient;
import com.alipay.sofa.ark.api.ClientResponse;
import com.alipay.sofa.ark.api.ResponseCode;
import com.alipay.sofa.ark.common.util.BizIdentityUtils;
import com.alipay.sofa.ark.common.util.FileUtils;
import com.alipay.sofa.ark.common.util.StringUtils;
import com.alipay.sofa.ark.spi.model.Biz;
import com.alipay.sofa.ark.spi.service.biz.BizFactoryService;
import com.alipay.sofa.koupleless.arklet.core.command.builtin.BuiltinCommand;
import com.alipay.sofa.koupleless.arklet.core.command.coordinate.BizOpsPodCoordinator;
import com.alipay.sofa.koupleless.arklet.core.command.meta.AbstractCommandHandler;
import com.alipay.sofa.koupleless.arklet.core.command.meta.Command;
import com.alipay.sofa.koupleless.arklet.core.command.meta.Output;
Expand Down Expand Up @@ -54,8 +56,8 @@
* @version 1.0.0
*/
public class InstallBizHandler extends
AbstractCommandHandler<InstallBizHandler.Input, InstallBizHandler.InstallBizClientResponse>
implements ArkBizOps {
AbstractCommandHandler<InstallBizHandler.Input, InstallBizHandler.InstallBizClientResponse>
implements ArkBizOps {
private static final ArkletLogger LOGGER = ArkletLoggerFactory.getDefaultLogger();

/** {@inheritDoc} */
Expand All @@ -65,10 +67,13 @@ public Output<InstallBizClientResponse> handle(Input input) {
long startSpace = metaSpaceMXBean.getUsage().getUsed();
try {
InstallBizClientResponse installBizClientResponse = convertClientResponse(
getOperationService().install(convertInstallRequest(input)));
getOperationService().install(convertInstallRequest(input)));
installBizClientResponse
.setElapsedSpace(metaSpaceMXBean.getUsage().getUsed() - startSpace);
.setElapsedSpace(metaSpaceMXBean.getUsage().getUsed() - startSpace);
if (ResponseCode.SUCCESS.equals(installBizClientResponse.getCode())) {
String bizIdentity = BizIdentityUtils.generateBizIdentity(input.getBizName(), input.getBizVersion());
String bizModelVersion = input.getBizModelVersion();
BizOpsPodCoordinator.save(bizIdentity, bizModelVersion);
return Output.ofSuccess(installBizClientResponse);
} else {
return Output.ofFailed(installBizClientResponse, "install biz not success!");
Expand All @@ -80,8 +85,8 @@ public Output<InstallBizClientResponse> handle(Input input) {

private InstallRequest convertInstallRequest(Input input) {
return InstallRequest.builder().bizName(input.getBizName())
.bizVersion(input.getBizVersion()).bizUrl(input.getBizUrl()).args(input.getArgs())
.envs(input.getEnvs()).installStrategy(input.getInstallStrategy()).build();
.bizVersion(input.getBizVersion()).bizUrl(input.getBizUrl()).args(input.getArgs())
.envs(input.getEnvs()).installStrategy(input.getInstallStrategy()).build();
}

private InstallBizClientResponse convertClientResponse(ClientResponse res) {
Expand All @@ -106,12 +111,12 @@ public void validate(Input input) throws CommandValidationException {

public static void validateInput(Input input) throws CommandValidationException {
isTrue(!input.isAsync() || !StringUtils.isEmpty(input.getRequestId()),
"requestId should not be blank when async is true");
"requestId should not be blank when async is true");
notBlank(input.getBizUrl(), "bizUrl should not be blank");

if (StringUtils.isEmpty(input.getBizName()) || StringUtils.isEmpty(input.getBizVersion())) {
LOGGER.warn(
"biz name and version should not be empty, or it will reduce the performance.");
"biz name and version should not be empty, or it will reduce the performance.");
}

if (StringUtils.isEmpty(input.getBizName()) && StringUtils.isEmpty(input.getBizVersion())) {
Expand All @@ -120,16 +125,16 @@ public static void validateInput(Input input) throws CommandValidationException
refreshBizInfoFromJar(input);
} catch (IOException e) {
throw new CommandValidationException(
String.format("refresh biz info from jar failed: %s", e.getMessage()));
String.format("refresh biz info from jar failed: %s", e.getMessage()));
}
} else if (!StringUtils.isEmpty(input.getBizName())
&& !StringUtils.isEmpty(input.getBizVersion())) {
&& !StringUtils.isEmpty(input.getBizVersion())) {
// if bizName and bizVersion is not blank, it means that we should install the biz with the given bizName and bizVersion.
// do nothing.
} else {
// if bizName or bizVersion is blank, it is invalid, throw exception.
throw new CommandValidationException(
"bizName and bizVersion should be both blank or both not blank.");
"bizName and bizVersion should be both blank or both not blank.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@

import com.alipay.sofa.ark.api.ClientResponse;
import com.alipay.sofa.ark.api.ResponseCode;
import com.alipay.sofa.ark.common.util.BizIdentityUtils;
import com.alipay.sofa.ark.common.util.StringUtils;
import com.alipay.sofa.koupleless.arklet.core.command.builtin.BuiltinCommand;
import com.alipay.sofa.koupleless.arklet.core.command.builtin.handler.UninstallBizHandler.Input;
import com.alipay.sofa.koupleless.arklet.core.command.coordinate.BizOpsPodCoordinator;
import com.alipay.sofa.koupleless.arklet.core.command.meta.AbstractCommandHandler;
import com.alipay.sofa.koupleless.arklet.core.command.meta.Command;
import com.alipay.sofa.koupleless.arklet.core.command.meta.Output;
import com.alipay.sofa.koupleless.arklet.core.command.meta.bizops.ArkBizMeta;
import com.alipay.sofa.koupleless.arklet.core.command.meta.bizops.ArkBizOps;
import com.alipay.sofa.koupleless.arklet.core.common.exception.ArkletRuntimeException;
import com.alipay.sofa.koupleless.arklet.core.common.exception.CommandValidationException;
import com.alipay.sofa.koupleless.common.log.ArkletLogger;
import com.alipay.sofa.koupleless.common.log.ArkletLoggerFactory;

/**
* <p>UninstallBizHandler class.</p>
Expand All @@ -37,15 +41,25 @@
* @version 1.0.0
*/
public class UninstallBizHandler extends AbstractCommandHandler<Input, ClientResponse>
implements ArkBizOps {
implements ArkBizOps {

private static final ArkletLogger LOGGER = ArkletLoggerFactory.getDefaultLogger();

/** {@inheritDoc} */
@Override
public Output<ClientResponse> handle(Input input) {
try {
ClientResponse res = getOperationService().uninstall(input.getBizName(),
input.getBizVersion());
String bizIdentity = BizIdentityUtils.generateBizIdentity(input.getBizName(), input.getBizVersion());
String bizModelVersion = input.getBizModelVersion();
if (!BizOpsPodCoordinator.canAccess(bizIdentity, bizModelVersion)) {
LOGGER.error(
"can not access biz because the command is expired. bizIdentity: {}, bizModelVersion: {}",
bizIdentity, bizModelVersion);
return Output.ofFailed("can not access biz because the command is expired");
}
ClientResponse res = getOperationService().uninstall(input.getBizName(), input.getBizVersion());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Access check allows empty bizModelVersion — consider stricter rule behind a flag.

Current logic permits uninstall when request version is empty, even if a non‑empty version is recorded; this weakens protection against stale commands. Suggest denying when map has a non‑empty version and request is empty, gated by a compat flag.

Proposed guard:

-if (!BizOpsPodCoordinator.canAccess(bizIdentity, bizModelVersion)) {
+if (!BizOpsPodCoordinator.canAccess(bizIdentity, bizModelVersion)) {
     ...
 }

and inside coordinator (see separate comment) refine empty‑version handling or make it configurable.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java
around lines 52 to 60, the current access check allows an empty bizModelVersion
in the request to pass when a non-empty version is recorded, weakening
protection; update the guard to deny access when the stored coordinator version
is non-empty and the request bizModelVersion is empty, unless a
backward-compatibility flag is enabled. Implement this by: 1) querying
BizOpsPodCoordinator for the recorded version for bizIdentity before the
canAccess call, 2) if recordedVersion is non-empty and
input.getBizModelVersion() is empty, return Output.ofFailed(...) (or call
canAccess only after checking the compat flag), and 3) add a feature
flag/compatibility toggle (checked here or via BizOpsPodCoordinator) to allow
the old behavior when explicitly enabled.

if (ResponseCode.SUCCESS.equals(res.getCode())) {
BizOpsPodCoordinator.remove(bizIdentity, bizModelVersion);
return Output.ofSuccess(res);
Comment on lines 63 to 65
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Possible NPE on remove when bizModelVersion is null (ConcurrentHashMap disallows null value).

BizOpsPodCoordinator.remove(bizIdentity, bizModelVersion) will throw if bizModelVersion == null (legacy clients). Fix in coordinator by normalizing null/empty before calling Map.remove.

Apply coordinator fix shown in that file’s comment; alternatively, add a local guard:

- BizOpsPodCoordinator.remove(bizIdentity, bizModelVersion);
+ if (!StringUtils.isEmpty(bizModelVersion)) {
+     BizOpsPodCoordinator.remove(bizIdentity, bizModelVersion);
+ } else {
+     // legacy client: best-effort cleanup; safe to remove by key
+     BizOpsPodCoordinator.remove(bizIdentity, StringUtils.EMPTY_STRING);
+ }

Committable suggestion skipped: line range outside the PR's diff.

} else {
return Output.ofFailed(res, "uninstall biz not success!");
Expand All @@ -67,7 +81,7 @@ public void validate(Input input) throws CommandValidationException {
notBlank(input.getBizName(), "bizName should not be blank");
notBlank(input.getBizVersion(), "bizVersion should not be blank");
isTrue(!input.isAsync() || !StringUtils.isEmpty(input.getRequestId()),
"requestId should not be blank when async is true");
"requestId should not be blank when async is true");
}

public static class Input extends ArkBizMeta {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.koupleless.arklet.core.command.coordinate;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.alipay.sofa.ark.common.util.StringUtils;

/**
* <p>
* BizOpsPodCoordinator class.
* </p>
*
* @author liuzhuoheng
* @since 2025/7/15
* @version 1.0.0
*/
public class BizOpsPodCoordinator {

/**
* bizIdentityLockMap
* key: bizIdentity, value: bizModelVersion
*/
private static final Map<String, String> bizIdentityLockMap = new ConcurrentHashMap<>();

/**
* <p>
* save.
* </p>
*
* @param bizIdentity a {@link java.lang.String} object
* @param bizModelVersion a {@link java.lang.String} object
* @return
*/
public static void save(String bizIdentity, String bizModelVersion) {
if (StringUtils.isEmpty(bizIdentity)) {
return;
}
if (StringUtils.isEmpty(bizModelVersion)) {
bizModelVersion = StringUtils.EMPTY_STRING;
}
bizIdentityLockMap.put(bizIdentity, bizModelVersion);
}
Comment on lines +50 to +58
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate bizIdentity to avoid CHM NPE; normalize inputs.

ConcurrentHashMap disallows null keys; add a guard for bizIdentity and normalize bizModelVersion eagerly.

Apply:

 public static void save(String bizIdentity, String bizModelVersion) {
+    if (StringUtils.isEmpty(bizIdentity)) {
+        // ignore invalid key or consider throwing IllegalArgumentException
+        return;
+    }
     if (StringUtils.isEmpty(bizModelVersion)) {
         bizModelVersion = StringUtils.EMPTY_STRING;
     }
     bizIdentityLockMap.put(bizIdentity, bizModelVersion);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static void save(String bizIdentity, String bizModelVersion) {
if (StringUtils.isEmpty(bizModelVersion)) {
bizModelVersion = StringUtils.EMPTY_STRING;
}
bizIdentityLockMap.put(bizIdentity, bizModelVersion);
}
public static void save(String bizIdentity, String bizModelVersion) {
if (StringUtils.isEmpty(bizIdentity)) {
// ignore invalid key or consider throwing IllegalArgumentException
return;
}
if (StringUtils.isEmpty(bizModelVersion)) {
bizModelVersion = StringUtils.EMPTY_STRING;
}
bizIdentityLockMap.put(bizIdentity, bizModelVersion);
}
🤖 Prompt for AI Agents
In
arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java
around lines 50 to 55, the method saves into a ConcurrentHashMap without
validating the key which can cause a NullPointerException; update the method to
first validate bizIdentity (return or throw IllegalArgumentException if
null/blank) and normalize bizModelVersion eagerly (replace null with
StringUtils.EMPTY_STRING and trim it), then perform the put into
bizIdentityLockMap only with the validated non-null key and normalized value.


/**
* <p>
* remove.
* </p>
*
* @param bizIdentity a {@link java.lang.String} object
* @param bizModelVersion a {@link java.lang.String} object
* @return
*/
public static void remove(String bizIdentity, String bizModelVersion) {
if (StringUtils.isEmpty(bizIdentity)) {
return;
}
if (StringUtils.isEmpty(bizModelVersion)) {
bizIdentityLockMap.remove(bizIdentity);
return;
}
bizIdentityLockMap.remove(bizIdentity, bizModelVersion);
}

/**
* <p>
* canAccess.
* </p>
* 判断是否可以访问指定的业务模块,基于业务模块版本的协调机制
*
* @param bizIdentity 业务模块标识 (bizName:bizVersion)
* @param bizModelVersion 业务模块模型版本,用于命令协调和防止过期命令执行
* @return 是否允许访问该业务模块
*/
public static boolean canAccess(String bizIdentity, String bizModelVersion) {
// 判断逻辑说明:
// Case 1: bizModelVersion 为空 - 兼容性处理,允许访问(兼容旧版本 module-controller,arktcl,
// pod-not-exist 和 pod 紧急删除场景)
// Case 2: bizIdentityLockMap 中没有该 bizIdentity 的记录,允许访问(安装时不带
// BizModelVersion,卸载时带上 BizModelVersion)
// Case 3: bizIdentityLockMap 中的版本与当前请求的版本匹配 - 版本一致,确认卸载的是该 Biz,允许访问
// 只有当 bizModelVersion 不为空且存在 bizModelVersion 且不匹配时,才拒绝访问(防止旧的卸载命令执行)
return StringUtils.isEmpty(bizModelVersion)
|| StringUtils.isEmpty(bizIdentityLockMap.get(bizIdentity))
|| bizIdentityLockMap.get(bizIdentity).equals(bizModelVersion);
}
Comment on lines +90 to +101
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix NPE risks in canAccess: null key to CHM and TOCTOU on double get.

  • If bizIdentity is null/empty, ConcurrentHashMap.get(null) throws.
  • Two consecutive get calls can race with a concurrent remove(), making the second equals dereference NPE.

Proposed fix: guard bizIdentity and read the map once; compare safely.

-    public static boolean canAccess(String bizIdentity, String bizModelVersion) {
-        // 判断逻辑说明:
-        // Case 1: bizModelVersion 为空 - 兼容性处理,允许访问(兼容旧版本 module-controller,arktcl,
-        // pod-not-exist 和 pod 紧急删除场景)
-        // Case 2: bizIdentityLockMap 中没有该 bizIdentity 的记录,允许访问(安装时不带
-        // BizModelVersion,卸载时带上 BizModelVersion)
-        // Case 3: bizIdentityLockMap 中的版本与当前请求的版本匹配 - 版本一致,确认卸载的是该 Biz,允许访问
-        // 只有当 bizModelVersion 不为空且存在 bizModelVersion 且不匹配时,才拒绝访问(防止旧的卸载命令执行)
-        return StringUtils.isEmpty(bizModelVersion)
-                || StringUtils.isEmpty(bizIdentityLockMap.get(bizIdentity))
-                || bizIdentityLockMap.get(bizIdentity).equals(bizModelVersion);
-    }
+    public static boolean canAccess(String bizIdentity, String bizModelVersion) {
+        // Case 1: legacy / compatibility
+        if (StringUtils.isEmpty(bizModelVersion)) {
+            return true;
+        }
+        // Guard against null/blank identity (avoid CHM NPE). Choose safer default: deny.
+        if (StringUtils.isEmpty(bizIdentity)) {
+            return false;
+        }
+        final String recorded = bizIdentityLockMap.get(bizIdentity);
+        // Case 2: no record or empty record -> allow
+        // Case 3: exact match -> allow
+        return StringUtils.isEmpty(recorded) || bizModelVersion.equals(recorded);
+    }

Optional: if you prefer permissive behavior for blank bizIdentity, return true instead — please confirm desired policy.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java"

echo "PWD: $(pwd)"
if [ -f "$file" ]; then
  echo "=== FOUND: $file ==="
  nl -ba "$file" | sed -n '1,260p'
else
  echo "=== FILE NOT FOUND: $file ==="
  echo "Searching repo for class / filename..."
  rg -n --hidden --glob '!**/target/**' 'BizOpsPodCoordinator' -S || true
  rg -n --hidden --glob '!**/target/**' 'BizOpsPodCoordinator.java' -S || true
fi

echo
echo "=== Search for bizIdentityLockMap declaration/usages ==="
rg -n --hidden --glob '!**/target/**' 'bizIdentityLockMap' -S || true

echo
echo "=== Search for canAccess(...) call sites ==="
rg -n --hidden --glob '!**/target/**' 'canAccess\(' -S || true

Length of output: 189


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java"

echo "PWD: $(pwd)"
if [ -f "$file" ]; then
  echo "=== FOUND: $file ==="
  sed -n '1,240p' "$file"
else
  echo "=== FILE NOT FOUND: $file ==="
  echo "Searching for BizOpsPodCoordinator..."
  rg -n --hidden --glob '!**/target/**' 'class\s+BizOpsPodCoordinator' -S || true
fi

echo
echo "=== Search for bizIdentityLockMap declaration/usages ==="
rg -n --hidden --glob '!**/target/**' 'bizIdentityLockMap' -S || true

echo
echo "=== Search for canAccess(...) call sites ==="
rg -n --hidden --glob '!**/target/**' 'canAccess\(' -S || true

Length of output: 3944


Fix NPE & TOCTOU in canAccess — guard null identity and read map once

ConcurrentHashMap.get(null) throws NPE; calling get() twice races with concurrent remove() and can cause a .equals(...) NPE. Read the map once and validate bizIdentity before lookup.

-    public static boolean canAccess(String bizIdentity, String bizModelVersion) {
-        // 判断逻辑说明:
-        // Case 1: bizModelVersion 为空 - 兼容性处理,允许访问(兼容旧版本 module-controller,arktcl,
-        // pod-not-exist 和 pod 紧急删除场景)
-        // Case 2: bizIdentityLockMap 中没有该 bizIdentity 的记录,允许访问(安装时不带
-        // BizModelVersion,卸载时带上 BizModelVersion)
-        // Case 3: bizIdentityLockMap 中的版本与当前请求的版本匹配 - 版本一致,确认卸载的是该 Biz,允许访问
-        // 只有当 bizModelVersion 不为空且存在 bizModelVersion 且不匹配时,才拒绝访问(防止旧的卸载命令执行)
-        return StringUtils.isEmpty(bizModelVersion)
-                || StringUtils.isEmpty(bizIdentityLockMap.get(bizIdentity))
-                || bizIdentityLockMap.get(bizIdentity).equals(bizModelVersion);
-    }
+    public static boolean canAccess(String bizIdentity, String bizModelVersion) {
+        // Case 1: legacy / compatibility
+        if (StringUtils.isEmpty(bizModelVersion)) {
+            return true;
+        }
+        // Guard against null/blank identity (avoid CHM NPE). Choose safer default: deny.
+        if (StringUtils.isEmpty(bizIdentity)) {
+            return false;
+        }
+        final String recorded = bizIdentityLockMap.get(bizIdentity);
+        // Case 2: no record or empty record -> allow
+        // Case 3: exact match -> allow
+        return StringUtils.isEmpty(recorded) || bizModelVersion.equals(recorded);
+    }

If permissive behavior for blank bizIdentity is preferred, change the blank-identity branch to return true — confirm policy.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static boolean canAccess(String bizIdentity, String bizModelVersion) {
// 判断逻辑说明:
// Case 1: bizModelVersion 为空 - 兼容性处理,允许访问(兼容旧版本 module-controller,arktcl,
// pod-not-exist 和 pod 紧急删除场景)
// Case 2: bizIdentityLockMap 中没有该 bizIdentity 的记录,允许访问(安装时不带
// BizModelVersion,卸载时带上 BizModelVersion)
// Case 3: bizIdentityLockMap 中的版本与当前请求的版本匹配 - 版本一致,确认卸载的是该 Biz,允许访问
// 只有当 bizModelVersion 不为空且存在 bizModelVersion 且不匹配时,才拒绝访问(防止旧的卸载命令执行)
return StringUtils.isEmpty(bizModelVersion)
|| StringUtils.isEmpty(bizIdentityLockMap.get(bizIdentity))
|| bizIdentityLockMap.get(bizIdentity).equals(bizModelVersion);
}
public static boolean canAccess(String bizIdentity, String bizModelVersion) {
// Case 1: legacy / compatibility
if (StringUtils.isEmpty(bizModelVersion)) {
return true;
}
// Guard against null/blank identity (avoid CHM NPE). Choose safer default: deny.
if (StringUtils.isEmpty(bizIdentity)) {
return false;
}
final String recorded = bizIdentityLockMap.get(bizIdentity);
// Case 2: no record or empty record -> allow
// Case 3: exact match -> allow
return StringUtils.isEmpty(recorded) || bizModelVersion.equals(recorded);
}
🤖 Prompt for AI Agents
In
arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java
around lines 90 to 101, canAccess currently calls
bizIdentityLockMap.get(bizIdentity) twice and will NPE if bizIdentity is null
and can cause a TOCTOU .equals NPE when the map entry is removed concurrently;
fix by first validating bizIdentity (guard against null/blank according to
policy — if blank identity should be allowed, return true), then read
bizIdentityLockMap.get(bizIdentity) once into a local variable and use that
local for the emptiness check and equals comparison so no double lookup or
race-induced NPE occurs.

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class ArkBizMeta extends InputMeta {
private String bizName;
private String bizVersion;
private String requestId;
private String bizModelVersion;
private boolean async;

/**
Expand Down Expand Up @@ -85,6 +86,24 @@ public void setRequestId(String requestId) {
this.requestId = requestId;
}

/**
* <p>Getter for the field <code>bizModelVersion</code>.</p>
*
* @return a {@link java.lang.String} object
*/
public String getBizModelVersion() {
return bizModelVersion;
}

/**
* <p>Setter for the field <code>bizModelVersion</code>.</p>
*
* @param bizModelVersion a {@link java.lang.String} object
*/
public void setBizModelVersion(String bizModelVersion) {
this.bizModelVersion = bizModelVersion;
}

/**
* <p>isAsync.</p>
*
Expand Down
Loading
Loading