@@ -101,6 +101,10 @@ public class TikaServerProcess {
101101 private static final Logger LOG = LoggerFactory .getLogger (TikaServerProcess .class );
102102 public static int DO_NOT_RESTART_EXIT_VALUE = -100 ;
103103
104+ private static final List <String > VALID_ENDPOINTS = List .of ("tika" , "rmeta" , "meta" ,
105+ "unpack" , "detect" , "language" , "translate" , "mime" , "mime-types" , "detectors" ,
106+ "parsers" , "version" , "status" , "pipes" , "async" );
107+
104108 private static Options getOptions () {
105109 Options options = new Options ();
106110 options .addOption ("h" , "host" , true , "host name, use * for all)" );
@@ -158,6 +162,35 @@ private static void startServer(ServerDetails serverDetails) {
158162 LOG .info ("Started Apache Tika server {} at {}" , serverDetails .serverId , serverDetails .url );
159163 }
160164
165+ /**
166+ * Warns for settings that widen what a caller can reach. The security prose already
167+ * exists, but only in the message shown when a flag is *not* set -- i.e. only to the
168+ * operators who did not enable the risky thing.
169+ */
170+ private static void warnOnPermissiveConfig (TikaServerConfig config ) {
171+ if (config .isAllowPipes ()) {
172+ LOG .warn ("allowPipes is enabled: /pipes and /async can fetch and emit through "
173+ + "configured fetchers/emitters, reaching files and network resources at "
174+ + "the server's privilege level. Expose only to trusted callers." );
175+ }
176+ if (config .isAllowPerRequestConfig ()) {
177+ LOG .warn ("allowPerRequestConfig is enabled: callers may set any parser option, "
178+ + "including options that spawn external processes such as OCR." );
179+ }
180+ if (config .isReturnStackTrace ()) {
181+ LOG .warn ("returnStackTrace is enabled: error responses include server-side stack "
182+ + "traces. Intended for development, not production." );
183+ }
184+ if ("*" .equals (config .getCors ())) {
185+ LOG .warn ("cors is '*': any origin may call this server from a browser." );
186+ }
187+ if ("0.0.0.0" .equals (config .getHost ()) || "*" .equals (config .getHost ())) {
188+ LOG .warn ("host is {}: the server is reachable on every interface. tika-server "
189+ + "performs no authentication; restrict access at the network layer." ,
190+ config .getHost ());
191+ }
192+ }
193+
161194 //This returns the server, configured and ready to be started.
162195 private static ServerDetails initServer (TikaServerConfig tikaServerConfig ) throws Exception {
163196 String host = tikaServerConfig .getHost ();
@@ -199,6 +232,8 @@ private static ServerDetails initServer(TikaServerConfig tikaServerConfig) throw
199232 sf .setOutInterceptors (Collections .singletonList (new GZIPOutInterceptor ()));
200233 sf .setInInterceptors (Collections .singletonList (new GZIPInInterceptor ()));
201234
235+ warnOnPermissiveConfig (tikaServerConfig );
236+
202237 String protocol = tikaServerConfig
203238 .getTlsConfig ()
204239 .isActive () ? "https" : "http" ;
@@ -362,34 +397,38 @@ static List<ResourceProvider> loadCoreProviders(TikaServerConfig tikaServerConfi
362397 // this default set.
363398 } else {
364399 for (String endPoint : tikaServerConfig .getEndpoints ()) {
365- if ("meta" .equals (endPoint )) {
366- resourceProviders .add (new SingletonResourceProvider (new MetadataResource (tikaResource )));
367- } else if ("rmeta" .equals (endPoint )) {
368- resourceProviders .add (new SingletonResourceProvider (new RecursiveMetadataResource (tikaResource )));
369- } else if ("detect" .equals (endPoint )) {
370- resourceProviders .add (new SingletonResourceProvider (new DetectorResource (serverStatus , tikaResource )));
371- } else if ("language" .equals (endPoint )) {
372- resourceProviders .add (new SingletonResourceProvider (new LanguageResource ()));
373- } else if ("translate" .equals (endPoint )) {
374- resourceProviders .add (new SingletonResourceProvider (new TranslateResource (serverStatus )));
375- } else if ("tika" .equals (endPoint )) {
376- resourceProviders .add (new SingletonResourceProvider (tikaResource ));
377- } else if ("unpack" .equals (endPoint )) {
378- resourceProviders .add (new SingletonResourceProvider (new UnpackerResource (tikaResource )));
379- } else if ("mime" .equals (endPoint )) {
380- resourceProviders .add (new SingletonResourceProvider (new TikaMimeTypes (tikaResource )));
381- } else if ("detectors" .equals (endPoint )) {
382- resourceProviders .add (new SingletonResourceProvider (new TikaDetectors (tikaResource )));
383- } else if ("parsers" .equals (endPoint )) {
384- resourceProviders .add (new SingletonResourceProvider (new TikaParsers (tikaResource )));
385- } else if ("version" .equals (endPoint )) {
386- resourceProviders .add (new SingletonResourceProvider (new TikaVersion ()));
387- } else if ("pipes" .equals (endPoint )) {
388- addPipesResource = true ;
389- } else if ("async" .equals (endPoint )) {
390- addAsyncResource = true ;
391- } else if ("status" .equals (endPoint )) {
392- resourceProviders .add (new SingletonResourceProvider (new TikaServerStatus (serverStatus )));
400+ switch (endPoint ) {
401+ case "meta" -> resourceProviders .add (
402+ new SingletonResourceProvider (new MetadataResource (tikaResource )));
403+ case "rmeta" -> resourceProviders .add (
404+ new SingletonResourceProvider (new RecursiveMetadataResource (tikaResource )));
405+ case "detect" -> resourceProviders .add (
406+ new SingletonResourceProvider (new DetectorResource (serverStatus , tikaResource )));
407+ case "language" -> resourceProviders .add (
408+ new SingletonResourceProvider (new LanguageResource ()));
409+ case "translate" -> resourceProviders .add (
410+ new SingletonResourceProvider (new TranslateResource (serverStatus )));
411+ case "tika" -> resourceProviders .add (
412+ new SingletonResourceProvider (tikaResource ));
413+ case "unpack" -> resourceProviders .add (
414+ new SingletonResourceProvider (new UnpackerResource (tikaResource )));
415+ // "mime-types" accepted too: that is the path, and it is the natural guess.
416+ case "mime" , "mime-types" -> resourceProviders .add (
417+ new SingletonResourceProvider (new TikaMimeTypes (tikaResource )));
418+ case "detectors" -> resourceProviders .add (
419+ new SingletonResourceProvider (new TikaDetectors (tikaResource )));
420+ case "parsers" -> resourceProviders .add (
421+ new SingletonResourceProvider (new TikaParsers (tikaResource )));
422+ case "version" -> resourceProviders .add (
423+ new SingletonResourceProvider (new TikaVersion ()));
424+ case "status" -> resourceProviders .add (
425+ new SingletonResourceProvider (new TikaServerStatus (serverStatus )));
426+ case "pipes" -> addPipesResource = true ;
427+ case "async" -> addAsyncResource = true ;
428+ // Refuse rather than skip: a dropped name is a missing endpoint at
429+ // runtime with nothing in the log pointing at the config.
430+ default -> throw new TikaConfigException ("Unrecognized endpoint '" + endPoint
431+ + "'. Valid endpoints: " + VALID_ENDPOINTS );
393432 }
394433 }
395434 }
0 commit comments