From 9978a87ab7f5585c4247214e89aebc0d42e935a6 Mon Sep 17 00:00:00 2001 From: danrega <16720986+danrega@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:39:37 +0200 Subject: [PATCH] Update --- 01_Internal_Tables.md | 2 +- 03_ABAP_SQL.md | 24 +- 06_Dynamic_Programming.md | 459 +++++++++++++++++++++++++++++++++----- 08_EML_ABAP_for_RAP.md | 3 +- 22_Misc_ABAP_Classes.md | 33 ++- README.md | 8 +- 6 files changed, 450 insertions(+), 79 deletions(-) diff --git a/01_Internal_Tables.md b/01_Internal_Tables.md index 6f4c354..ed9baeb 100644 --- a/01_Internal_Tables.md +++ b/01_Internal_Tables.md @@ -2689,7 +2689,7 @@ ENDCLASS. #### Excursion: Restrictions Regarding Selecting from Internal Tables -- This excursion is intended to underscore the restricitions mentioned above and in the [documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_engine_restr.htm) in more detail when selecting from internal tables. +- This excursion is intended to underscore the restrictions mentioned above and in the [documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_engine_restr.htm) in more detail when selecting from internal tables. - Components having deep types, such as strings, cannot be included, for example, in the `SELECT` list or `WHERE` clause. - Note that only those fields are checked (and sent to the database) that are actually used (as shown in the example below). - However, the type string is allowed if it is declared using the built-in dictionary type `sstring` (for example, a component typed with a [data element](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_element_glosry.htm) or a [CDS simple type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_simple_type_glosry.htm) that uses `sstring`). diff --git a/03_ABAP_SQL.md b/03_ABAP_SQL.md index 03bcbe1..9b7b3d5 100644 --- a/03_ABAP_SQL.md +++ b/03_ABAP_SQL.md @@ -36,7 +36,7 @@ - [Using UPDATE](#using-update) - [Using MODIFY](#using-modify) - [Using DELETE](#using-delete) - - [Using of Constructor Expressions in ABAP SQL Statements](#using-of-constructor-expressions-in-abap-sql-statements) + - [Using Constructor Expressions in ABAP SQL Statements](#using-constructor-expressions-in-abap-sql-statements) - [Example: Exploring ABAP SQL Statements Changing Data in Database Tables](#example-exploring-abap-sql-statements-changing-data-in-database-tables) - [RAP-Specific ABAP SQL Variants](#rap-specific-abap-sql-variants) - [More Information](#more-information) @@ -1997,12 +1997,6 @@ UPDATE dbtab FROM TABLE @itab. UPDATE dbtab FROM TABLE @( VALUE #( ( comp1 = ... comp2 = ... ) ( comp1 = ... comp2 = ... ) ) ). -"Excursion: Constructor expression with VALUE and BASE -"The example assumes selecting an entry from a database, modifying it, and -"updating it again, but the non-modified entries shall remain unchanged. -SELECT SINGLE * FROM dbtab WHERE key = ... INTO @DATA(read_line). -UPDATE dbtab FROM @( VALUE #( BASE read_line comp2 = ... comp4 = ... ) ). - "-------------------------- SET addition -------------------------- "Changing values of specific fields without overwriting other, non-specified "fields @@ -2021,7 +2015,7 @@ UPDATE dbtab SET comp2 = 'X', comp3 = 'Y' WHERE comp4 > 100. "Changing values of specific fields in a single database table entry "Assume the entry can be uniquely identified by specifying key values "in the WHERE clause -UPDATE dbtab SET comp2 = 'X' WHERE key1 = 'Y'. +UPDATE dbtab SET comp2 = 'X' WHERE key_field = 'Y'. "--------------- INDICATORS ... SET STRUCTURE addition --------------- "Similar to SET, using the INDICATORS ... addition, you can change content @@ -2106,7 +2100,7 @@ DELETE dbtab FROM TABLE @( VALUE #( ( comp1 = ... )

⬆️ back to top

-### Using of Constructor Expressions in ABAP SQL Statements +### Using Constructor Expressions in ABAP SQL Statements [Constructor expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_glosry.htm) can be very handy in ABAP SQL statements. For more information about constructor expressions, see the ABAP Keyword Documentation and the [Constructor Expressions cheat sheet](05_Constructor_Expressions.md). Many additions are available. @@ -2120,7 +2114,7 @@ INSERT dbtab FROM TABLE @( VALUE #( ( key_field = 1 comp1 = ... ) ( key_field = 3 comp1 = ... ) ) ). "Inserting a table row from a row created in place -"INSERT dbtab FROM @( VALUE #( key_field = 4 comp1 = ... ) ). +INSERT dbtab FROM @( VALUE #( key_field = 4 comp1 = ... ) ). "FOR LOOP with VALUE @@ -2129,12 +2123,12 @@ DATA(it) = VALUE some_it_type( ( key_field = 5 comp1 = ... ) ( key_field = 7 comp1 = ... ) ). "In the following example, the internal table from above is looped across. You -"can imagine modifying the components when mapping the fields (as inidicated by the -"concatentation in the example). In doing so, the internal table values are modified -"(or not) and inserted into the database table. +"can imagine modifying the components (or not) when mapping the fields (as indicated +"by the concatentation in the example). In doing so, the internal table values may or +"may be not modified and inserted into the database table. INSERT dbtab FROM TABLE @( VALUE #( FOR wa IN it ( key_field = wa-key_field - comp1 = wa-comp1 && 'XYZ' - ... ) ) ). + comp1 = wa-comp1 && 'XYZ' + ... ) ) ). "Using a constructor expression with VALUE and BASE in an UPDATE statement "The example assumes selecting an entry from a database, modifying it, and updating it again, diff --git a/06_Dynamic_Programming.md b/06_Dynamic_Programming.md index 2b52ae8..4b62523 100644 --- a/06_Dynamic_Programming.md +++ b/06_Dynamic_Programming.md @@ -29,7 +29,7 @@ - [Dynamic Invoke](#dynamic-invoke) - [Dynamic ABAP EML Statements](#dynamic-abap-eml-statements) - [Dynamic Formatting Option Specifications in String Templates](#dynamic-formatting-option-specifications-in-string-templates) - - [Validating Input for Dynamic Specifications (CL\_ABAP\_DYN\_PRG)](#validating-input-for-dynamic-specifications-cl_abap_dyn_prg) + - [Security Considerations in Dynamic Programming Using External Input](#security-considerations-in-dynamic-programming-using-external-input) - [Runtime Type Services (RTTS)](#runtime-type-services-rtts) - [Getting Type Information at Runtime](#getting-type-information-at-runtime) - [RTTI: Attribute Access and Method Calls](#rtti-attribute-access-and-method-calls) @@ -67,8 +67,11 @@ - Further aspects for dynamic programming in ABAP enter the picture if you want to determine information about data types and data objects at runtime ([RTTI](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrun_time_type_identific_glosry.htm)) or even create them ([RTTC](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrun_time_type_creation_glosry.htm)). -- In general, dynamic programming also comes with some downsides. For example, the ABAP compiler cannot check the dynamic programming feature like the `SELECT` statement mentioned above. There is no syntax warning or suchlike (note the `CL_ABAP_DYN_PRG` class that supports dynamic programming). The checks are performed only at runtime, which has an impact on the performance. Plus, the testing of [procedures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm "Glossary Entry") -that include dynamic programming features may be difficult. +- In general, dynamic programming also comes with some downsides. For example: + - The ABAP compiler cannot check the dynamic programming feature like the `SELECT` statement mentioned above. There is no syntax warning or suchlike. + - The checks are performed only at runtime, which has an impact on the performance. + - The testing of [procedures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm "Glossary Entry") that include dynamic programming features may be difficult. + - Including external input in dynamic ABAP SQL statements without an appropriate handling, there can be potential security risks. You can, for example, use the `CL_ABAP_DYN_PRG` class to manage security risks.

⬆️ back to top

@@ -882,7 +885,8 @@ ref4 = CAST #( ref3 ). Before addressing the content of data objects a data reference points to, you must dereference data reference variables. Use the [dereferencing operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendereferencing_operat_glosry.htm "Glossary Entry") -`->*`. To check if dereferencing works, you can use a logical expression with [`IS BOUND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_bound.htm). +`->*`. To check if dereferencing works, you can use a logical expression with [`IS BOUND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_bound.htm). When dereferencing a data reference variable that has a structured data type, you can use the component selector `->` to address individual components. + ``` abap "Creating data reference variables and assign values @@ -1384,6 +1388,7 @@ CREATE DATA dataref TYPE HANDLE tdo_elem. - [`CREATE OBJECT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcreate_object_explicit.htm) statements can be used to create instances of classes by specifying the type dynamically. - It assigns the reference to the object to an object reference variable. +- The `NEW` operator cannot be used to create instances of classes by specifying the type dynamically.. ```abap @@ -2283,60 +2288,412 @@ DATA(s6) = |{ some_string CASE = int_tab[ 1 ] }|. "AbAp

⬆️ back to top

-### Validating Input for Dynamic Specifications (CL_ABAP_DYN_PRG) +## Security Considerations in Dynamic Programming Using External Input -You can use the `CL_ABAP_DYN_PRG` class to validate input for dynamic specifications. -There are several methods for different use cases. See the class documentation (click F2 on the class name in ADT) for more information. -The following examples show some of those methods. If the validation is successful, the methods in the examples return the input value. -Otherwise, an exception is raised. +Dynamic programming techniques can present a security risk, particularly when they incorporate external input. Consider a scenario where a user interface allows users to input values. Dynamic ABAP statements that use this input, such as in ABAP SQL statement clauses, can be vulnerable to SQL injections if the input is not properly checked for malicious content. + +It is crucial to perform checks and handle dynamic programming techniques cautiously when including external content. You can use the `CL_ABAP_DYN_PRG` class. If escaping is necessary, you can also and additionally use the built-in function `escape` (which is recommended). The following example illustrates a selection and highlights various aspects. + +For more details, refer to the ABAP Keyword Documentation [here (Standard ABAP documentation)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abendynamic_programming_scrty.htm). + +To try the example out, create a demo class named `zcl_some_class` and paste the code into it. After activation, choose *F9* in ADT to execute the class. The example uses objects of the ABAP cheat sheets repository and is set up to display output in the console. +It covers the following aspects: +- Dynamic `WHERE` clause and specifying the data object holding external input as operand and literal +- Verifying input for not allowed database table access +- Verifying input against a given allowlist +- Potential manipulation of ABAP SQL clauses +- Escaping + ```abap -"The following method checks database table names. The name is provided -"with the val parameter. The packages formal parameter expects a table -"containing the names of packages in which the specified table should be -"included. Assuming you provide incorrect input for the table name, or -"the table is not contained in the specified packages, you can expect an -"exception to be raied. +CLASS zcl_some_class DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . -TRY. - DATA(dbtab) = cl_abap_dyn_prg=>check_table_name_tab( - val = `ZDEMO_ABAP_FLI` - packages = VALUE #( ( `TEST_ABAP_CHEAT_SHEETS` ) - ( `TEST_SOME_PACK` ) ) ). + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. - SELECT SINGLE * FROM (dbtab) INTO NEW @DATA(ref_wa). - CATCH cx_abap_not_a_table cx_abap_not_in_package. - ... -ENDTRY. -"In the following examples, a method is used to check whether -"the input is allowed or not. For this, you specify an allowlist. -"Here, the relvant parameter expects a comma-separated list of -"allowed values. -TRY. - DATA(value1) = cl_abap_dyn_prg=>check_allowlist( - val = `A` - allowlist_str = `A,B,C,D` ). - - ... "Here might go an ABAP SQL statement with a dynamic specification. - CATCH cx_abap_not_in_allowlist. - ... -ENDTRY. -"Another parameter of the method expects an internal table that -"contains the allowed values. -TRY. - DATA(value2) = cl_abap_dyn_prg=>check_allowlist( - val = `XYZ` - allowlist_htab = VALUE #( ( `A` ) - ( `B` ) - ( `C` ) - ( `D` ) ) ). - - ... "Here might go an ABAP SQL statement with a dynamic specification. - CATCH cx_abap_not_in_allowlist. - ... -ENDTRY. +CLASS zcl_some_class IMPLEMENTATION. + + METHOD if_oo_adt_classrun~main. + + "Filling demo database tables of the ABAP cheat sheet repository + zcl_demo_abap_aux=>fill_dbtabs( ). + + "-------------------------------------------------------------------- + "--- Specifying the data object holding external input as operand --- + "--- and literal ---------------------------------------------------- + "-------------------------------------------------------------------- + + "The example explores a dynamic WHERE clause. External content is used + "in the WHERE clause, unchecked. + + "Assuming the data object 'input' holds external input inserted on a UI. + DATA(input) = 'LH'. + + "Inserting the input value into a dynamic WHERE clause as literal + DATA(cond1) = `CARRID = '` && input && `'`. + SELECT SINGLE * FROM zdemo_abap_fli WHERE (cond1) INTO @DATA(db_entry). + + out->write( data = db_entry name = `db_entry` ). + out->write( |\n\n| ). + + "Inserting the input value into a dynamic WHERE clause using the data + "object name + DATA(cond2) = `CARRID = @input`. + SELECT SINGLE * FROM zdemo_abap_fli WHERE (cond2) INTO @db_entry. + + out->write( data = db_entry name = `db_entry` ). + out->write( |\n\n| ). + + "Assuming bad input is provided that is unchecked + DATA(bad_input) = |LH' AND CONNID = '401|. + + "Inserting the input value as literal + "Because of using the value as literal, the WHERE clause + "can be manipulated, yielding a potentially different + "result, thus posing a security risk. + DATA(cond3) = `CARRID = '` && bad_input && `'`. + SELECT SINGLE * FROM zdemo_abap_fli WHERE (cond3) INTO @db_entry. + + out->write( data = db_entry name = `db_entry` ). + out->write( |\n\n| ). + + "Inserting the input value using the data object name + "In doing so, the WHERE clause becomes erroneous, the ABAP + "SQL statement cannot be executed. + DATA(cond4) = `CARRID = @bad_input`. + TRY. + SELECT SINGLE * FROM zdemo_abap_fli WHERE (cond4) INTO @db_entry. + out->write( data = db_entry name = `db_entry` ). + CATCH cx_sy_dynamic_osql_error cx_sy_open_sql_data_error INTO DATA(select_error). + out->write( select_error->get_text( ) ). + ENDTRY. + out->write( |\n\n| ). + out->write( |{ repeat( val = `*` occ = 70 ) }| ). + + "-------------------------------------------------------------------- + "------------ Accessing not allowed database tables ----------------- + "-------------------------------------------------------------------- + "Assume the name of a database table is specified externally, and a + "dynamic ABAP SQL statement uses this name. Potentially, users that + "are actually not allowed to access the database table may get access. + "The example uses the CL_ABAP_DYN_PRG class that checks a list of + "allowed database tables. + + "The following methods check ... + "- Database table names + "- Whether the database table is contained in a/certain package/s + "Assuming you provide incorrect input for the table name, or + "the table is not contained in the specified packages, you should + "expect an exception to be raied. + + "Assuming the following data object contains external input + DATA(input_dbtab_name) = `zdemo_abap_fli`. + + "check_table_name_str method: Specifying a single package + TRY. + DATA(dbtab) = cl_abap_dyn_prg=>check_table_name_str( + val = to_upper( input_dbtab_name ) + packages = `ZABAP_CHEAT_SHEETS` ). + + SELECT SINGLE * FROM (dbtab) INTO NEW @DATA(ref_db_entry). + out->write( data = ref_db_entry name = `ref_db_entry` ). + CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(error_input_dbtab1). + out->write( error_input_dbtab1->get_text( ) ). + ENDTRY. + out->write( |\n\n| ). + + "check_table_name_tab method: Specifying multiple packages in an internal + "table + TRY. + dbtab = cl_abap_dyn_prg=>check_table_name_tab( + val = to_upper( input_dbtab_name ) + packages = VALUE #( ( `ZABAP_CHEAT_SHEETS` ) + ( `ZSOME_PACKAGE` ) ) ). + + SELECT SINGLE * FROM (dbtab) INTO NEW @ref_db_entry. + out->write( data = ref_db_entry name = `ref_db_entry` ). + CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(error_input_dbtab2). + out->write( error_input_dbtab2->get_text( ) ). + ENDTRY. + out->write( |\n\n| ). + + "Not existant database table/invalid name + input_dbtab_name = `not_a_dbtab!!`. + TRY. + dbtab = cl_abap_dyn_prg=>check_table_name_tab( + val = to_upper( input_dbtab_name ) + packages = VALUE #( ( `ZABAP_CHEAT_SHEETS` ) + ( `ZSOME_PACKAGE` ) ) ). + + SELECT SINGLE * FROM (dbtab) INTO NEW @ref_db_entry. + out->write( data = ref_db_entry name = `ref_db_entry` ). + CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(error_input_dbtab3). + out->write( error_input_dbtab3->get_text( ) ). + ENDTRY. + out->write( |\n\n| ). + + "Database table not existant in packages specified (assuming you have imported + "the ABAP cheat sheet repository, and the database table is available) + input_dbtab_name = `zdemo_abap_fli`. + TRY. + dbtab = cl_abap_dyn_prg=>check_table_name_tab( + val = to_upper( input_dbtab_name ) + packages = VALUE #( ( `SAP_BASIS` ) ) ). + + SELECT SINGLE * FROM (dbtab) INTO NEW @ref_db_entry. + out->write( data = ref_db_entry name = `ref_db_entry` ). + CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(error_input_dbtab4). + out->write( error_input_dbtab4->get_text( ) ). + ENDTRY. + out->write( |\n\n| ). + out->write( |{ repeat( val = `*` occ = 70 ) }| ). + + "-------------------------------------------------------------------- + "------------ Verifying input against a given allowlist ------------ + "-------------------------------------------------------------------- + + "Assume a SELECT statement dynamically specifies the column names + "in the SELECT list. Table columns might be accessed although + "they should not be. + "You may check against an allowlist. + + "check_allowlist method + "In the following examples, a method is used to check whether + "the input is allowed or not. For this, you specify an allowlist. + "Here, the relevant parameter expects a comma-separated list of + "allowed values. + + "Assuming the following data object contains external input + DATA(input_col_name) = `carrid`. + + TRY. + DATA(value1) = cl_abap_dyn_prg=>check_allowlist( + val = to_upper( input_col_name ) + allowlist_str = `CARRID,CONNID,FLDATE` ). + + SELECT SINGLE (input_col_name) FROM zdemo_abap_fli INTO NEW @ref_db_entry. + out->write( data = ref_db_entry name = `ref_db_entry` ). + CATCH cx_abap_not_in_allowlist INTO DATA(error_allowed1). + out->write( error_allowed1->get_text( ) ). + ENDTRY. + out->write( |\n\n| ). + + "The allowlist_htab formal parameter expects an internal table. + input_col_name = `price`. + TRY. + DATA(value2) = cl_abap_dyn_prg=>check_allowlist( + val = to_upper( input_col_name ) + allowlist_htab = VALUE #( ( `CARRID` ) + ( `CONNID` ) + ( `FLDATE` ) ) ). + + SELECT SINGLE (input_col_name) FROM zdemo_abap_fli INTO NEW @ref_db_entry. + out->write( data = ref_db_entry name = `ref_db_entry` ). + CATCH cx_abap_not_in_allowlist INTO DATA(error_allowed2). + out->write( error_allowed2->get_text( ) ). + ENDTRY. + out->write( |\n\n| ). + out->write( |{ repeat( val = `*` occ = 70 ) }| ). + + "-------------------------------------------------------------------- + "------------ Potential manipulation of ABAP SQL clauses ------------ + "-------------------------------------------------------------------- + + "In the following example, a dynamic WHERE clause is set up. For this, + "it is assumed that the WHERE clause uses external input via input fields. + "This is represented by the column and value data objects. It is assumed + "that column holds the name of the table column, value a dedicated value in + "the specified table column. + "The cl_abap_dyn_prg class is used to check content in two ways: + "- Checking if the provided column name is valid using the check_column_name + " method. + "- Using the quote method for putting single quotes around the value and escaping + " single quotes. + "In a DO loop, various example inputs are explored. The fourth loop pass includes + "bad input without using the quote method. This way, an SQL injection takes + "place, yielding a different result. In this case, all database table entries + "are retrieved because the WHERE clause is as follows: + "CARRID = 'LH' OR CARRID <> 'LH'. + "This is prevented using the quote method, resulting in a non-functional SELECT + "statement. + + DATA: column TYPE c LENGTH 30, + value TYPE c LENGTH 30. + + DO 4 TIMES. + CASE sy-index. + WHEN 1. + "Working example + column = 'carrid'. + value = 'lh'. + WHEN 2. + "Invalid column name + column = '?=('. + value = 'lh'. + WHEN 3. + "Bad input, using cl_abap_dyn_prg + column = 'carrid'. + value = |'LH' OR CARRID <> 'LH'|. + + WHEN 4. + "Bad input, not using cl_abap_dyn_prg + column = 'carrid'. + value = |'LH' OR CARRID <> 'LH'|. + + ENDCASE. + + out->write( |---------- Run { sy-index } ----------| ). + + TRY. + cl_abap_dyn_prg=>check_column_name( column ). + CATCH cx_abap_invalid_name INTO DATA(error_col_name). + out->write( error_col_name->get_text( ) ). + ENDTRY. + + DATA(cond_syntax) = to_upper( column ) && ` = ` && + COND #( WHEN sy-index <> 4 THEN cl_abap_dyn_prg=>quote( to_upper( value ) ) ELSE to_upper( value ) ). + + TRY. + SELECT * + FROM zdemo_abap_flsch + WHERE (cond_syntax) + INTO TABLE @DATA(itab_flsch). + + out->write( itab_flsch ). + CATCH cx_sy_dynamic_osql_error cx_sy_open_sql_data_error INTO DATA(error_select). + out->write( error_select->get_text( ) ). + ENDTRY. + + out->write( |\n\n| ). + ENDDO. + + "Example manipulating the SET clause in an UPDATE statement + "Inserting a database table entry to work with in the example + INSERT zdemo_abap_carr FROM @( VALUE #( carrid = 'XY' carrname = 'XY Airways' currcode = 'EUR' url = 'some_url' ) ). + SELECT SINGLE * FROM zdemo_abap_carr WHERE carrid = 'XY' INTO @DATA(row4update). + + out->write( data = row4update name = `row4update` ). + out->write( |\n\n| ). + + "Assuming the carrier name is to be changed (that was previously created and retrieved + "for demo purposes). The carrier name is provided via external input, represented by + "the following data object assignment. + DATA(input_carrname) = 'Air XY'. + + "Specifying a potentially dangerous dynamic SET clause by directly using external + "input in the clause + DATA(dyn_set_clause) = `CARRNAME = '` && input_carrname && `'`. + + UPDATE zdemo_abap_carr + SET (dyn_set_clause) + WHERE carrid = @row4update-carrid. + + SELECT SINGLE * FROM zdemo_abap_carr WHERE carrid = 'XY' INTO @row4update. + out->write( data = row4update name = `row4update` ). + out->write( |\n\n| ). + + "Bad input, not using cl_abap_dyn_prg + "In the example, the input is manipulated in a way that also changes + "another field value. + DATA(bad_input_carrname) = |XY Airways', URL = '#########|. + dyn_set_clause = `CARRNAME = '` && bad_input_carrname && `'`. + + UPDATE zdemo_abap_carr + SET (dyn_set_clause) + WHERE carrid = @row4update-carrid. + + SELECT SINGLE * FROM zdemo_abap_carr WHERE carrid = 'XY' INTO @row4update. + out->write( data = row4update name = `row4update` ). + out->write( |\n\n| ). + + "Bad input, using cl_abap_dyn_prg + "Undoing the changes for the demo database table row + MODIFY zdemo_abap_carr FROM @( VALUE #( carrid = 'XY' carrname = 'XY Airways' currcode = 'EUR' url = 'some_url' ) ). + SELECT SINGLE * FROM zdemo_abap_carr WHERE carrid = 'XY' INTO @row4update. + + bad_input_carrname = |XY Airways', URL = '#########|. + dyn_set_clause = `CARRNAME = ` && cl_abap_dyn_prg=>quote( bad_input_carrname ). + + TRY. + UPDATE zdemo_abap_carr + SET (dyn_set_clause) + WHERE carrid = @row4update-carrid. + CATCH cx_sy_open_sql_data_error INTO DATA(error_set). + out->write( error_set->get_text( ) ). + ENDTRY. + + out->write( |{ repeat( val = `*` occ = 70 ) }| ). + + "-------------------------------------------------------------------- + "---------------------------- Escaping ------------------------------ + "-------------------------------------------------------------------- + + "In various contexts, a replacement of special characters may be important. + "Such an escaping is applied on characters contained in a string according + "to a set of rules. + + "The following example deals with Cross Site Scripting, e.g. manipulating + "HTML pages and embedding scripts displayed in a browser. In ABAP, this + "enters the picture, for example, when directly dealing with the Internet + "Communication Framework. + "The built-in function escape can be used to escape content in various contexts. + "The cl_abap_dyn_prg class also offers methods to escape. However, the function + "is recommended due to performance reasons. + + "Assuming building HTML code by using external input + DATA your_name TYPE string. + your_name = sy-uname. + DATA(html) = `

Hello ` && your_name && `!

`. + + out->write( data = html name = `html` ). + out->write( |\n\n| ). + + "Embedding potentially malicious scripts into the code + your_name = ``. + html = `

Hello ` && your_name && `!

`. + + "Inserted this in an HTML and run in a browser, an alert will be displayed. + out->write( data = html name = `html` ). + out->write( |\n\n| ). + + "Escaping may be done as follows + "Check the various methods available for escaping with cl_abap_dyn_prg, as well as + "the formats in the context of the escape function + DATA(esc_js_cl) = `

Hello ` && cl_abap_dyn_prg=>escape_xss_javascript( html ) && `!

`. + + "Using the built-in function escape + DATA(esc_js_fu) = `

Hello ` && escape( val = html format = cl_abap_format=>e_xss_js ) && `!

`. + + "Further character handling and escaping examples using the cl_abap_dyn_prg class + Data(quote) = |10 o'clock|. + DATA(handle_quotes) = cl_abap_dyn_prg=>quote( quote ). + Data(backtick) = |The character ` is a backtick|. + DATA(handle_backtick) = cl_abap_dyn_prg=>quote_str( backtick ). + DATA(esc_quotes) = cl_abap_dyn_prg=>escape_quotes( quote ). + DATA(esc_backticks) = cl_abap_dyn_prg=>escape_quotes_str( backtick ). + "You may also do the escaping using string processing techniques, e.g. + "using the replace function. + DATA(esc_quotes_replace) = replace( val = quote sub = |'| with = |''| occ = 0 ). + DATA(esc_backticks_replace) = replace( val = backtick sub = |`| with = |``| occ = 0 ). + + out->write( data = esc_js_cl name = `esc_js_cl` ). + out->write( data = esc_js_fu name = `esc_js_fu` ). + out->write( data = handle_quotes name = `handle_quotes` ). + out->write( data = handle_backtick name = `handle_backtick` ). + out->write( data = esc_quotes name = `esc_quotes` ). + out->write( data = esc_backticks name = `esc_backticks` ). + out->write( data = esc_quotes_replace name = `esc_quotes_replace` ). + out->write( data = esc_backticks_replace name = `esc_backticks` ). + ENDMETHOD. +ENDCLASS. ```

⬆️ back to top

diff --git a/08_EML_ABAP_for_RAP.md b/08_EML_ABAP_for_RAP.md index ad59ae0..54884c7 100644 --- a/08_EML_ABAP_for_RAP.md +++ b/08_EML_ABAP_for_RAP.md @@ -1715,7 +1715,8 @@ contains all relevant components for the chosen scenario. > **💡 Note**
> Assignment of Key Component Groups > -> As a general best practice, you should use a RAP BO instance key component group when referring to the entire key, rather than listing the individual key fields. It is recommended that you use `%tky` whenever possible. +> As a general best practice, you should use a RAP BO instance key component group when referring to the entire key, rather than listing the individual key fields. It is recommended that you use `%tky` whenever possible. +> - Avoid specifying multiple keys. For example, check the F2 information to understand what is included in the component groups to avoid redundancy. You should avoid specifying a component group and a key separately if the group already includes that key. > In the following cases, type compatibility cannot be guaranteed in component group assignments: > - Mixing key component groups when they refer to the same RAP BO entity, e.g. `wa-%tky = wa-%key`. Such an assignment should also be avoided when both component groups have an identical scope in terms of components (e.g. `%tky` and `%key` in non-late-numbering and non-draft scenarios). > - Mixing the same key component groups when referring to two different RAP BO entities, for example, `wa_root-%tky = wa_child-%tky`. In this case, adding more components later may cause syntax errors for an assignment that worked previously. diff --git a/22_Misc_ABAP_Classes.md b/22_Misc_ABAP_Classes.md index 5d18a05..08c3dd9 100644 --- a/22_Misc_ABAP_Classes.md +++ b/22_Misc_ABAP_Classes.md @@ -4,7 +4,7 @@ - [Misc ABAP Classes](#misc-abap-classes) - [Excursion: Available Classes in ABAP for Cloud Development](#excursion-available-classes-in-abap-for-cloud-development) - - [Creating UUIDs](#creating-uuids) + - [Creating and Transforming UUIDs](#creating-and-transforming-uuids) - [Displaying Output in the ADT Console](#displaying-output-in-the-adt-console) - [RAP](#rap) - [Transactional Consistency](#transactional-consistency) @@ -58,7 +58,7 @@ SELECT ReleasedObjectType, ReleasedObjectName, ReleaseState INTO TABLE @DATA(released_classes). ``` -## Creating UUIDs +## Creating and Transforming UUIDs @@ -71,7 +71,7 @@ Creating and and converting system UUIDs with various algorithms

``` abap -"Generating UUIDs in binary format (16 bytes) +"Creating UUIDs in binary format (16 bytes) TRY. DATA(uuid) = cl_system_uuid=>create_uuid_x16_static( ) . CATCH cx_uuid_error. @@ -80,6 +80,24 @@ ENDTRY. "e.g. B2B012691AC31EDEADA0A495A7130961 ``` + + + + +
XCO_CP_UUID +Transforming between different UUID formats +

+ +``` abap +DATA(uuid_c36) = xco_cp_uuid=>format->c36->to_uuid( '7cd44fff-036a-4155-b0d2-f5a4dfbcee92' ). + +"7CD44FFF036A4155B0D2F5A4DFBCEE92 +DATA(uuid_c36_to_c32) = CONV sysuuid_c32( xco_cp_uuid=>format->c32->from_uuid( uuid_c36 ) ). + +"7cd44fff-036a-4155-b0d2-f5a4dfbcee92 +DATA(uuid_c32_to_c36) = to_lower( CONV sysuuid_c36( xco_cp_uuid=>format->c36->from_uuid( uuid_c36 ) ) ). +``` +
@@ -601,6 +619,7 @@ DATA(chars) = 'ABAP '. cl_abap_string_utilities=>c2str_preserving_blanks( EXPORTING source = chars IMPORTING dest = DATA(str_w_blanks) ). "`ABAP ` + DATA(str_no_blanks) = CONV string( chars ). "`ABAP` ``` @@ -2089,7 +2108,7 @@ ENDCLASS. CL_BALI_LOG -For creating and reading application logs. Refer to this documentation for more information and code snippets. +For creating and reading application logs. Refer to this documentation for more information and code snippets. Note that there is also an XCO module available dealing with business application log (XCO_CP_BAL; see this documentation).

``` abap @@ -2631,7 +2650,7 @@ ENDCLASS. The XCO library offers classes (`XCO_CP_XLSX` and others) and methods for reading and writing XLSX content. You can find more information [here](https://help.sap.com/docs/btp/sap-business-technology-platform/xlsx). The following example demonstrates a selection of methods and includes the following steps: -- Importing an existing XLSX content into your SAP BTP ABAP Environment system (not related to the XCO library; just to have content to work with in the self-contained example below) +- Importing existing XLSX content into your SAP BTP ABAP Environment system (not related to the XCO library; just to have content to work with in the self-contained example below) - This is a simplified, nonsemantic, and explorative RAP example (not delving into RAP as such; just using various ABAP repository objects related to RAP) solely for importing XLSX content to work with in the example. - ⚠️ Note the repository's readme file for disclaimer and the [documentation](https://help.sap.com/docs/btp/sap-business-technology-platform/xlsx-read-access) for security considerations when importing and processing external content. - The import is done using an automatically created SAP Fiori Elements app preview, which provides a simple UI for uploading local XLSX content. @@ -3103,7 +3122,7 @@ CLASS zcl_some_class IMPLEMENTATION. "---- explore the new XLSX content (without considering RAP-related ---- "--- things and other database table fields such as the time stamps). ---- - out->write( `-------------------- Read Write to XLSX content --------------------` ). + out->write( `-------------------- Write XLSX content --------------------` ). out->write( |\n\n| ). "Creating a new XLSX document @@ -3186,7 +3205,7 @@ CLASS zcl_some_class IMPLEMENTATION. DATA(file_content) = write_xlsx->get_file_content( ). "In this example, the uploaded XLSX content is replaced by the newly created - "XLSX content by just updating the file content field. It is just for demonstration + "XLSX content by just updating the file_content field. It is just for demonstration "purposes as commented above. "If you have created the SAP Fiori Elements preview app, you can download the XLSX file. "To do so, access the app via the service binding as described. Find the entry with the diff --git a/README.md b/README.md index 027f078..6d1f76d 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ ABAP cheat sheets[^1] ... - [Here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrestricted_abap_elements.htm) is an overview of the different ABAP language elements in the different ABAP versions, i.e. what is allowed in ABAP Cloud and what is not. See also the released APIs [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreleased_apis.htm). - In order to have all ABAP cheat sheet documents in one place, the *main* branch (for examples to be imported into the SAP BTP ABAP environment) also contains the ABAP cheat sheet documents that are only relevant for classic ABAP. - The example classes contained in the branches for classic ABAP mostly use syntax that is also available in ABAP for Cloud Development. Only the `TEST_ABAP_CHEAT_SHEETS_CLASSIC` subpackage contains syntax relevant to Standard ABAP and that is not available in ABAP for Cloud Development, such as dynpro-related ABAP keywords. -- The code snippets in the ABAP cheat sheet documents and the executable examples include many comments. While it is generally not recommended to overuse comments in your code, they are used here to educate, explain, and provide context directly where it is needed. In some cases, they illustrate the results of ABAP statements. +- The code snippets in the ABAP cheat sheet documents and the executable examples include many comments. While it is generally not recommended to overuse comments in your code, they are used here to explain and provide context directly where it is needed. In many cases, they illustrate the results of ABAP statements.
@@ -96,8 +96,8 @@ ABAP cheat sheets[^1] ... ## 🎬 Getting Started with the Examples -The main focus of the ABAP Cheat Sheets is ABAP for Cloud Development. The examples in the *main* branch of the repository are designed to be imported into the SAP BTP ABAP environment. -For Standard ABAP, you can find examples in the other branches of the repository that you can import into your sandbox SAP system. Just select the appropriate version (*v757* stands for ABAP version 7.57). Check the information in the following collapsible sections for your system environment and perform the required steps. +The main focus of the ABAP cheat sheets is ABAP for Cloud Development. The examples in the *main* branch of the repository are designed to be imported into the SAP BTP ABAP environment. +For Standard ABAP, you can find examples in the other branches of the repository (note that except for specific examples, the example code there uses syntax that is also availabe in ABAP for Cloud Development) that you can import into your sandbox SAP system. Just select the appropriate version (*v757* stands for ABAP version 7.57). Check the information in the following collapsible sections for your system environment and perform the required steps.
1) General info @@ -122,7 +122,7 @@ For Standard ABAP, you can find examples in the other branches of the repository **Import Code** -Use the abapGit plug-in to install the ABAP Cheat Sheets by carrying out the following steps: +Use the abapGit plug-in to install the ABAP cheat sheets by carrying out the following steps: 1. In your ABAP cloud project, create a package, for example, *ZABAP_CHEAT_SHEETS* as the target package. It is recommended that you assign the package to a transport request that is suitable for demo content. 2. Add the package to the *Favorite Packages* in the *Project Explorer* view in ADT.