package com.battery.device.bts; import java.util.Locale; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 新威 BTS 对外接口的**只读命令白名单**(ADR-0068 §1)。 * *

启用厂商对外接口等于同时打开了控制通道:厂商协议里 {@code start}/{@code stop}/{@code reset}/ * {@code broadcaststop} 与只读命令走同一条管道、同一套报文格式,服务端不做区分。 * 因此"只观测不控制"这条不变量在新威这条路上**不是服务端保证,而是代码纪律**, * 落点就是这里:每条出站请求在发出前按 {@code } 取值比对白名单,不在白名单内的一律拒绝。 * *

为什么拦在传输层而不是各调用处:调用处会随需求增加,白名单只有一处才可能被审计。 * 独立成类而不写在传输类里,是为了让这条边界能被单独测到(不需要真的有一条管道)。 */ final class NewareBtsReadOnlyCommands { /** 允许发送的命令,全部是只读命令;取值来自《BTS API技术协议》与现场实测(ADR-0068)。 */ private static final Set ALLOWED = Set.of( "getdevinfo", "getchlstatus", "inquire", "inquiredf", "download", "downloadsteplayer", "downloadlog", "getparallel", "getpause"); private static final Pattern COMMAND = Pattern.compile("\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*"); private NewareBtsReadOnlyCommands() { } /** * 校验请求只含白名单内的命令,返回解析出的命令名(小写)。 * *

没有 {@code } 的报文不是本协议的请求,同样拒绝——放行一个解析不出命令的报文, * 等于把"这道门"的判定权交给厂商服务端。 * * @param request 完整请求报文 * @return 请求里的命令名(小写) * @throws BtsTransportException 缺少命令,或命令不在只读白名单内 */ static String requireAllowed(String request) { Matcher matcher = request == null ? null : COMMAND.matcher(request); if (matcher == null || !matcher.find()) throw new BtsTransportException(BtsTransportException.Reason.READ_ONLY_REJECTED, "BTS 请求缺少 ,拒绝发出", null); String command = matcher.group(1).toLowerCase(Locale.ROOT); if (!ALLOWED.contains(command)) throw new BtsTransportException(BtsTransportException.Reason.READ_ONLY_REJECTED, "BTS 只读白名单外的命令被拒绝:" + command, null); return command; } }