Skip to content

[feature] Desensitize sensitive information(new) #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions hertzbeat-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,20 @@
<version>${poi.version}</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
<scope>compile</scope>
</dependency>

<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
Comment on lines +147 to +151

Choose a reason for hiding this comment

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

medium

Consider using specific hutool modules instead of hutool-all to reduce the application size. For the functionality used in this PR (StrUtil, DesensitizedUtil), hutool-core might be sufficient.

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ private CacheFactory() {}

private static final CommonCacheService<String, Object> ALERT_CONVERGE_CACHE =
new CaffeineCacheServiceImpl<>(10, 1000, Duration.ofDays(1), false);

private static final CommonCacheService<String, Object> DESENSITIZATION_MAP_CACHE =
new CaffeineCacheServiceImpl<>(10, 1000, Duration.ofDays(1), false);

/**
* get notice cache
Expand All @@ -57,4 +60,10 @@ public static CommonCacheService<String, Object> getAlertSilenceCache() {
public static CommonCacheService<String, Object> getAlertConvergeCache() {
Copy link

Choose a reason for hiding this comment

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

Sensitive Data Exposure through Unprotected Cache

The implementation stores original sensitive data (emails and phone numbers) in a cache that lacks access controls. While the data is desensitized in API responses, there's no mechanism to ensure that only authorized code can retrieve the original values from the cache. The cache has a long expiration time (1 day) and doesn't implement any authentication or authorization checks for data access.

Suggested change
public static CommonCacheService<String, Object> getAlertConvergeCache() {
private static final CommonCacheService<String, Object> DESENSITIZATION_MAP_CACHE =
new CaffeineCacheServiceImpl<>(10, 1000, Duration.ofMinutes(30), false);
/**
* Get desensitizationMap cache with appropriate access control checks
* @param accessContext The context of the access request for authorization
* @return desensitizationMap cache if authorized
*/
Rationale
  • Reducing the cache expiration time from 1 day to 30 minutes minimizes the window of exposure if the cache is compromised
  • Adding documentation that reminds developers to implement proper access control when retrieving sensitive data
  • In a complete implementation, the method should verify the caller has appropriate permissions before returning the original sensitive data
References
  • CWE: CWE-922
  • OWASP: A02:2021-Cryptographic Failures

return ALERT_CONVERGE_CACHE;
}

/**
* get desensitizationMap cache
* @return desensitizationMap cache
*/
public static CommonCacheService<String, Object> getDesensitizationMapCache(){return DESENSITIZATION_MAP_CACHE;};

Choose a reason for hiding this comment

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

medium

For better readability, format this method across multiple lines.

    public static CommonCacheService<String, Object> getDesensitizationMapCache() {
        return DESENSITIZATION_MAP_CACHE;
    }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*
* 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 org.apache.hertzbeat.common.entity.dto.vo;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;

Choose a reason for hiding this comment

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

medium

The NoticeReceiverVO is a DTO, and should not have JPA annotations. Remove @Column.

import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
Comment on lines +22 to +25

Choose a reason for hiding this comment

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

medium

The NoticeReceiverVO is a DTO, and should not have JPA annotations. Remove @Id and @GeneratedValue.

Comment on lines +22 to +25
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove JPA entity annotations from VO class.

A VO (Value Object/View Object) should not contain JPA entity annotations like @Id, @GeneratedValue, @Column. These annotations are for entity classes that map to database tables, not for DTOs used for data transfer.

-import jakarta.persistence.Column;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;

-@Id
-@GeneratedValue(strategy = GenerationType.IDENTITY)
 @Schema(title = "Recipient entity primary key index ID", description = "Recipient entity primary key index ID",
         example = "87584674384", accessMode = READ_ONLY)
 private Long id;

-@Column(length = 300)
 private String hookUrl;

Apply this pattern to remove all @Column annotations throughout the class.

Also applies to: 55-56, 97-98

🤖 Prompt for AI Agents
In
hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/dto/vo/NoticeReceiverVO.java
at lines 22-25, 55-56, and 97-98, remove all JPA entity annotations such as @Id,
@GeneratedValue, and @Column from the VO class. These annotations are
inappropriate for a Value Object and should be eliminated to keep the class
focused on data transfer without persistence mapping.

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.serialize.EmailDesensitizationSerializer;
import org.apache.hertzbeat.common.serialize.PhoneDesensitizationSerializer;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
Comment on lines +36 to +39
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove Spring Data auditing annotations from VO class.

Spring Data auditing annotations (@CreatedBy, @LastModifiedBy, @CreatedDate, @LastModifiedDate) are meant for entity classes, not VOs. These annotations won't function in a VO and create confusion about the class's purpose.

-import org.springframework.data.annotation.CreatedBy;
-import org.springframework.data.annotation.CreatedDate;
-import org.springframework.data.annotation.LastModifiedBy;
-import org.springframework.data.annotation.LastModifiedDate;

 @Schema(title = "The creator of this record", example = "tom",
         accessMode = READ_ONLY)
-@CreatedBy
 private String creator;

 @Schema(title = "This record was last modified by", example = "tom", accessMode = READ_ONLY)
-@LastModifiedBy
 private String modifier;

 @Schema(title = "Record creation time (millisecond timestamp)",
         example = "1612198922000", accessMode = READ_ONLY)
-@CreatedDate
 private LocalDateTime gmtCreate;

 @Schema(title = "Record the latest modification time (timestamp in milliseconds)",
         example = "1612198444000", accessMode = READ_ONLY)
-@LastModifiedDate
 private LocalDateTime gmtUpdate;

Also applies to: 226-241

🤖 Prompt for AI Agents
In
hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/dto/vo/NoticeReceiverVO.java
at lines 36-39 and 226-241, remove the Spring Data auditing annotations
@CreatedBy, @LastModifiedBy, @CreatedDate, and @LastModifiedDate from the VO
class. These annotations are intended for entity classes and do not function
properly in VO classes, so deleting them will clarify the class's purpose and
avoid confusion.


import java.time.LocalDateTime;

import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;

/**
* 2024-12-06
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class NoticeReceiverVO {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider the class design and naming convention.

The class appears to be a direct copy of an entity class with serializers added. Consider whether this is truly a VO/DTO or if you should:

  1. Create a proper mapping between entity and VO
  2. Keep only the fields needed for the specific view/transfer purpose
  3. Consider renaming to NoticeReceiverDTO to better reflect its purpose
🤖 Prompt for AI Agents
In
hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/dto/vo/NoticeReceiverVO.java
at line 53, the class is currently named NoticeReceiverVO but appears to be a
direct copy of an entity class with serializers added. To fix this, create a
proper mapping between the entity and this class, retain only the fields
necessary for the view or transfer purpose, and consider renaming the class to
NoticeReceiverDTO to better reflect its role as a data transfer object.


@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Recipient entity primary key index ID", description = "Recipient entity primary key index ID",
example = "87584674384", accessMode = READ_ONLY)
private Long id;

@Schema(title = "Recipient name", description = "Recipient name",
example = "tom", accessMode = READ_WRITE)
@Size(max = 100)
@NotBlank(message = "name can not null")
private String name;

@Schema(title = "Notification information method: 0-SMS 1-Email 2-webhook 3-WeChat Official Account 4-Enterprise WeChat Robot "
+ "5-DingTalk Robot 6-FeiShu Robot 7-Telegram Bot 8-SlackWebHook 9-Discord Bot 10-Enterprise WeChat app message",
description = "Notification information method: "
+ "0-SMS 1-Email 2-webhook 3-WeChat Official Account "
+ "4-Enterprise WeChat Robot 5-DingTalk Robot 6-FeiShu Robot "
+ "7-Telegram Bot 8-SlackWebHook 9-Discord Bot 10-Enterprise "
+ "WeChat app message",
accessMode = READ_WRITE)
@Min(0)
@NotNull(message = "type can not null")
private Byte type;

@Schema(title = "Mobile number: Valid when the notification method is SMS",
description = "Mobile number: Valid when the notification method is SMS",
example = "18923435643", accessMode = READ_WRITE)
@Size(max = 100)
@JsonSerialize(using = PhoneDesensitizationSerializer.class)
private String phone;

@Schema(title = "Email account: Valid when the notification method is email",
description = "Email account: Valid when the notification method is email",
example = "[email protected]", accessMode = READ_WRITE)
@Size(max = 100)
@JsonSerialize(using = EmailDesensitizationSerializer.class)
private String email;

@Schema(title = "URL address: The notification method is valid for webhook",
description = "URL address: The notification method is valid for webhook",
example = "https://www.tancloud.cn", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String hookUrl;

@Schema(title = "openId : The notification method is valid for WeChat official account, enterprise WeChat robot or FlyBook robot",
description = "openId : The notification method is valid for WeChat official account, enterprise WeChat robot or FlyBook robot",
example = "343432", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String wechatId;

@Schema(title = "Access token : The notification method is valid for DingTalk robot",
description = "Access token : The notification method is valid for DingTalk robot",
example = "34823984635647", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String accessToken;

@Schema(title = "Telegram bot token : The notification method is valid for Telegram Bot",
description = "Telegram bot token : The notification method is valid for Telegram Bot",
example = "1499012345:AAEOB_wEYS-DZyPM3h5NzI8voJMXXXXXX", accessMode = READ_WRITE)
private String tgBotToken;

@Schema(title = "Telegram user id: The notification method is valid for Telegram Bot",
description = "Telegram user id: The notification method is valid for Telegram Bot",
example = "779294123", accessMode = READ_WRITE)
private String tgUserId;

@Schema(title = "DingTalk,FeiShu,WeWork user id: The notification method is valid for DingTalk,FeiShu,WeWork Bot",
description = "DingTalk,FeiShu,WeWork user id: The notification method is valid for DingTalk,FeiShu,WeWork Bot",
example = "779294123", accessMode = READ_WRITE)
private String userId;

@Schema(title = "URL address: The notification method is valid for Slack",
description = "URL address: The notification method is valid for Slack",
example = "https://hooks.slack.com/services/XXXX/XXXX/XXXX", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String slackWebHookUrl;

@Schema(title = "Enterprise weChat message: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat message: The notification method is valid for Enterprise WeChat app message",
example = "ww1a603432123d0dc1", accessMode = READ_WRITE)
private String corpId;

@Schema(title = "Enterprise weChat appId: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat appId: The notification method is valid for Enterprise WeChat app message",
example = "1000001", accessMode = READ_WRITE)
private Integer agentId;

@Schema(title = "Enterprise weChat secret: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat secret: The notification method is valid for Enterprise WeChat app message",
example = "oUydwn92ey0lnuY02MixNa57eNK-20dJn5NEOG-u2uE", accessMode = READ_WRITE)
private String appSecret;

@Schema(title = "Enterprise weChat party id: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat party id: The notification method is valid for Enterprise WeChat app message",
example = "779294123", accessMode = READ_WRITE)
private String partyId;

@Schema(title = "Enterprise weChat tag id: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat tag id: The notification method is valid for Enterprise WeChat app message",
example = "779294123", accessMode = READ_WRITE)
private String tagId;

@Schema(title = "Discord channel id: The notification method is valid for Discord",
description = "Discord channel id: The notification method is valid for Discord",
example = "1065303416030642266", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String discordChannelId;

@Schema(title = "Discord bot token: The notification method is valid for Discord",
description = "Discord bot token: The notification method is valid for Discord",
example = "MTA2NTMwMzU0ODY4Mzg4MjUzNw.xxxxx.xxxxxxx", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String discordBotToken;

@Schema(title = "huawei cloud SMN ak: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN ak: If the notification method is valid for huawei cloud SMN",
example = "NCVBODJOEYHSW3VNXXXX", accessMode = READ_WRITE)
@Size(max = 22)
@Column(length = 22)
private String smnAk;

@Schema(title = "huawei cloud SMN sk: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN sk: If the notification method is valid for huawei cloud SMN",
example = "nmSNhUJN9MlpPl8lfCsgdA0KvHCL9JXXXX", accessMode = READ_WRITE)
@Size(max = 42)
@Column(length = 42)
private String smnSk;

@Schema(title = "huawei cloud SMN projectId: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN projectId: If the notification method is valid for huawei cloud SMN",
example = "320c2fb11edb47a481c299c1XXXXXX", accessMode = READ_WRITE)
@Size(max = 32)
@Column(length = 32)
private String smnProjectId;

@Schema(title = "huawei cloud SMN region: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN region: If the notification method is valid for huawei cloud SMN",
example = "cn-east-3", accessMode = READ_WRITE)
@Size(max = 32)
@Column(length = 32)
private String smnRegion;

@Schema(title = "huawei cloud SMN TopicUrn: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN TopicUrn: If the notification method is valid for huawei cloud SMN",
example = "urn:smn:cn-east-3:xxx:hertzbeat_test", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String smnTopicUrn;

@Schema(title = "serverChanToken : The notification method is valid for ServerChan",
description = "serverChanToken : The notification method is valid for ServerChan",
example = "SCT193569TSNm6xIabdjqeZPtOGOWcvU1e", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String serverChanToken;

@Schema(title = "Gotify token : The notification method is valid for Gotify",
description = "Gotify token : The notification method is valid for Gotify",
example = "A845h__ZMqDxZlO", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String gotifyToken;

@Schema(title = "The creator of this record", example = "tom",
accessMode = READ_ONLY)
@CreatedBy
private String creator;

@Schema(title = "This record was last modified by", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;

@Schema(title = "Record creation time (millisecond timestamp)",
example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;

@Schema(title = "Record the latest modification time (timestamp in milliseconds)",
example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 org.apache.hertzbeat.common.serialize;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.hertzbeat.common.cache.CacheFactory;
import org.apache.hertzbeat.common.cache.CommonCacheService;
import org.apache.hertzbeat.common.entity.dto.vo.NoticeReceiverVO;

import java.io.IOException;

/**
* 2024-12-06
* Email Desensitizing serializer
*/
public class EmailDesensitizationSerializer extends JsonSerializer<String> {

@Override
public void serialize(String email, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
String emailDesensitization = "";
CommonCacheService<String, Object> desensitizationMapCache = CacheFactory.getDesensitizationMapCache();
if (StrUtil.isNotBlank(email)) {
int index = StrUtil.indexOf(email, '@');
emailDesensitization = index <= 1 ? email :
StrUtil.replace(email, 1, index, '*');
Comment on lines +41 to +43
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve email desensitization logic and use Hutool's built-in method.

The current email desensitization logic has several issues:

  1. No validation that @ symbol exists in the email
  2. Doesn't handle edge cases where @ is at position 0 or 1
  3. Hutool already provides DesensitizedUtil.email() method
-int index = StrUtil.indexOf(email, '@');
-emailDesensitization = index <= 1 ? email :
-        StrUtil.replace(email, 1, index, '*');
+emailDesensitization = DesensitizedUtil.email(email);
📝 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
int index = StrUtil.indexOf(email, '@');
emailDesensitization = index <= 1 ? email :
StrUtil.replace(email, 1, index, '*');
- int index = StrUtil.indexOf(email, '@');
- emailDesensitization = index <= 1 ? email :
- StrUtil.replace(email, 1, index, '*');
+ emailDesensitization = DesensitizedUtil.email(email);
🤖 Prompt for AI Agents
In
hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/EmailDesensitizationSerializer.java
around lines 41 to 43, the current email desensitization logic lacks validation
for the presence of '@' and does not handle edge cases where '@' is at position
0 or 1. Replace this custom logic with Hutool's built-in
DesensitizedUtil.email() method, which properly validates and desensitizes email
addresses, ensuring robust handling of all edge cases.

NoticeReceiverVO currentValue = (NoticeReceiverVO) jsonGenerator.getOutputContext().getCurrentValue();
desensitizationMapCache.put(currentValue.getId() + "_" + emailDesensitization, email);
}
jsonGenerator.writeString(emailDesensitization);
}

Choose a reason for hiding this comment

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

medium

Add a newline character at the end of the file.

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 org.apache.hertzbeat.common.serialize;

import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.hertzbeat.common.cache.CacheFactory;
import org.apache.hertzbeat.common.cache.CommonCacheService;
import org.apache.hertzbeat.common.entity.dto.vo.NoticeReceiverVO;

import java.io.IOException;

/**
* 2024-12-06
* Phone Desensitizing serializer
*/
public class PhoneDesensitizationSerializer extends JsonSerializer<String> {

@Override
public void serialize(String phone, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
String phoneDesensitization = "";
CommonCacheService<String, Object> desensitizationMapCache = CacheFactory.getDesensitizationMapCache();
if (StrUtil.isNotBlank(phone)){
phoneDesensitization = DesensitizedUtil.mobilePhone(phone);
NoticeReceiverVO currentValue = (NoticeReceiverVO)jsonGenerator.getOutputContext().getCurrentValue();
desensitizationMapCache.put(currentValue.getId()+"_"+phoneDesensitization, phone);
Comment on lines +43 to +44
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null safety checks and improve reusability.

The code has several issues:

  1. No null check for currentValue.getId() before string concatenation
  2. Hard-coded cast to NoticeReceiverVO makes the serializer non-reusable
  3. Missing error handling for cache operations
-NoticeReceiverVO currentValue = (NoticeReceiverVO)jsonGenerator.getOutputContext().getCurrentValue();
-desensitizationMapCache.put(currentValue.getId()+"_"+phoneDesensitization, phone);
+Object currentObj = jsonGenerator.getOutputContext().getCurrentValue();
+if (currentObj instanceof NoticeReceiverVO) {
+    NoticeReceiverVO currentValue = (NoticeReceiverVO) currentObj;
+    if (currentValue.getId() != null) {
+        try {
+            desensitizationMapCache.put(currentValue.getId() + "_" + phoneDesensitization, phone);
+        } catch (Exception e) {
+            // Log error but don't fail serialization
+            // Consider using a logger here
+        }
+    }
+}
📝 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
NoticeReceiverVO currentValue = (NoticeReceiverVO)jsonGenerator.getOutputContext().getCurrentValue();
desensitizationMapCache.put(currentValue.getId()+"_"+phoneDesensitization, phone);
Object currentObj = jsonGenerator.getOutputContext().getCurrentValue();
if (currentObj instanceof NoticeReceiverVO) {
NoticeReceiverVO currentValue = (NoticeReceiverVO) currentObj;
if (currentValue.getId() != null) {
try {
desensitizationMapCache.put(
currentValue.getId() + "_" + phoneDesensitization,
phone
);
} catch (Exception e) {
// Log error but don't fail serialization
// Consider using a logger here
}
}
}
🤖 Prompt for AI Agents
In
hertzbeat-common/src/main/java/org/apache/hertzbeat/common/serialize/PhoneDesensitizationSerializer.java
around lines 43 to 44, add a null check for currentValue and
currentValue.getId() before concatenating to avoid NullPointerException.
Refactor the code to avoid hard-coded casting to NoticeReceiverVO by making the
serializer generic or accepting a more flexible type. Also, add error handling
around the desensitizationMapCache.put operation to catch and log any exceptions
during cache updates.

}

jsonGenerator.writeString(phoneDesensitization);
}

Choose a reason for hiding this comment

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

medium

Add a newline character at the end of the file.

}

}
Loading