Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 3.9k
[fix](data security) Mask ai.api_key in CREATE/ALTER RESOURCE logs#66006
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
501db33b2e604ebe9a1929a956e84d1858672307e9e449fbe075e1a6eaf6ef13207a3144b4ca8File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -24,7 +24,6 @@ | ||
| import org.apache.doris.nereids.DorisParser; | ||
| import org.apache.doris.nereids.DorisParser.InsertTableContext; | ||
| import org.apache.doris.nereids.DorisParser.JobFromToClauseContext; | ||
| import org.apache.doris.nereids.DorisParser.SupportedDmlStatementContext; | ||
| import org.apache.doris.nereids.trees.plans.commands.info.SetVarOp; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; | ||
| @@ -203,6 +202,17 @@ public LogicalPlan visitAlterAuthenticationIntegrationProperties( | ||
| return super.visitAlterAuthenticationIntegrationProperties(ctx); | ||
| } | ||
| @Override | ||
| public LogicalPlan visitAlterResource(DorisParser.AlterResourceContext ctx) { | ||
| if (ctx.propertyClause() != null) { | ||
| DorisParser.PropertyClauseContext propertyClauseContext = ctx.propertyClause(); | ||
| encryptProperty(visitPropertyClause(propertyClauseContext), | ||
| propertyClauseContext.fileProperties.start.getStartIndex(), | ||
| propertyClauseContext.fileProperties.stop.getStopIndex()); | ||
| } | ||
| return super.visitAlterResource(ctx); | ||
| } | ||
| // select from tvf | ||
| @Override | ||
| public LogicalPlan visitTableValuedFunction(DorisParser.TableValuedFunctionContext ctx) { | ||
| @@ -217,9 +227,8 @@ public LogicalPlan visitTableValuedFunction(DorisParser.TableValuedFunctionConte | ||
| // create job select tvf | ||
| @Override | ||
| public LogicalPlan visitCreateScheduledJob(DorisParser.CreateScheduledJobContext ctx) { | ||
| if (ctx.supportedDmlStatement() != null) { | ||
| SupportedDmlStatementContext supportedDmlStatementContext = ctx.supportedDmlStatement(); | ||
| visitInsertTable((InsertTableContext) supportedDmlStatementContext); | ||
| if (ctx.supportedDmlStatement() instanceof InsertTableContext) { | ||
wenzhenghu marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| visitInsertTable((InsertTableContext) ctx.supportedDmlStatement()); | ||
| } else if (ctx.jobFromToClause() != null) { | ||
| JobFromToClauseContext jobFromToClauseContext = ctx.jobFromToClause(); | ||
| encryptProperty(visitPropertyItemList(jobFromToClauseContext.sourceProperties), | ||
| @@ -233,9 +242,8 @@ public LogicalPlan visitCreateScheduledJob(DorisParser.CreateScheduledJobContext | ||
| // alter job select tvf | ||
| @Override | ||
| public LogicalPlan visitAlterJob(DorisParser.AlterJobContext ctx) { | ||
| SupportedDmlStatementContext supportedDmlStatementContext = ctx.supportedDmlStatement(); | ||
| if (ctx.supportedDmlStatement() != null) { | ||
| visitInsertTable((InsertTableContext) supportedDmlStatementContext); | ||
| if (ctx.supportedDmlStatement() instanceof InsertTableContext) { | ||
| visitInsertTable((InsertTableContext) ctx.supportedDmlStatement()); | ||
| } else if (ctx.jobFromToClause() != null) { | ||
| JobFromToClauseContext jobFromToClauseContext = ctx.jobFromToClause(); | ||
| encryptProperty(visitPropertyItemList(jobFromToClauseContext.sourceProperties), | ||
| @@ -246,6 +254,17 @@ public LogicalPlan visitAlterJob(DorisParser.AlterJobContext ctx) { | ||
| return super.visitAlterJob(ctx); | ||
| } | ||
| @Override | ||
| public LogicalPlan visitCreateResource(DorisParser.CreateResourceContext ctx) { | ||
| if (ctx.properties != null) { | ||
| DorisParser.PropertyClauseContext propertyClauseContext = ctx.properties; | ||
| encryptProperty(visitPropertyClause(propertyClauseContext), | ||
| propertyClauseContext.fileProperties.start.getStartIndex(), | ||
| propertyClauseContext.fileProperties.stop.getStopIndex()); | ||
| } | ||
| return super.visitCreateResource(ctx); | ||
| } | ||
| private void encryptProperty(Map<String, String> properties, int start, int stop) { | ||
| if (MapUtils.isNotEmpty(properties)) { | ||
| DatasourcePrintableMap<String, String> printableMap = new DatasourcePrintableMap<>(properties, "=", | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -100,6 +100,7 @@ | ||
| import org.apache.doris.nereids.trees.plans.commands.EmptyCommand; | ||
| import org.apache.doris.nereids.trees.plans.commands.Forward; | ||
| import org.apache.doris.nereids.trees.plans.commands.LoadCommand; | ||
| import org.apache.doris.nereids.trees.plans.commands.NeedAuditEncryption; | ||
| import org.apache.doris.nereids.trees.plans.commands.PrepareCommand; | ||
| import org.apache.doris.nereids.trees.plans.commands.Redirect; | ||
| import org.apache.doris.nereids.trees.plans.commands.SupportProfile; | ||
| @@ -182,6 +183,7 @@ public class StmtExecutor { | ||
| private static final Logger LOG = LogManager.getLogger(StmtExecutor.class); | ||
| private static final AtomicLong STMT_ID_GENERATOR = new AtomicLong(0); | ||
| private static final String MASKED_STMT_FALLBACK = "/* masked statement unavailable */"; | ||
| public static final int MAX_DATA_TO_SEND_FOR_TXN = 100; | ||
| private static Set<String> blockSqlAstNames = Sets.newHashSet(); | ||
| @@ -588,7 +590,7 @@ public void execute() throws Exception { | ||
| TUniqueId queryId = UniqueIdUtils.fastUniqueId(); | ||
| if (Config.enable_print_request_before_execution) { | ||
| LOG.info("begin to execute query {} {}", | ||
| DebugUtil.printId(queryId), originStmt == null ? "null" : originStmt.originStmt); | ||
| DebugUtil.printId(queryId), getStmtForLoggingBeforeParse()); | ||
| } | ||
| queryRetry(queryId); | ||
| } | ||
| @@ -756,7 +758,7 @@ public void checkBlockRulesByScan(Planner planner) throws AnalysisException { | ||
| private void executeByNereids(TUniqueId queryId) throws Exception { | ||
| if (LOG.isDebugEnabled()) { | ||
| LOG.debug("Nereids start to execute query:\n {}", originStmt.originStmt); | ||
| LOG.debug("Nereids start to execute query:\n {}", getStmtForLoggingBeforeParse()); | ||
| } | ||
| context.setQueryId(queryId); | ||
| context.setStartTime(); | ||
| @@ -837,29 +839,32 @@ private void executeByNereids(TUniqueId queryId) throws Exception { | ||
| ((Command) logicalPlan).run(context, this); | ||
| } catch (QueryStateException e) { | ||
| if (LOG.isDebugEnabled()) { | ||
| LOG.debug("Command({}) process failed.", originStmt.originStmt, e); | ||
| LOG.debug("Command({}) process failed.", getStmtForLogging(originStmt.originStmt), e); | ||
| } | ||
| context.setState(e.getQueryState()); | ||
| throw new NereidsException("Command(" + originStmt.originStmt + ") process failed", | ||
| throw new NereidsException("Command(" + getStmtForLogging(originStmt.originStmt) | ||
| + ") process failed", | ||
| new AnalysisException(e.getMessage(), e)); | ||
| } catch (UserException e) { | ||
| // Return message to info client what happened. | ||
| if (LOG.isDebugEnabled()) { | ||
| LOG.debug("Command({}) process failed.", originStmt.originStmt, e); | ||
| LOG.debug("Command({}) process failed.", getStmtForLogging(originStmt.originStmt), e); | ||
| } | ||
| if (Config.isCloudMode() && SystemInfoService.needRetryWithReplan(e.getDetailMessage())) { | ||
| // For errors in SystemInfoService.NEED_REPLAN_ERRORS, | ||
| // throw exception directly to trigger a replan retry outside(in StmtExecutor.queryRetry()) | ||
| throw e; | ||
| } | ||
| context.getState().setError(e.getMysqlErrorCode(), e.getMessage()); | ||
| throw new NereidsException("Command (" + originStmt.originStmt + ") process failed", | ||
| throw new NereidsException("Command (" + getStmtForLogging(originStmt.originStmt) | ||
| + ") process failed", | ||
| new AnalysisException(e.getMessage(), e)); | ||
| } catch (Exception | Error e) { | ||
| // Maybe our bug | ||
| LOG.info("Command({}) process failed.", originStmt.originStmt, e); | ||
| LOG.info("Command({}) process failed.", getStmtForLogging(originStmt.originStmt), e); | ||
| context.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, e.getMessage()); | ||
| throw new NereidsException("Command (" + originStmt.originStmt + ") process failed.", | ||
| throw new NereidsException("Command (" + getStmtForLogging(originStmt.originStmt) | ||
| + ") process failed.", | ||
| new AnalysisException(e.getMessage() == null ? e.toString() : e.getMessage(), e)); | ||
| } | ||
| } else { | ||
| @@ -899,7 +904,7 @@ private void executeByNereids(TUniqueId queryId) throws Exception { | ||
| planner.plan(parsedStmt, context.getSessionVariable().toThrift()); | ||
| checkBlockRulesByScan(planner); | ||
| } catch (Exception e) { | ||
| LOG.warn("Nereids plan query failed:\n{}", originStmt.originStmt, e); | ||
| LOG.warn("Nereids plan query failed:\n{}", getStmtForLogging(originStmt.originStmt), e); | ||
| throw new NereidsException(new AnalysisException(e.getMessage(), e)); | ||
| } | ||
| profile.getSummaryProfile().setQueryPlanFinishTime(TimeUtils.getStartTimeMs()); | ||
| @@ -2390,6 +2395,55 @@ public String getOriginStmtInString() { | ||
| return ""; | ||
| } | ||
| private String getStmtForLogging(String stmt) { | ||
wenzhenghu marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (stmt == null) { | ||
| return stmt; | ||
| } | ||
| if (!(parsedStmt instanceof LogicalPlanAdapter)) { | ||
| return getStmtForLoggingBeforeParse(stmt); | ||
| } | ||
| // Internal export outfile tasks use an empty origin SQL, so audit masking must skip reparsing here. | ||
| if (stmt.isEmpty()) { | ||
| return stmt; | ||
| } | ||
| LogicalPlan logicalPlan = ((LogicalPlanAdapter) parsedStmt).getLogicalPlan(); | ||
| if (!(logicalPlan instanceof NeedAuditEncryption)) { | ||
| return stmt; | ||
| } | ||
| try { | ||
| return ((NeedAuditEncryption) logicalPlan).geneEncryptionSQL(stmt); | ||
| } catch (Exception e) { | ||
| // Logging must not leak plaintext or change command behavior when masking fails. | ||
| LOG.warn("failed to mask statement for FE logging", e); | ||
| return MASKED_STMT_FALLBACK; | ||
| } | ||
| } | ||
| private String getStmtForLoggingBeforeParse() { | ||
| return getStmtForLoggingBeforeParse(originStmt == null ? null : originStmt.originStmt); | ||
| } | ||
| private String getStmtForLoggingBeforeParse(String stmt) { | ||
| if (stmt == null) { | ||
| return null; | ||
| } | ||
| // Empty SQL cannot produce a valid parse tree for audit masking, so keep the original text. | ||
| if (stmt.isEmpty()) { | ||
| return stmt; | ||
| } | ||
| try { | ||
| LogicalPlan logicalPlan = new NereidsParser().parseSingle(stmt); | ||
| if (!(logicalPlan instanceof NeedAuditEncryption)) { | ||
| return stmt; | ||
| } | ||
| return ((NeedAuditEncryption) logicalPlan).geneEncryptionSQL(stmt); | ||
| } catch (Exception e) { | ||
| // Logging must fail closed before parsing so secrets never fall back to plaintext. | ||
| LOG.warn("failed to prepare masked statement for FE logging", e); | ||
| return MASKED_STMT_FALLBACK; | ||
| } | ||
| } | ||
| public List<ByteBuffer> getProxyQueryResultBufList() { | ||
| return ((ProxyMysqlChannel) context.getMysqlChannel()).getProxyResultBufferList(); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.