bpo-37363: Add audit events on startup for the run commands (GH-14524)

(cherry picked from commit e226e83d36dfc7220d836fb7a249ce18e70cb4a6)

Co-authored-by: Steve Dower <steve.dower@python.org>
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 856144b..3d27ed2 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -1235,6 +1235,101 @@
     }
 }
 
+typedef struct {
+    const char* expected;
+    int exit;
+} AuditRunCommandTest;
+
+static int _audit_hook_run(const char *eventName, PyObject *args, void *userData)
+{
+    AuditRunCommandTest *test = (AuditRunCommandTest*)userData;
+    if (strcmp(eventName, test->expected)) {
+        return 0;
+    }
+
+    if (test->exit) {
+        PyObject *msg = PyUnicode_FromFormat("detected %s(%R)", eventName, args);
+        if (msg) {
+            printf("%s\n", PyUnicode_AsUTF8(msg));
+            Py_DECREF(msg);
+        }
+        exit(test->exit);
+    }
+
+    PyErr_Format(PyExc_RuntimeError, "detected %s(%R)", eventName, args);
+    return -1;
+}
+
+static int test_audit_run_command(void)
+{
+    AuditRunCommandTest test = {"cpython.run_command"};
+    wchar_t *argv[] = {L"./_testembed", L"-c", L"pass"};
+
+    Py_IgnoreEnvironmentFlag = 0;
+    PySys_AddAuditHook(_audit_hook_run, (void*)&test);
+
+    return Py_Main(Py_ARRAY_LENGTH(argv), argv);
+}
+
+static int test_audit_run_file(void)
+{
+    AuditRunCommandTest test = {"cpython.run_file"};
+    wchar_t *argv[] = {L"./_testembed", L"filename.py"};
+
+    Py_IgnoreEnvironmentFlag = 0;
+    PySys_AddAuditHook(_audit_hook_run, (void*)&test);
+
+    return Py_Main(Py_ARRAY_LENGTH(argv), argv);
+}
+
+static int run_audit_run_test(int argc, wchar_t **argv, void *test)
+{
+    PyStatus status;
+    PyConfig config;
+    status = PyConfig_InitPythonConfig(&config);
+    if (PyStatus_Exception(status)) {
+        Py_ExitStatusException(status);
+    }
+    config.argv.length = argc;
+    config.argv.items = argv;
+    config.parse_argv = 1;
+    config.program_name = argv[0];
+    config.interactive = 1;
+    config.isolated = 0;
+    config.use_environment = 1;
+    config.quiet = 1;
+
+    PySys_AddAuditHook(_audit_hook_run, test);
+
+    status = Py_InitializeFromConfig(&config);
+    if (PyStatus_Exception(status)) {
+        Py_ExitStatusException(status);
+    }
+
+    return Py_RunMain();
+}
+
+static int test_audit_run_interactivehook(void)
+{
+    AuditRunCommandTest test = {"cpython.run_interactivehook", 10};
+    wchar_t *argv[] = {L"./_testembed"};
+    return run_audit_run_test(Py_ARRAY_LENGTH(argv), argv, &test);
+}
+
+static int test_audit_run_startup(void)
+{
+    AuditRunCommandTest test = {"cpython.run_startup", 10};
+    wchar_t *argv[] = {L"./_testembed"};
+    return run_audit_run_test(Py_ARRAY_LENGTH(argv), argv, &test);
+}
+
+static int test_audit_run_stdin(void)
+{
+    AuditRunCommandTest test = {"cpython.run_stdin"};
+    wchar_t *argv[] = {L"./_testembed"};
+    return run_audit_run_test(Py_ARRAY_LENGTH(argv), argv, &test);
+}
+
 static int test_init_read_set(void)
 {
     PyStatus status;
@@ -1413,6 +1508,11 @@
     {"test_open_code_hook", test_open_code_hook},
     {"test_audit", test_audit},
     {"test_audit_subinterpreter", test_audit_subinterpreter},
+    {"test_audit_run_command", test_audit_run_command},
+    {"test_audit_run_file", test_audit_run_file},
+    {"test_audit_run_interactivehook", test_audit_run_interactivehook},
+    {"test_audit_run_startup", test_audit_run_startup},
+    {"test_audit_run_stdin", test_audit_run_stdin},
     {NULL, NULL}
 };