SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

-- [PRDEX_TEST_DYNAMIC_SQL]
-- =================================================================
-- Central guard for dynamic SQL safety checks
-- 
-- Returns: INTEGER; 0 = ok; 0 <> nicht ok
-- =================================================================
-- Copyright (c) 2025 by Digital Data GmbH
--
-- Digital Data GmbH • Ludwig-Rinn-Strasse 16 • D-35452 Heuchelheim
-- Tel.: 0641/202360 • E-Mail: info-flow@digitaldata.works
-- =================================================================
-- Creation Date / Author:    24.02.2026 / MK
-- Version Date / Editor:     24.02.2026 / MK
-- Version Number:            1.0.0.0
-- =================================================================
-- History:
-- 24.02.2026 / MK - First Version

CREATE OR ALTER PROCEDURE [dbo].[PRDEX_TEST_DYNAMIC_SQL](
	@pQUERY					NVARCHAR(MAX),
	@pRETURN_STATUS			INT,
	@pQUERY_NAME			NVARCHAR(100) = N'@QUERY'
)
AS
BEGIN TRY

	--================================================-- Set session options --===============================================--
	SET NOCOUNT ON;
	----------------------------------------------------------------------------------------------------------------------------

	--=========================================-- declare new vars because of parameter sniffing --===========================--
	DECLARE @QUERY					NVARCHAR(MAX)	= ISNULL(@pQUERY,N''),
			@RETURN_STATUS			INT				= ISNULL(@pRETURN_STATUS,50000),
			@QUERY_NAME				NVARCHAR(100)	= ISNULL(@pQUERY_NAME,N'@QUERY'),
			@QUERY_SCAN				NVARCHAR(MAX)	= N'',
			@QUERY_SCAN_NORM		NVARCHAR(MAX)	= N'',
			@COMMENT_START			INT				= 0,
			@COMMENT_END			INT				= 0,
			@SCAN_POS				INT				= 1,
			@TOKEN_START			INT				= 0,
			@TOKEN_END				INT				= 0,
			@QUOTE_START			INT				= 0,
			@QUOTE_END				INT				= 0,
			@TOKEN					NVARCHAR(200)	= N'',
			@HAS_UNRESOLVED_PLACEHOLDER	BIT				= 0,
			@HAS_RESTRICTED_SQL		BIT				= 0,
			@RETURN_ERROR_TEXT		NVARCHAR(MAX)	= N'';
	----------------------------------------------------------------------------------------------------------------------------

	--=========================================-- validate query content --====================================================--
	SET @QUERY_SCAN = UPPER(@QUERY);

	SET @COMMENT_START = CHARINDEX('/*',@QUERY_SCAN);
	WHILE (@COMMENT_START > 0)
	BEGIN
		SET @COMMENT_END = CHARINDEX('*/',@QUERY_SCAN,@COMMENT_START + 2);

		IF (@COMMENT_END = 0)
			BREAK;

		SET @QUERY_SCAN = STUFF(@QUERY_SCAN,@COMMENT_START,(@COMMENT_END - @COMMENT_START + 2),REPLICATE(' ',(@COMMENT_END - @COMMENT_START + 2)));
		SET @COMMENT_START = CHARINDEX('/*',@QUERY_SCAN,@COMMENT_START + 1);
	END;

	SET @COMMENT_START = CHARINDEX('--',@QUERY_SCAN);
	WHILE (@COMMENT_START > 0)
	BEGIN
		SET @COMMENT_END = CHARINDEX(CHAR(10),@QUERY_SCAN,@COMMENT_START + 2);

		IF (@COMMENT_END = 0)
			SET @COMMENT_END = LEN(@QUERY_SCAN) + 1;

		SET @QUERY_SCAN = STUFF(@QUERY_SCAN,@COMMENT_START,(@COMMENT_END - @COMMENT_START),REPLICATE(' ',(@COMMENT_END - @COMMENT_START)));
		SET @COMMENT_START = CHARINDEX('--',@QUERY_SCAN,@COMMENT_START + 1);
	END;

	SET @QUERY_SCAN_NORM = REPLACE(REPLACE(REPLACE(@QUERY_SCAN,CHAR(9),' '),CHAR(10),' '),CHAR(13),' ');

	WHILE (CHARINDEX('  ',@QUERY_SCAN_NORM) > 0)
	BEGIN
		SET @QUERY_SCAN_NORM = REPLACE(@QUERY_SCAN_NORM,'  ',' ');
	END;

	SET @QUOTE_START = CHARINDEX('''',@QUERY_SCAN);
	WHILE (@QUOTE_START > 0)
	BEGIN
		SET @QUOTE_END = CHARINDEX('''',@QUERY_SCAN,@QUOTE_START + 1);

		IF (@QUOTE_END = 0)
			BREAK;

		SET @QUERY_SCAN = STUFF(@QUERY_SCAN,@QUOTE_START,(@QUOTE_END - @QUOTE_START + 1),REPLICATE(' ',(@QUOTE_END - @QUOTE_START + 1)));
		SET @QUOTE_START = CHARINDEX('''',@QUERY_SCAN,@QUOTE_START + 1);
	END;

	WHILE (1 = 1)
	BEGIN
		SET @TOKEN_START = CHARINDEX('%',@QUERY_SCAN,@SCAN_POS);

		IF (@TOKEN_START = 0)
			BREAK;

		SET @TOKEN_END = CHARINDEX('%',@QUERY_SCAN,@TOKEN_START + 1);

		IF (@TOKEN_END = 0)
			BREAK;

		SET @TOKEN = SUBSTRING(@QUERY_SCAN,@TOKEN_START + 1,@TOKEN_END - @TOKEN_START - 1);

		IF (LEN(@TOKEN) > 0)
		AND (LEFT(@TOKEN,1) LIKE '[A-Z_]')
		AND (@TOKEN NOT LIKE '%[^A-Z0-9_]%')
		BEGIN
			SET @HAS_UNRESOLVED_PLACEHOLDER = 1;
			BREAK;
		END;

		SET @SCAN_POS = @TOKEN_END + 1;
	END;

	SET @HAS_RESTRICTED_SQL = CASE WHEN
		(CHARINDEX(';--',@QUERY_SCAN_NORM) > 0) OR
		(CHARINDEX('; --',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%XP_CMDSHELL%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%SP_OA%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%OPENROWSET%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%OPENDATASOURCE%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%BULK INSERT%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%SP_CONFIGURE%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%RECONFIGURE%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%ALTER SERVER%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%ALTER LOGIN%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%CREATE LOGIN%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%DROP LOGIN%',@QUERY_SCAN_NORM) > 0) OR
		(PATINDEX('%DROP DATABASE%',@QUERY_SCAN_NORM) > 0)
	THEN 1 ELSE 0 END;
	----------------------------------------------------------------------------------------------------------------------------

	IF (@HAS_UNRESOLVED_PLACEHOLDER = 1) OR (@HAS_RESTRICTED_SQL = 1) BEGIN
		SET @RETURN_ERROR_TEXT = CONCAT('Blocked unsafe query content in ',@QUERY_NAME,'. Detected unresolved placeholder tokens (%TOKEN%) or restricted statements.');
		THROW @RETURN_STATUS,@RETURN_ERROR_TEXT,1;
	END;

	RETURN 0;

END TRY BEGIN CATCH
	THROW;
END CATCH;
GO