fix(configuration): resolve High code-review findings (Configuration-001, Configuration-008)
Configuration-001: wrap the EXEC dbo.sp_ValidateDraft call in sp_PublishGeneration in a BEGIN TRY/CATCH ROLLBACK; THROW block so a validation RAISERROR aborts the publish instead of being ignored. Configuration-008: route caller-supplied strings interpolated into ConfigAuditLog.DetailsJson through STRING_ESCAPE(@x, 'json') and emit sp_RollbackToGeneration's @TargetGenerationId as a bare JSON number, closing the JSON-injection / denial-of-operation vector. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -145,9 +145,12 @@ BEGIN
|
||||
(NodeId, CurrentGenerationId, LastAppliedAt, LastAppliedStatus, LastAppliedError, LastSeenAt)
|
||||
VALUES (@NodeId, @GenerationId, SYSUTCDATETIME(), @Status, @Error, SYSUTCDATETIME());
|
||||
|
||||
-- Build DetailsJson via STRING_ESCAPE so a @Status containing a double-quote/backslash cannot
|
||||
-- produce malformed JSON (which would fail CK_ConfigAuditLog_DetailsJson_IsJson and abort the
|
||||
-- transaction) or inject extra JSON structure into the audit record.
|
||||
INSERT dbo.ConfigAuditLog (Principal, EventType, NodeId, GenerationId, DetailsJson)
|
||||
VALUES (@Caller, 'NodeApplied', @NodeId, @GenerationId,
|
||||
CONCAT('{""status"":""', @Status, '""}'));
|
||||
CONCAT('{""status"":""', STRING_ESCAPE(@Status, 'json'), '""}'));
|
||||
END
|
||||
";
|
||||
|
||||
@@ -279,7 +282,18 @@ BEGIN
|
||||
RETURN;
|
||||
END
|
||||
|
||||
EXEC dbo.sp_ValidateDraft @DraftGenerationId = @DraftGenerationId;
|
||||
-- sp_ValidateDraft signals every rejection with RAISERROR(..., 16, 1) — a severity-16 error is
|
||||
-- NOT batch-aborting and SET XACT_ABORT ON does not abort the transaction for it, so without a
|
||||
-- TRY/CATCH control would return here and the draft would publish despite failed validation.
|
||||
-- Catch the validation error, roll back the publish transaction, and re-raise so the caller sees
|
||||
-- the real validation failure.
|
||||
BEGIN TRY
|
||||
EXEC dbo.sp_ValidateDraft @DraftGenerationId = @DraftGenerationId;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0 ROLLBACK;
|
||||
THROW;
|
||||
END CATCH
|
||||
|
||||
MERGE dbo.ExternalIdReservation AS tgt
|
||||
USING (
|
||||
@@ -369,9 +383,11 @@ BEGIN
|
||||
|
||||
EXEC dbo.sp_PublishGeneration @ClusterId = @ClusterId, @DraftGenerationId = @NewGenId, @Notes = @Notes;
|
||||
|
||||
-- @TargetGenerationId is a bigint, but build the JSON value via an explicit numeric CONVERT so
|
||||
-- the emitted token is always a bare JSON number — never reliant on implicit string coercion.
|
||||
INSERT dbo.ConfigAuditLog (Principal, EventType, ClusterId, GenerationId, DetailsJson)
|
||||
VALUES (SUSER_SNAME(), 'RolledBack', @ClusterId, @NewGenId,
|
||||
CONCAT('{""rolledBackTo"":', @TargetGenerationId, '}'));
|
||||
CONCAT('{""rolledBackTo"":', CONVERT(nvarchar(20), CONVERT(bigint, @TargetGenerationId)), '}'));
|
||||
|
||||
COMMIT;
|
||||
END
|
||||
@@ -464,9 +480,12 @@ BEGIN
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- Escape both caller-supplied values via STRING_ESCAPE so quotes/backslashes cannot break the
|
||||
-- JSON document or inject additional structure into the audit record.
|
||||
INSERT dbo.ConfigAuditLog (Principal, EventType, DetailsJson)
|
||||
VALUES (SUSER_SNAME(), 'ExternalIdReleased',
|
||||
CONCAT('{""kind"":""', @Kind, '"",""value"":""', @Value, '""}'));
|
||||
CONCAT('{""kind"":""', STRING_ESCAPE(@Kind, 'json'),
|
||||
'"",""value"":""', STRING_ESCAPE(@Value, 'json'), '""}'));
|
||||
END
|
||||
";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user