Skip to content

ABAP Keyword Documentation →  ABAP - Reference →  program editing →  Dynamic Program Editing →  Source Code 

Program Generation

This example demonstrates how a program is generated using GENERATE SUBROUTINE POOL.

Other versions: 7.31 | 7.40 | 7.54

Source Code

REPORT demo_generic_program.

CLASS display DEFINITION FINAL.
  PUBLIC SECTION.
    TYPES: txt_line TYPE c LENGTH 72,
           txt      TYPE STANDARD TABLE OF txt_line
                    WITH DEFAULT KEY.
    CLASS-DATA:
      read_only TYPE abap_bool READ-ONLY VALUE abap_true.

    CLASS-METHODS:
      class_constructor,
      main,
      fill_abap_editor   IMPORTING editor TYPE REF TO cl_gui_abapedit
                                  text TYPE  txt,
      read_abap_editor   IMPORTING editor TYPE REF TO cl_gui_abapedit
                         EXPORTING text TYPE txt.

  PRIVATE SECTION.
    CLASS-METHODS:
      prepare_editors,
      create_abap_editor
        IMPORTING parent_container TYPE REF TO cl_gui_custom_container
        RETURNING VALUE(editor)    TYPE REF TO cl_gui_abapedit.

ENDCLASS.

CLASS program DEFINITION FINAL.
  PUBLIC SECTION.
    CLASS-METHODS:
      execute IMPORTING check_only TYPE abap_bool DEFAULT abap_false,
      build_source          RETURNING VALUE(rc) TYPE sy-subrc,
      check_declarations    RETURNING VALUE(rc) TYPE sy-subrc,
      check_implementation  RETURNING VALUE(rc) TYPE sy-subrc,
      check_syntax          RETURNING VALUE(rc) TYPE sy-subrc.

  PRIVATE SECTION.
    CLASS-DATA:
      source TYPE display=>txt.

ENDCLASS.

DATA:
  g_links      TYPE TABLE OF tline,
  g_ok_code    TYPE sy-ucomm.

DATA:
  g_editor1    TYPE REF TO cl_gui_abapedit,
  g_editor2    TYPE REF TO cl_gui_abapedit.

DATA:
  g_declarations      TYPE display=>txt,
  g_implementation    TYPE display=>txt.

START-OF-SELECTION.
  display=>main( ).

CLASS program IMPLEMENTATION.
  METHOD execute.
    DATA program TYPE progname.
    DATA class TYPE string.

    IF check_declarations( )   <> 0 OR
       check_implementation( ) <> 0.
      RETURN.
    ENDIF.

    IF build_source( ) <> 0.
      RETURN.
    ENDIF.

    IF check_only = abap_true.
      MESSAGE text-sok TYPE 'S'.
      RETURN.
    ENDIF.

    DATA(source_name) = 'SOURCE'.
    FIELD-SYMBOLS <source> TYPE STANDARD TABLE.
    ASSIGN (source_name) TO <source>.
    TRY.
        GENERATE SUBROUTINE POOL <source> NAME program.
      CATCH cx_sy_generate_subpool_full.
        MESSAGE text-srf TYPE 'I' DISPLAY LIKE 'E'.
        RETURN.
    ENDTRY.

    class = `\PROGRAM=` && program && `\CLASS=DEMO`.
    TRY.
        CALL METHOD (class)=>main.
      CATCH cx_root INTO DATA(exc) ##CATCH_ALL.
        MESSAGE exc->get_text( ) TYPE 'I' DISPLAY LIKE 'E'.
    ENDTRY.

  ENDMETHOD.

  METHOD build_source.
    DATA idx TYPE sy-tabix.
    DATA subrc TYPE sy-subrc.

    TRY.
        READ REPORT 'DEMO_GENERIC_TEMPLATE' INTO source.
        subrc = sy-subrc.
      CATCH cx_sy_read_src_line_too_long.
        subrc = 4.
    ENDTRY.

    IF subrc = 0.
      FIND '* declarations' IN TABLE source MATCH LINE idx.
      subrc = sy-subrc.
      DELETE source INDEX idx.
      INSERT LINES OF g_declarations INTO source INDEX idx.
    ENDIF.

    IF subrc = 0.
      FIND '* implementation' IN TABLE source MATCH LINE idx.
      subrc = sy-subrc.
      DELETE source INDEX idx.
      INSERT LINES OF g_implementation INTO source INDEX idx.
    ENDIF.

    IF subrc <> 0.
      MESSAGE text-wtl TYPE 'I' DISPLAY LIKE 'E'.
      LEAVE PROGRAM.
    ENDIF.

    rc = check_syntax( ).

  ENDMETHOD.

  METHOD check_declarations.
    DATA: code LIKE source,
          mess TYPE string,
          lin  TYPE i ##needed,
          wrd  TYPE string ##needed,
          warnings TYPE  STANDARD TABLE OF rslinlmsg.

    "Normal syntax check to get typos
    code = VALUE #( ( 'PROGRAM.' ) ).
    APPEND LINES OF g_declarations TO code.
    SYNTAX-CHECK FOR code MESSAGE mess LINE lin WORD wrd
                     ID 'MSG' TABLE warnings
                     PROGRAM sy-repid.
    rc = sy-subrc.
    IF rc <> 0.
      MESSAGE mess TYPE 'I' DISPLAY LIKE 'E'.
      RETURN.
    ENDIF.
    IF warnings IS NOT INITIAL.
      DATA(warning) = warnings[ 1 ].
      MESSAGE warning-message TYPE 'I' DISPLAY LIKE 'W'.
      RETURN.
    ENDIF.

    "Restrict to declarative statements
    code = VALUE #( ( 'PROGRAM.' )
                    ( 'CLASS class DEFINITION.' )
                    ( 'PUBLIC SECTION.' )
                    ( 'ENDCLASS.' ) ) ##no_text.
    INSERT LINES OF g_declarations INTO code INDEX lines( code ).
    SYNTAX-CHECK FOR code MESSAGE mess LINE lin WORD wrd
                     ID 'MSG' TABLE warnings
                     PROGRAM 'DEMO_GENERIC_TEMPLATE'.
    rc = sy-subrc.
    IF rc <> 0.
      MESSAGE text-dcl TYPE 'S' DISPLAY LIKE 'E'.
    ENDIF.
    IF warnings IS NOT INITIAL.
      warning = warnings[ 1 ].
      MESSAGE warning-message TYPE 'I' DISPLAY LIKE 'W'.
      RETURN.
    ENDIF.
  ENDMETHOD.

  METHOD check_implementation.
    "Only a very limited set of statements is allowed
    DATA black_list TYPE cl_demo_secure_abap_code=>string_table.
    DATA white_list TYPE cl_demo_secure_abap_code=>string_table.

    "Blacklist
    black_list = VALUE #(
      ( `->` )
      ( `=>` ) ).

    "Whitelist
    white_list = VALUE #(
      ( `FIELD-SYMBOLS`        )

      ( `CHECK`                )
      ( `EXIT`                 )
      ( `RETURN`               )

      ( `DO`                   )
      ( `ENDDO`                )
      ( `WHILE`                )
      ( `ENDWHILE`             )
      ( `CASE`                 )
      ( `WHEN`                 )
      ( `ENDCASE`              )
      ( `IF`                   )
      ( `ELSEIF`               )
      ( `ELSE`                 )
      ( `ENDIF`                )

      ( `MOVE-CORRESPONDING`   )
      ( `ASSIGN`               )
      ( `UNASSIGN`             )
      ( `CLEAR`                )
      ( `FREE`                 )

      ( `FIND`                 )
      ( `REPLACE`              )

      ( `APPEND`               )
      ( `INSERT`               )
      ( `MODIFY`               )
      ( `DELETE`               )
      ( `COLLECT`              )
      ( `READ`                 )
      ( `LOOP`                 )
      ( `ENDLOOP`              )
      ( `SORT`                 ) ).

    rc = cl_demo_secure_abap_code=>check(
      source_code  = g_implementation
      black_list   = black_list
      white_list   = white_list
      declarations = g_declarations ).
    IF rc <> 0.
      MESSAGE text-exe TYPE 'S' DISPLAY LIKE 'E'.
    ENDIF.

  ENDMETHOD.

  METHOD check_syntax.
    DATA: mess TYPE string,
          lin  TYPE i ##needed,
          wrd  TYPE string ##needed,
          warnings TYPE  STANDARD TABLE OF rslinlmsg.
    "Syntax check for implementations with declarations
    SYNTAX-CHECK FOR source MESSAGE mess LINE lin WORD wrd
                     ID 'MSG' TABLE warnings
                     PROGRAM 'DEMO_GENERIC_TEMPLATE'.
    rc = sy-subrc.
    IF rc <> 0.
      MESSAGE mess TYPE 'I' DISPLAY LIKE 'E'.
    ENDIF.
    IF warnings IS NOT INITIAL.
      DATA(warning) = warnings[ 1 ].
      MESSAGE warning-message TYPE 'I' DISPLAY LIKE 'W'.
      RETURN.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

CLASS display IMPLEMENTATION.
  METHOD class_constructor.
    "Security checks

    "Not allowed in production systems
    IF cl_abap_demo_services=>is_production_system( ).
      MESSAGE text-prs TYPE 'S' DISPLAY LIKE 'W'.
      RETURN.
    ENDIF.

    "Only users who are allowed to use the ABAP Editor
    CALL FUNCTION 'AUTHORITY_CHECK_TCODE'
      EXPORTING
        tcode  = 'SE38'
      EXCEPTIONS
        ok     = 1
        not_ok = 2
        OTHERS = 3.

    "Only users who are allowed to create and run $TMP programs
    IF sy-subrc < 2.
      AUTHORITY-CHECK OBJECT 'S_DEVELOP'
        ID 'DEVCLASS' FIELD '$TMP'
        ID 'OBJTYPE'  FIELD 'PROG'
        ID 'OBJNAME'  DUMMY
        ID 'P_GROUP'  DUMMY
        ID 'ACTVT'    FIELD '02'.
      IF sy-subrc =  0.
        read_only = abap_false.
        RETURN.
      ENDIF.
    ENDIF.

    MESSAGE text-aut TYPE 'S' DISPLAY LIKE 'E'.

  ENDMETHOD.

  METHOD main.

    prepare_editors( ).
    CALL SCREEN 100.

  ENDMETHOD.

  METHOD prepare_editors.
    DATA:
      container1 TYPE REF TO cl_gui_custom_container,
      container2 TYPE REF TO cl_gui_custom_container.

    g_declarations = VALUE #(
      ( 'DATA text TYPE string VALUE `Hello, I''m generic!`.' ) )
      ##no_text.
    g_implementation = VALUE #(
      ( 'cl_demo_output' && '=>display_text( text ).' ) )
      ##no_text.

    CREATE OBJECT container1
      EXPORTING
        container_name = 'CUSTOM_CONTAINER1'.
    g_editor1 = display=>create_abap_editor( container1 ).
    CREATE OBJECT container2
      EXPORTING
        container_name = 'CUSTOM_CONTAINER2'.
    g_editor2 = display=>create_abap_editor( container2 ).
  ENDMETHOD.

  METHOD create_abap_editor.
    CREATE OBJECT editor
      EXPORTING
        parent = parent_container.
    editor->set_toolbar_mode( 0 ).
    editor->set_statusbar_mode( 0 ).
    IF read_only = abap_true.
      editor->set_readonly_mode( 1 ).
    ELSE.
      editor->set_readonly_mode( 0 ).
    ENDIF.
  ENDMETHOD.

  METHOD fill_abap_editor.
    editor->set_text( text ).
  ENDMETHOD.

  METHOD read_abap_editor.
    editor->get_text( IMPORTING table = text ).
  ENDMETHOD.

ENDCLASS.

MODULE status_0100 OUTPUT.
  SET PF-STATUS 'STATUS_100'.
  SET TITLEBAR  'TITLE_100'.
  display=>fill_abap_editor( editor = g_editor1
                             text   = g_declarations ).
  display=>fill_abap_editor( editor = g_editor2
                             text   = g_implementation ).
ENDMODULE.

MODULE cancel_0100 INPUT.
  LEAVE PROGRAM.
ENDMODULE.

MODULE user_command_0100.
  IF g_ok_code = 'INFO'.
    CALL FUNCTION 'HELP_OBJECT_SHOW'
      EXPORTING
        dokclass = 'RE'
        doklangu = sy-langu
        dokname  = 'DEMO_GENERIC_PROGRAM'
      TABLES
        links    = g_links.
    CLEAR g_ok_code.
    RETURN.
  ENDIF.
  IF display=>read_only = abap_true.
    MESSAGE text-aut TYPE 'S' DISPLAY LIKE 'E'.
    CLEAR g_ok_code.
    RETURN.
  ENDIF.
  display=>read_abap_editor( EXPORTING editor = g_editor1
                             IMPORTING text   = g_declarations ).
  display=>read_abap_editor( EXPORTING editor = g_editor2
                            IMPORTING text   = g_implementation ).
  CASE g_ok_code.
    WHEN 'EXECUTE'.
      program=>execute( ).
    WHEN 'CHECK'.
      program=>execute( check_only = abap_true ).
    WHEN 'CLEAR'.
      CLEAR: g_declarations,
             g_implementation.
  ENDCASE.
  CLEAR g_ok_code.
ENDMODULE.

Description

The program permits declaration statements to be entered in a declaration part and operational statements to be entered in an implementation part. These entries are inserted consecutively into a method of a pattern program, which is imported into an internal table using the statement READ REPORT. When Execute is selected, the program is generated using GENERATE SUBROUTINE POOL and the method is called. Before this happens, the syntax is checked using SYNTAX-CHECK.

The ability to enter source code for a generic program presents the greatest potential security risk. The following measures have been taken to avoid abuse of this program:

  • The static constructor of the class display checks whether the program is executed in a production system (a system with production clients). Source code cannot be entered in these systems and no function codes are accepted apart from the display of documentation.
  • The static constructor of the class display checks whether the current user has authorization for ABAP Editor in the current system and development authorization for modifying and executing temporary programs. Only these users can enter source code and execute programs, since all actions possible here are also possible in the development environment.
  • Since developers in particular are tempted to test the vulnerability of their test programs, the available statements are restricted as follows:
  • Only declarative statements can be entered in the declaration part. This is checked using the same syntax check as for the declaration part of a class. This check is made in the method check_declarations of the class program.
  • Only those statements entered in a white list are valid in the implementation part. A black list prevents the use of other classes or objects (except for the output class CL_DEMO_OUTPUT). This check is made in the method check_implementation of the class program, with the method CHECK of the class CL_DEMO_SECURE_ABAP_CODE being called. If the statements INSERT, MODIFY, or DELETE are used, the addressed table must be declared in the declaration part. This prevents writes from being performed on database tables.


Note

If, despite these measures, it is still possible to generate and execute potentially dangerous code with this program without manipulating the program flow or the program data in the debugger, inform the component BC-ABA-LA immediately.