Skip to content

Commit

Permalink
Extend Tsunami's core engine to support dynamic plugins.
Browse files Browse the repository at this point in the history
A dynamic plugin is a plugin that is entirely created at runtime.

PiperOrigin-RevId: 701269359
Change-Id: I10adafb30dfbee80da5d81eb374c94e745438179
  • Loading branch information
tooryx authored and copybara-github committed Nov 29, 2024
1 parent 08be617 commit 02eb2ad
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,26 @@ protected final void registerPlugin(Class<? extends TsunamiPlugin> tsunamiPlugin
.to(tsunamiPluginClazz);
logger.atInfo().log("Plugin %s is registered.", tsunamiPluginClazz);
}

/**
* Register a {@link TsunamiPlugin} that is dynamically created at runtime.
*
* @param name the name of the plugin
* @param author the author of the plugin
* @param requiresCallbackServer whether the plugin requires a callback server
* @param tsunamiPlugin the {@link TsunamiPlugin} to be registered
*/
protected final void registerDynamicPlugin(
PluginType pluginType,
String name,
String author,
boolean requiresCallbackServer,
boolean isForWebService,
TsunamiPlugin tsunamiPlugin) {
var pluginDef =
PluginDefinition.forDynamicPlugin(
pluginType, name, author, isForWebService, requiresCallbackServer);
tsunamiPluginBinder.addBinding(pluginDef).toInstance(tsunamiPlugin);
logger.atInfo().log("Dynamic plugin registered: %s", pluginDef.name());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@
/** A data class that captures all the definition details about a {@link TsunamiPlugin}. */
@AutoValue
abstract class PluginDefinition {
abstract PluginInfo pluginInfo();
abstract PluginType type();

abstract String name();

abstract String author();

abstract String version();

abstract Optional<ForServiceName> targetServiceName();

Expand All @@ -53,22 +59,6 @@ public String id() {
return String.format("/%s/%s/%s/%s", author(), type(), name(), version());
}

public PluginType type() {
return pluginInfo().type();
}

public String name() {
return pluginInfo().name();
}

public String author() {
return pluginInfo().author();
}

public String version() {
return pluginInfo().version();
}

/**
* Factory method for creating a {@link PluginDefinition} from the {@link TsunamiPlugin} class.
*
Expand All @@ -93,7 +83,10 @@ public static PluginDefinition forPlugin(Class<? extends TsunamiPlugin> pluginCl
pluginClazz);

return new AutoValue_PluginDefinition(
pluginInfo.get(),
pluginInfo.get().type(),
pluginInfo.get().name(),
pluginInfo.get().author(),
pluginInfo.get().version(),
targetServiceName,
targetSoftware,
isForWebService,
Expand All @@ -109,12 +102,46 @@ public static PluginDefinition forPlugin(Class<? extends TsunamiPlugin> pluginCl
* @return a {@link PluginDefinition} built from the plugin info.
*/
public static PluginDefinition forRemotePlugin(PluginInfo remotePluginInfo) {
checkNotNull(remotePluginInfo);
return new AutoValue_PluginDefinition(
checkNotNull(remotePluginInfo),
remotePluginInfo.type(),
remotePluginInfo.name(),
remotePluginInfo.author(),
remotePluginInfo.version(),
Optional.empty(),
Optional.empty(),
false,
Optional.empty(),
false);
}

/**
* Factory method for creating a {@link PluginDefinition} for dynamic plugins. A dynamic plugin is
* a plugin that is created at runtime and for which we cannot rely on the PluginInfo annotation.
*
* <p>Note that for dynamic plugins, we drop the notion of version and forces it to be 1.0.
*
* @param pluginName the name of the plugin
* @param pluginAuthor the author of the plugin
* @param isForWebService whether the plugin is for web service
* @param requiresCallbackServer whether the plugin requires a callback server
* @return a {@link PluginDefinition} built from the plugin info.
*/
public static PluginDefinition forDynamicPlugin(
PluginType pluginType,
String pluginName,
String pluginAuthor,
boolean isForWebService,
boolean requiresCallbackServer) {
return new AutoValue_PluginDefinition(
pluginType,
pluginName,
pluginAuthor,
"1.0",
Optional.empty(),
Optional.empty(),
isForWebService,
Optional.empty(),
requiresCallbackServer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private <T> PluginExecutionResult<T> buildSucceededResult(
}
logger.atInfo().log(
"%s plugin execution finished in %d (ms)",
executorConfig.matchedPlugin().pluginDefinition().pluginInfo().name(),
executorConfig.matchedPlugin().pluginDefinition().name(),
executionStopwatch.elapsed().toMillis());
return PluginExecutionResult.<T>builder()
.setExecutionStatus(ExecutionStatus.SUCCEEDED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ public void forRemotePlugin_always_generatesCorrectDefinition() {
PluginInfo pluginInfo = FakeRemoteVulnDetector.class.getAnnotation(PluginInfo.class);
PluginDefinition pluginDefinition = PluginDefinition.forRemotePlugin(pluginInfo);

assertThat(pluginDefinition.pluginInfo()).isEqualTo(pluginInfo);
assertThat(pluginDefinition.type()).isEqualTo(pluginInfo.type());
assertThat(pluginDefinition.name()).isEqualTo(pluginInfo.name());
assertThat(pluginDefinition.author()).isEqualTo(pluginInfo.author());
assertThat(pluginDefinition.version()).isEqualTo(pluginInfo.version());
assertThat(pluginDefinition.id())
.isEqualTo("/fake/REMOTE_VULN_DETECTION/FakeRemoteVulnDetector/v0.1");
}
Expand Down

0 comments on commit 02eb2ad

Please sign in to comment.