--- a/net/minecraft/world/level/BaseCommandBlock.java
+++ b/net/minecraft/world/level/BaseCommandBlock.java
@@ -35,6 +_,9 @@
     private String command = "";
     @Nullable
     private Component customName;
+    // CraftBukkit start
+    protected abstract org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper);
+    // CraftBukkit end
 
     public int getSuccessCount() {
         return this.successCount;
@@ -112,7 +_,13 @@
                                 this.successCount++;
                             }
                         });
-                        server.getCommands().performPrefixedCommand(commandSourceStack, this.command);
+                        // Paper start - ServerCommandEvent
+                        org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(commandSourceStack.getBukkitSender(), net.minecraft.commands.Commands.trimOptionalPrefix(this.command));
+                        if (!event.callEvent()) {
+                            return true;
+                        }
+                        server.getCommands().performPrefixedCommand(commandSourceStack, event.getCommand());
+                        // Paper end - ServerCommandEvent
                     }
                 } catch (Throwable var8) {
                     CrashReport crashReport = CrashReport.forThrowable(var8, "Executing command block");
@@ -168,7 +_,7 @@
     }
 
     public InteractionResult usedBy(Player player) {
-        if (!player.canUseGameMasterBlocks()) {
+        if (!player.canUseGameMasterBlocks() && (!player.isCreative() || !player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission
             return InteractionResult.PASS;
         } else {
             if (player.level().isClientSide()) {
@@ -185,7 +_,7 @@
 
     public abstract boolean isValid();
 
-    protected class CloseableCommandBlockSource implements CommandSource, AutoCloseable {
+    public class CloseableCommandBlockSource implements CommandSource, AutoCloseable { // Paper - public
         private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss", Locale.ROOT);
         private boolean closed;
 
@@ -207,6 +_,7 @@
         @Override
         public void sendSystemMessage(Component component) {
             if (!this.closed) {
+                org.spigotmc.AsyncCatcher.catchOp("sendSystemMessage to a command block"); // Paper - Don't broadcast messages to command blocks
                 BaseCommandBlock.this.lastOutput = Component.literal("[" + TIME_FORMAT.format(ZonedDateTime.now()) + "] ").append(component);
                 BaseCommandBlock.this.onUpdated();
             }
@@ -216,5 +_,12 @@
         public void close() throws Exception {
             this.closed = true;
         }
+
+        // CraftBukkit start
+        @Override
+        public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) {
+            return BaseCommandBlock.this.getBukkitSender(wrapper);
+        }
+        // CraftBukkit end
     }
 }
