-
Notifications
You must be signed in to change notification settings - Fork 0
[feature] Desensitize sensitive information(new) #13
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
base: master
Are you sure you want to change the base?
Changes from all commits
c9adee8
ab96101
578337c
d2a8725
5b34623
8ade3d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Expiration RiskDesensitization cache uses a long expiration time (1 day) which increases sensitive data exposure risk. Attackers with access to the application could access sensitive data longer than necessary. Standards
Comment on lines
+37
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Size LimitationThe DESENSITIZATION_MAP_CACHE is configured with a maximum size of 1000 entries and TTL of 1 day. For large systems with many users viewing sensitive data, this could lead to cache evictions and reduced performance. The cache size should be adjusted based on expected user load and sensitive data volume. Standards
Comment on lines
+37
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Cleanup MissingThe desensitization cache stores sensitive data for 1 day without explicit cleanup mechanism. This can lead to sensitive data persisting longer than necessary, creating potential data exposure risks. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Cache ExpirationSensitive data cache retains information for 1 day without explicit expiration control. Extended cache retention increases exposure window of sensitive information. Shorter TTL reduces risk window. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Eviction StrategyDesensitization cache uses fixed 1-day expiration without explicit eviction strategy. This could lead to memory pressure if many records are processed. Consider implementing a more aggressive expiration policy or size-based eviction. Standards
|
||
|
|
||
| /** | ||
| * get notice cache | ||
|
|
@@ -57,4 +60,10 @@ public static CommonCacheService<String, Object> getAlertSilenceCache() { | |
| public static CommonCacheService<String, Object> getAlertConvergeCache() { | ||
| return ALERT_CONVERGE_CACHE; | ||
| } | ||
|
|
||
| /** | ||
| * get desensitizationMap cache | ||
| * @return desensitizationMap cache | ||
| */ | ||
| public static CommonCacheService<String, Object> getDesensitizationMapCache(){return DESENSITIZATION_MAP_CACHE;}; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| 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; | ||
| import jakarta.persistence.GeneratedValue; | ||
| import jakarta.persistence.GenerationType; | ||
| import jakarta.persistence.Id; | ||
| 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; | ||
|
|
||
| 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 { | ||
|
|
||
| @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 { | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Javadoc CommentsClass and method lack Javadoc comments explaining purpose and behavior. Missing documentation reduces maintainability by making code intent and usage less clear for future developers. Standards
|
||||||||||||||||||||||||||||||||||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inefficient Email DesensitizationThe current implementation uses StrUtil.replace() which creates a new string for each desensitization operation. For high-volume systems processing many emails, this creates unnecessary object allocations and GC pressure. The string replacement operation has O(n) complexity where n is the length of the email string. Standards
Comment on lines
+41
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Email Desensitization LogicThe email desensitization logic has an edge case where emails with only one character before @ (e.g., [email protected]) won't be desensitized at all. This contradicts the desensitization purpose and creates inconsistent behavior between similar email addresses. Standards
|
||||||||||||||||||||||||||||||||||
| NoticeReceiverVO currentValue = (NoticeReceiverVO) jsonGenerator.getOutputContext().getCurrentValue(); | ||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Casting |
||||||||||||||||||||||||||||||||||
| desensitizationMapCache.put(currentValue.getId() + "_" + emailDesensitization, email); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+44
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Key CollisionCache key construction using desensitized value creates potential collisions when different emails produce identical desensitized patterns. This can cause incorrect data retrieval during restoration.
Suggested change
Standards
Comment on lines
+44
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Entry LeakageCache entries are created but never removed, potentially causing memory leaks over time. Long-lived cached sensitive data increases exposure risk and memory consumption. Standards
Comment on lines
+44
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Key CollisionCache key uses desensitized email which creates collision risk when multiple receivers have similar emails like '[email protected]' and '[email protected]' (both masked as '*@example.com'). This would overwrite cache entries causing data retrieval errors. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Key CollisionCache key uses desensitized email which could cause key collisions when different emails desensitize to the same pattern. This creates a risk of retrieving incorrect original emails during restoration. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential Cache LeakageCache entries are created but never expired or cleaned up, potentially causing memory leaks as the cache grows unbounded over time. Without TTL or eviction policy, sensitive data remains in memory indefinitely. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Key CollisionCache key construction using desensitized value creates potential collision risk. Multiple different emails could produce identical desensitized values, causing cache overwrite and incorrect data retrieval. This would lead to incorrect information being restored during edit operations. Commitable Suggestion
Suggested change
Standards
Comment on lines
+44
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Null ID HandlingThe code assumes currentValue.getId() is non-null when constructing the cache key, but there's no null check. If ID is null during serialization (e.g., for new unsaved entities), this will cause a NullPointerException when concatenating with the string. Commitable Suggestion
Suggested change
Standards
Comment on lines
+43
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Security RiskSensitive email data is stored in a cache without expiration controls or access restrictions. This creates a potential data exposure risk if the cache is compromised or accessed by unauthorized code. The original sensitive data remains accessible through the cache indefinitely. Standards
|
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| jsonGenerator.writeString(emailDesensitization); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+36
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Null Safety ImprovementThe code assumes currentValue is always a NoticeReceiverVO and never null. If the serializer is used with a different context, it could cause NullPointerException during desensitization. Standards
Comment on lines
+38
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent Null HandlingThe code handles null/blank emails by setting emailDesensitization to an empty string, but doesn't add anything to the cache. This creates inconsistent behavior where desensitized empty values can't be reversed through the cache lookup mechanism used elsewhere. Standards
|
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| 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(); | ||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Casting |
||||||||||||
| desensitizationMapCache.put(currentValue.getId()+"_"+phoneDesensitization, phone); | ||||||||||||
|
Comment on lines
+42
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Null ID RiskNo null check on currentValue.getId() before cache key creation. NullPointerException risk if serializing object with null ID, causing serialization failures. Standards
Comment on lines
+43
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Phone Desensitization CollisionCache key uses desensitized phone which creates collision risk when multiple receivers have similar phone numbers that produce identical masked values. This would overwrite cache entries causing data retrieval errors. Standards
Comment on lines
+42
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Key CollisionPhone serializer uses the same cache key pattern as email serializer without type distinction. This creates potential key collisions when phone and email desensitize to similar patterns, causing data restoration errors. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Key CollisionCache key construction using desensitized value creates potential collision risk. Different phone numbers with same desensitized pattern could overwrite each other in cache. This would lead to incorrect information being restored during edit operations. Commitable Suggestion
Suggested change
Standards
Comment on lines
+43
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Null CheckNo null check on currentValue or its getId() method before using in cache key. NullPointerException could occur if the context value is null or doesn't have a valid ID, causing serialization failure and potential data loss. Standards
Comment on lines
+42
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cache Security RiskSensitive phone number data is stored in a cache without expiration controls or access restrictions. This creates a potential data exposure risk if the cache is compromised or accessed by unauthorized code. The original sensitive data remains accessible through the cache indefinitely. Standards
|
||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| jsonGenerator.writeString(phoneDesensitization); | ||||||||||||
|
Comment on lines
+36
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Null Safety ImprovementSimilar to the email serializer, this code assumes currentValue is always a NoticeReceiverVO and never null. Missing null checks could cause NullPointerException during phone desensitization. Standards
|
||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+35
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate Cache LogicSimilar caching logic is duplicated across Email and Phone serializers. This violates DRY principle and makes future changes error-prone as modifications must be synchronized across multiple classes. Standards
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cache Expiration Risk
Sensitive data stored in cache for 1 day creates unnecessary exposure risk. Long-lived sensitive data cache increases potential compromise window if application is breached.
Standards