Micro Java Web Framework

Overview

Micro Java Web Framework

Join the chat at https://gitter.im/decebals/pippo Travis CI Build Status Coverage Status Maven Central

It's an open source (Apache License) micro web framework in Java, with minimal dependencies and a quick learning curve.
The goal of this project is to create a micro web framework in Java that should be easy to use and hack.
The size of pippo-core is only 140 KB and the size of pippo-controller (optional) is only 45 KB.

Sample code

1. Routes approach

First we must create an Application and add some routes:

public class BasicApplication extends Application {

    @Override
    protected void onInit() {
        // send 'Hello World' as response
        GET("/", routeContext -> routeContext.send("Hello World"));

        // send a file as response
        GET("/file", routeContext -> routeContext.send(new File("pom.xml")));

        // send a json as response
        GET("/json", routeContext -> {
            Contact contact = createContact();
            routeContext.json().send(contact);
        });

        // send xml as response
        GET("/xml", routeContext -> {
            Contact contact = createContact();
            routeContext.xml().send(contact);
        });

        // send an object and negotiate the Response content-type, default to XML
        GET("/negotiate", routeContext -> {
            Contact contact = createContact();
            routeContext.xml().negotiateContentType().send(contact);
        });

        // send a template with name "hello" as response
        GET("/template", routeContext -> {
            routeContext.setLocal("greeting", "Hello");
            routeContext.render("hello");
        });
    }

    private Contact createContact() {
        return new Contact()
            .setId(12345)
            .setName("John")
            .setPhone("0733434435")
            .setAddress("Sunflower Street, No. 6");
    }

}

where Contact is a simple POJO:

public class Contact  {

    private int id;
    private String name;
    private String phone;
    private String address;

    // getters and setters

}

The second step is to choose your favorite server, template engine and content type engine.
For example, I will choose Jetty as server, Freemarker as template engine, Jackson as JSON engine and JAXB as XML engine.
My Maven pom.xml looks like:

<dependency>
    <groupId>ro.pippo</groupId>
    <artifactId>pippo-core</artifactId>
    <version>${pippo.version}</version>
</dependency>

<dependency>
    <groupId>ro.pippo</groupId>
    <artifactId>pippo-jetty</artifactId>
    <version>${pippo.version}</version>
</dependency>

<dependency>
    <groupId>ro.pippo</groupId>
    <artifactId>pippo-freemarker</artifactId>
    <version>${pippo.version}</version>
</dependency>

<dependency>
    <groupId>ro.pippo</groupId>
    <artifactId>pippo-jackson</artifactId>
    <version>${pippo.version}</version>
</dependency>

The last step it's to start Pippo with your application as parameter:

public class BasicDemo {

    public static void main(String[] args) {
        Pippo pippo = new Pippo(new BasicApplication());
        pippo.start();
    }

}

Pippo launches the embedded web server (found in your classpath) and makes the application available on port 8338 (default value). Open your internet browser and check the routes declared in Application:

  • http://localhost:8338
  • http://localhost:8338/file
  • http://localhost:8338/json
  • http://localhost:8338/xml
  • http://localhost:8338/negotiate
  • http://localhost:8338/template

2. Controllers approach

Define controller(s):

@Path("/contacts")
@Logging
public class ContactsController extends Controller {

    private ContactService contactService;

    public ContactsController() {
        contactService = new InMemoryContactService();
    }

    @GET
    @Named("index")
//    @Produces(Produces.HTML)
    @Metered
    @Logging
    public void index() {
        // inject "user" attribute in session
        getRouteContext().setSession("user", "decebal");

        // send a template with name "contacts" as response
        getResponse()
            .bind("contacts", contactService.getContacts())
            .render("contacts");
    }

    @GET("/uriFor/{id: [0-9]+}")
    @Named("uriFor")
    @Produces(Produces.TEXT)
    @Timed
    public String uriFor(@Param int id, @Header String host, @Session String user) {
        System.out.println("id = " + id);
        System.out.println("host = " + host);
        System.out.println("user = " + user);

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("id", id);

        String uri = getApplication().getRouter().uriFor("api.get", parameters);

        return "id = " + id + "; uri = " + uri;
    }

    @GET("/api")
    @Named("api.getAll")
    @Produces(Produces.JSON)
    @NoCache
    public List<Contact> getAll() {
        return contactService.getContacts();
    }

    @GET("/api/{id: [0-9]+}")
    @Named("api.get")
    @Produces(Produces.JSON)
    public Contact get(@Param int id) {
        return contactService.getContact(id);
    }

}
@Path("/files")
public class FilesController extends Controller {

    @GET
    public void index() {
        // send a template with name "files" as response
        getRouteContext().render("files");
    }

    @GET("/download")
    public File download() {
        // send a file as response
        return new File("pom.xml");
    }

    @POST("/upload")
    @Produces(Produces.TEXT)
    public String upload(FileItem file) {
        // send a text (the info about uploaded file) as response
//        return file.toString();
        return new StringBuilder()
            .append(file.getName()).append("\n")
            .append(file.getSubmittedFileName()).append("\n")
            .append(file.getSize()).append("\n")
            .append(file.getContentType())
            .toString();
    }

}

Add controller(s) in your application:

public class BasicApplication extends ControllerApplication {

    @Override
    protected void onInit() {
        addControllers(ContactsController.class); // one instance for EACH request
        // OR
        addControllers(new ContactsController()); // one instance for ALL requests

        addControllers(FilesController.class);
    }

}

Don't forget that the Controller concept is included in pippo-controller module so you must add this module as dependency in your project.

Documentation

Documentation is available on pippo.ro

Demo

Demo applications are available on pippo-demo
For a real life application built with Pippo please look at Web Accounting - Pippo Demo

Comments
  • techempower benchmark ?

    techempower benchmark ?

    I think that it would be great to have pippo in the Techempower benchmark.

    https://www.techempower.com/benchmarks

    It will not only help to get pippo known (a lot of devs use that site to choose technologies/frameworks), but also to be compared with lot's of other technologies.

    What do you think?

    question 
    opened by robertoestivill 20
  • Add security (PAC4J) module

    Add security (PAC4J) module

    Integrate Pippo with PAC4J (http://www.pac4j.org). PAC4J supports most authentication mechanisms. You can use

    • OAuth (Facebook, Twitter, Google...)
    • SAML
    • CAS
    • OpenID Connect
    • HTTP
    • OpenID
    • Google App Engine
    • LDAP
    • SQL
    • JWT
    • MongoDB
    • CouchDB
    • IP address
    • Kerberos (SPNEGO)
    • REST API

    and authorization mechanisms:

    • Roles/permissions
    • Anonymous/remember me/(fully) authenticated
    • CORS
    • CSRF
    • HTTP Security headers

    This PR try to resolve #393. Thanks to @codematix for his work.

    opened by decebals 18
  • routeContext.getParameter(

    routeContext.getParameter("parameter").toString() not returning data when application is deployed as war

    Hi decebals,

    I'm new in using pippo, and pippo is very helping me when creating API. Right now, I already created API using pippo and succeeded when I use embedded web server (jetty), but then I have an issue when I create the API to war and deploy it on tomcat / jetty (as independent web server) .

    All the parameter that can work when I'm use it with embedded server suddenly can't be accessed to be passed to the local String variable, when I use routeContext.getParameter("parameter").toString() the result always null

    Is there any other setting that I have to change when I compile the application to war?

    opened by yanvir 17
  • Request hangs on GET with content length header

    Request hangs on GET with content length header

    Whenever content-length is specified in request GET, request get blocked. https://github.com/decebals/pippo/blob/master/pippo-core/src/main/java/ro/pippo/core/Request.java#L405 - to get body. Call gets blocked at this line - https://github.com/decebals/pippo/blob/master/pippo-core/src/main/java/ro/pippo/core/Request.java#L405

    on reader.read(buffer) - becoming blocking on IO calls. I was using undertow server and changed to jetty server to know if it is server issue.

    I thought that since body is not allowed in GET, we return empty string whenever it is GET and you call getBody() but i tried for other methods also, whenever content-length is set and body is not specified then request becomes blocking Observed this behavior in 1.2+ pippo

    opened by munendrasn 17
  • Working with pippo test

    Working with pippo test

    Hi, I am new user of Pippo.

    Using pippo-test for writing test cases I referred pippo.ro couldn't find any documentation for pippo tests.

    Below is the snippet.

    public class ServiceApplicationTest {
    
       // pippo app test instance with random port
        @ClassRule
        public  static PippoRule pippoRule = new PippoRule(new ServiceApplication(), AvailablePortFinder.findAvailablePort());
    
        @Test
        public void testServiceApplication() {
            PippoTest.get("/error");
        }
    
    }
    

    Running tests gives me this. I am unable to figure it out the reason. Need some help

    java.lang.NullPointerException
        at ro.pippo.core.WebServerSettings.<init>(WebServerSettings.java:38)
        at ro.pippo.jetty.JettySettings.<init>(JettySettings.java:39)
        at ro.pippo.jetty.JettyServer.createDefaultSettings(JettyServer.java:83)
        at ro.pippo.jetty.JettyServer.createDefaultSettings(JettyServer.java:49)
        at ro.pippo.core.AbstractWebServer.getSettings(AbstractWebServer.java:34)
        at ro.pippo.test.PippoRule.createPippo(PippoRule.java:59)
        at ro.pippo.test.PippoRule$2.evaluate(PippoRule.java:104)
        at org.junit.rules.RunRules.evaluate(RunRules.java:20)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
        at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
        at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
        at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:253)
        at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
    
    opened by munendrasn 17
  • Cleaning up a request-response cycle regardless of errors

    Cleaning up a request-response cycle regardless of errors

    Consider this scenario where we are getting a Db instance to be shared throughout a request-response cycle:

    // Preparation route for 
    ALL("/.*", (request, response, chain) -> {
        response.bind("db", getDb());
    }); 
    
    POST("/{id}", (request, response, chain) -> {
        Db db = (Db) response.getLocals().get("db");
        // this throws an runtime exception somewhere deep inside
        db.insert(something);
    });
    
    // Cleanup route
    ALL("/.*", (request, response, chain) -> {
        Db db = (Db) response.getLocals().remove("db");
        db.release();
    }); 
    

    If an exception is thrown in POST then the cleanup route is never executed and we are now leaking Db connections. Instead of trapping Db exceptions in every handler, it would be nice to be able to specify cleanup routes/routines that are guaranteed to execute at the end of the Request-Response cycle despite exceptions within the chain. These mechanisms should be fail-safe themselves and should not have a chain parameter.

    void cleanup(Request request, Response response);
    

    The RouteHandlerChain or PippoFilter should execute these cleanup routines within independent try {} catch {} blocks.

    for (CleanupHandler handler : cleanupHandlers) {
        try {
            handler.cleanup(request, response);
        } catch (Exception e) {
            log.error("Whoops", e);
        }
    }
    

    Perhaps these could be registered as:

    // Cleanup route
    CLEANUP("/.*", (request, response) -> {
        Db db = (Db) response.getLocals().remove("db");
        db.release();
    }); 
    
    opened by gitblit 16
  • New controller and routing system ignores global request interceptors

    New controller and routing system ignores global request interceptors

    1. Whenever I annotate my action method with more then 1 route annotation for example @GET("") and @PUT("") only the first one defined is actualy enabled and executed.

    2. I tried to create some basic type of interceptors for example one annotation @JustAtest that is placed on my controller class and a second one annotation @Required that is placed on an action method. When I annotade my controller class with @JustAtest and some of my action method with @Required only the @Required annotation is executed. Both of them should be executed right ?

    3. My application based filters are not executed anymore after using controllers. In my Application I have defined this filter:

    // auth user before filter
    ALL("/.*", new RouteHandler() {
    
    	@Override
    	public void handle(RouteContext routeContext) {
    		log.error("WHY IT DOES NOT GET HERE");
    	}
    
    });
    
    opened by dunsunik 15
  • Handling Accept-Encoding request

    Handling Accept-Encoding request

    How to handle Accept-Encoding header in request? Suppose Accept-Encoding=gzip then, response header should contain Content-Encoding=gzip. How to achieve this? Just adding header to response wouldn't be sufficient. Is there way already available in pippo to handle this or custom implementation is required for this??

    opened by munendrasn 15
  • Route Groups

    Route Groups

    Before:

    @Override
    protected void onInit() {
    
        GET("/users/{id}", routeContext -> {
            routeContext.send("find user, id: " + routeContext.getParameter("id").toString());
        });
    
        POST("/users", routeContext -> routeContext.send("create a new user"));
    
        PUT("/users/{id}", routeContext -> {
            routeContext.send("modify user, id: " + routeContext.getParameter("id").toString());
        });
    
        DELETE("/users/{id}", routeContext -> {
            routeContext.send("delete user, id: " + routeContext.getParameter("id").toString());
        });
    }
    

    After:

    @Override
    protected void onInit() {
    
        GROUP("/users", new RouteGroupHandler() {
    
            @Override
            public void handle() {
    
                GET("{id}", routeContext -> {
                    routeContext.send("find user, id: " + routeContext.getParameter("id").toString());
                });
    
                POST(routeContext -> routeContext.send("create a new user"));
    
                PUT("{id}", routeContext -> {
                    routeContext.send("modify user, id: " + routeContext.getParameter("id").toString());
                });
    
                DELETE("{id}", routeContext -> {
                    routeContext.send("delete user, id: " + routeContext.getParameter("id").toString());
                });
            }
        });
    }
    

    I think it's better

    ps: i pulled into a wrong branch?

    opened by ScienJus 15
  • Out of heap space for larger file uploads

    Out of heap space for larger file uploads

    Hello,

    I experience such an error while trying to write my uploads (20MB file) onto a disk.

    FileItem file = cx.getRequest().getFile("file"); file.write(new File('/tmp/myfile.jpg'));

    Looking at pippo framework source code write method calls IoUtils.copy method. And that one uses a buffered read approach. So obviously it should not be a problem, right ? So why does it eat my heap space memory ?

    Thank you

    opened by dunsunik 14
  • Issue #497: adopt build for java 11

    Issue #497: adopt build for java 11

    Various fixes to make maven build work on java 11 (see #497) :

    • lombok version update. it is used in single file (test); can be removed completely from the project
    • add explicit jaxb dependencies where required; see https://stackoverflow.com/questions/43574426/how-to-resolve-java-lang-noclassdeffounderror-javax-xml-bind-jaxbexception-in-j
    • fix javadoc issues (closing

      tags, wrong @see syntax, html entities)

    • update mockito versions
    • update maven plugin versions
    opened by whyicantusemyemailasusername 14
  • Please Open a GitHub Security Advisory

    Please Open a GitHub Security Advisory

    Hello, I'm an independent security researcher performing security research under the GitHub Security Lab Bug Bounty Program. I believe I may have found a security vulnerability in this project.

    Please open a security advisory against this repository so we can privately discuss the details. This advisory can be opened by a user with admin permissions on this repository.

    https://github.com/pippo-java/pippo/security/advisories

    @decebals, you should be able to view the disclosure here: https://github.com/JLLeitschuh/security-research/security/advisories/GHSA-v956-x5m6-xj62

    Vulnerability disclosure will occur on Mar 14, 2023.

    opened by JLLeitschuh 0
  • Bump spring-web from 4.1.1.RELEASE to 6.0.0 in /pippo-controller-parent/pippo-spring

    Bump spring-web from 4.1.1.RELEASE to 6.0.0 in /pippo-controller-parent/pippo-spring

    Bumps spring-web from 4.1.1.RELEASE to 6.0.0.

    Release notes

    Sourced from spring-web's releases.

    v6.0.0

    See What's New in Spring Framework 6.x and Upgrading to Spring Framework 6.x for upgrade instructions and details of new features.

    :star: New Features

    • Avoid direct URL construction and URL equality checks #29486
    • Simplify creating RFC 7807 responses from functional endpoints #29462
    • Allow test classes to provide runtime hints via declarative mechanisms #29455

    :notebook_with_decorative_cover: Documentation

    • Align javadoc of DefaultParameterNameDiscoverer with its behavior #29494
    • Document AOT support in the TestContext framework #29482
    • Document Ahead of Time processing in the reference guide #29350

    :hammer: Dependency Upgrades

    • Upgrade to Reactor 2022.0.0 #29465

    :heart: Contributors

    Thank you to all the contributors who worked on this release:

    @​ophiuhus and @​wilkinsona

    v6.0.0-RC4

    :star: New Features

    • Introduce DataFieldMaxValueIncrementer for SQL Server sequences #29447
    • Introduce findAllAnnotationsOnBean variant on ListableBeanFactory #29446
    • Introduce support for Jakarta WebSocket 2.1 #29436
    • Allow @ControllerAdvice in WebFlux to handle exceptions before a handler is selected #22991

    :lady_beetle: Bug Fixes

    • Bean with unresolved generics do not use fallback algorithms with AOT #29454
    • TomcatRequestUpgradeStrategy is not compatible with Tomcat 10.1 #29434
    • Autowiring of a generic type produced by a factory bean fails after AOT processing #29385

    :notebook_with_decorative_cover: Documentation

    • Reference PDF containing full docs not available #28451

    :hammer: Dependency Upgrades

    • Revisit Servlet API baseline: Servlet 6.0 in the build, Servlet 5.0 compatibility at runtime #29435
    • Upgrade to Context Propagation 1.0.0 #29442
    • Upgrade to Jackson 2.14.0 #29351
    • Upgrade to Micrometer 1.10.0 #29441

    ... (truncated)

    Commits
    • 5a30a43 Release v6.0.0
    • 42856ba Add milestone repo for optional Netty 5 support
    • 9be6cea Polishing deprecated methods
    • 37b4391 Align javadoc of DefaultParameterNameDiscoverer with its behavior
    • 09a58a5 Polish
    • 10f4ad1 Assert fixed in DefaultErrorResponseBuilder
    • 9457ed3 Document AOT support in the TestContext framework
    • 074ec97 Fix section formatting in the testing chapter
    • 9ede4af Revert "Ignore HttpComponents Javadoc"
    • bfc1251 Merge branch '5.3.x'
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump tomcat-embed-core from 8.5.61 to 8.5.63 in /pippo-server-parent/pippo-tomcat

    Bump tomcat-embed-core from 8.5.61 to 8.5.63 in /pippo-server-parent/pippo-tomcat

    Bumps tomcat-embed-core from 8.5.61 to 8.5.63.

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • fix(sec): upgrade junit:junit to 4.13.1

    fix(sec): upgrade junit:junit to 4.13.1

    What happened?

    There are 1 security vulnerabilities found in junit:junit 4.13

    What did I do?

    Upgrade junit:junit from 4.13 to 4.13.1 for vulnerability fix

    What did you expect to happen?

    Ideally, no insecure libs should be used.

    The specification of the pull request

    PR Specification from OSCS

    opened by lxxawfl 0
  • [SECURITY] Fix Zip Slip Vulnerability

    [SECURITY] Fix Zip Slip Vulnerability

    Security Vulnerability Fix

    This pull request fixes a Zip Slip vulnerability either due to an insufficient, or missing guard when unzipping zip files.

    Even if you deem, as the maintainer of this project, this is not necessarily fixing a security vulnerability, it is still, most likely, a valid security hardening.

    Preamble

    Impact

    This issue allows a malicious zip file to potentially break out of the expected destination directory, writing contents into arbitrary locations on the file system. Overwriting certain files/directories could allow an attacker to achieve remote code execution on a target system by exploiting this vulnerability.

    Why?

    The best description of Zip-Slip can be found in the white paper published by Snyk: Zip Slip Vulnerability

    But I had a guard in place, why wasn't it sufficient?

    If the changes you see are a change to the guard, not the addition of a new guard, this is probably because this code contains a Zip-Slip vulnerability due to a partial path traversal vulnerability.

    To demonstrate this vulnerability, consider "/usr/outnot".startsWith("/usr/out"). The check is bypassed although /outnot is not under the /out directory. It's important to understand that the terminating slash may be removed when using various String representations of the File object. For example, on Linux, println(new File("/var")) will print /var, but println(new File("/var", "/") will print /var/; however, println(new File("/var", "/").getCanonicalPath()) will print /var.

    The Fix

    Implementing a guard comparing paths with the method java.nio.files.Path#startsWith will adequately protect against this vulnerability.

    For example: file.getCanonicalFile().toPath().startsWith(BASE_DIRECTORY) or file.getCanonicalFile().toPath().startsWith(BASE_DIRECTORY_FILE.getCanonicalFile().toPath())

    Other Examples

    :arrow_right: Vulnerability Disclosure :arrow_left:

    :wave: Vulnerability disclosure is a super important part of the vulnerability handling process and should not be skipped! This may be completely new to you, and that's okay, I'm here to assist!

    First question, do we need to perform vulnerability disclosure? It depends!

    1. Is the vulnerable code only in tests or example code? No disclosure required!
    2. Is the vulnerable code in code shipped to your end users? Vulnerability disclosure is probably required!

    For partial path traversal, consider if user-supplied input could ever flow to this logic. If user-supplied input could reach this conditional, it's insufficient and, as such, most likely a vulnerability.

    Vulnerability Disclosure How-To

    You have a few options options to perform vulnerability disclosure. However, I'd like to suggest the following 2 options:

    1. Request a CVE number from GitHub by creating a repository-level GitHub Security Advisory. This has the advantage that, if you provide sufficient information, GitHub will automatically generate Dependabot alerts for your downstream consumers, resolving this vulnerability more quickly.
    2. Reach out to the team at Snyk to assist with CVE issuance. They can be reached at the Snyk's Disclosure Email. Note: Please include JLLeitschuh Disclosure in the subject of your email so it is not missed.

    Detecting this and Future Vulnerabilities

    You can automatically detect future vulnerabilities like this by enabling the free (for open-source) GitHub Action.

    I'm not an employee of GitHub, I'm simply an open-source security researcher.

    Source

    This contribution was automatically generated with an OpenRewrite refactoring recipe, which was lovingly handcrafted to bring this security fix to your repository.

    The source code that generated this PR can be found here: Zip Slip

    Why didn't you disclose privately (ie. coordinated disclosure)?

    This PR was automatically generated, in-bulk, and sent to this project as well as many others, all at the same time.

    This is technically what is called a "Full Disclosure" in vulnerability disclosure, and I agree it's less than ideal. If GitHub offered a way to create private pull requests to submit pull requests, I'd leverage it, but that infrastructure, sadly, doesn't exist yet.

    The problem is that, as an open source software security researcher, I (exactly like open source maintainers), I only have so much time in a day. I'm able to find vulnerabilities impacting hundreds, or sometimes thousands of open source projects with tools like GitHub Code Search and CodeQL. The problem is that my knowledge of vulnerabilities doesn't scale very well.

    Individualized vulnerability disclosure takes time and care. It's a long and tedious process, and I have a significant amount of experience with it (I have over 50 CVEs to my name). Even tracking down the reporting channel (email, Jira, etc..) can take time and isn't automatable. Unfortunately, when facing problems of this scale, individual reporting doesn't work well either.

    Additionally, if I just spam out emails or issues, I'll just overwhelm already over-taxed maintainers, I don't want to do this either.

    By creating a pull request, I am aiming to provide maintainers something highly actionable to actually fix the identified vulnerability; a pull request.

    There's a larger discussion on this topic that can be found here: https://github.com/JLLeitschuh/security-research/discussions/12

    Opting Out

    If you'd like to opt out of future automated security vulnerability fixes like this, please consider adding a file called .github/GH-ROBOTS.txt to your repository with the line:

    User-agent: JLLeitschuh/security-research
    Disallow: *
    

    This bot will respect the ROBOTS.txt format for future contributions.

    Alternatively, if this project is no longer actively maintained, consider archiving the repository.

    CLA Requirements

    This section is only relevant if your project requires contributors to sign a Contributor License Agreement (CLA) for external contributions.

    It is unlikely that I'll be able to directly sign CLAs. However, all contributed commits are already automatically signed off.

    The meaning of a signoff depends on the project, but it typically certifies that committer has the rights to submit this work under the same license and agrees to a Developer Certificate of Origin (see https://developercertificate.org/ for more information).

    - Git Commit Signoff documentation

    If signing your organization's CLA is a strict-requirement for merging this contribution, please feel free to close this PR.

    Sponsorship & Support

    This contribution is sponsored by HUMAN Security Inc. and the new Dan Kaminsky Fellowship, a fellowship created to celebrate Dan's memory and legacy by funding open-source work that makes the world a better (and more secure) place.

    This PR was generated by Moderne, a free-for-open source SaaS offering that uses format-preserving AST transformations to fix bugs, standardize code style, apply best practices, migrate library versions, and fix common security vulnerabilities at scale.

    Tracking

    All PR's generated as part of this fix are tracked here: https://github.com/JLLeitschuh/security-research/issues/16

    opened by JLLeitschuh 0
Owner
Pippo
Micro Java Web Framework
Pippo
An evolving set of open source web components for building mobile and desktop web applications in modern browsers.

Vaadin components Vaadin components is an evolving set of high-quality user interface web components commonly needed in modern mobile and desktop busi

Vaadin 519 Dec 31, 2022
Vaadin 6, 7, 8 is a Java framework for modern Java web applications.

Vaadin Framework Vaadin allows you to build modern web apps efficiently in plain Java, without touching low level web technologies. This repository co

Vaadin 1.7k Jan 5, 2023
Ninja is a full stack web framework for Java. Rock solid, fast and super productive.

_______ .___ _______ ____. _____ \ \ | |\ \ | | / _ \ / | \| |/ | \ | |/ /_\ \ / | \

Ninja Web Framework 1.9k Jan 5, 2023
The modular web framework for Java and Kotlin

∞ do more, more easily Jooby is a modern, performant and easy to use web framework for Java and Kotlin built on top of your favorite web server. Java:

jooby 1.5k Dec 16, 2022
Apache Wicket - Component-based Java web framework

What is Apache Wicket? Apache Wicket is an open source, java, component based, web application framework. With proper mark-up/logic separation, a POJO

The Apache Software Foundation 657 Dec 31, 2022
True Object-Oriented Java Web Framework

Project architect: @paulodamaso Takes is a true object-oriented and immutable Java8 web development framework. Its key benefits, comparing to all othe

Yegor Bugayenko 748 Dec 23, 2022
ZK is a highly productive Java framework for building amazing enterprise web and mobile applications

ZK ZK is a highly productive Java framework for building amazing enterprise web and mobile applications. Resources Documentation Tutorial ZK Essential

ZK 375 Dec 23, 2022
An Intuitive, Lightweight, High Performance Full Stack Java Web Framework.

mangoo I/O mangoo I/O is a Modern, Intuitive, Lightweight, High Performance Full Stack Java Web Framework. It is a classic MVC-Framework. The foundati

Sven Kubiak 52 Oct 31, 2022
A simple expressive web framework for java. Spark has a kotlin DSL https://github.com/perwendel/spark-kotlin

Spark - a tiny web framework for Java 8 Spark 2.9.3 is out!! Changeset <dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-core</a

Per Wendel 9.4k Dec 29, 2022
A web MVC action-based framework, on top of CDI, for fast and maintainable Java development.

A web MVC action-based framework, on top of CDI, for fast and maintainable Java development. Downloading For a quick start, you can use this snippet i

Caelum 347 Nov 15, 2022
A server-state reactive Java web framework for building real-time user interfaces and UI components.

RSP About Maven Code examples HTTP requests routing HTML markup Java DSL Page state model Single-page application Navigation bar URL path UI Component

Vadim Vashkevich 33 Jul 13, 2022
Javalin - A simple web framework for Java and Kotlin

Javalin is a very lightweight web framework for Kotlin and Java which supports WebSockets, HTTP2 and async requests. Javalin’s main goals are simplicity, a great developer experience, and first class interoperability between Kotlin and Java.

David (javalin.io) 6.2k Jan 6, 2023
The Grails Web Application Framework

Build Status Slack Signup Slack Signup Grails Grails is a framework used to build web applications with the Groovy programming language. The core fram

grails 2.7k Jan 5, 2023
jetbrick web mvc framework

jetbrick-webmvc Web MVC framework for jetbrick. Documentation http://subchen.github.io/jetbrick-webmvc/ Dependency <dependency> <groupId>com.githu

Guoqiang Chen 25 Nov 15, 2022
🚀 The best rbac web framework. base on Spring Boot 2.4、 Spring Cloud 2020、 OAuth2 . Thx Give a star

?? The best rbac web framework. base on Spring Boot 2.4、 Spring Cloud 2020、 OAuth2 . Thx Give a star

pig-mesh 4.3k Jan 8, 2023
Java Web Toolkit

What is JWt ? JWt is a Java library for developing web applications. It provides a pure Java component-driven approach to building web applications, a

null 48 Jul 16, 2022
RESTEasy is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications

RESTEasy RESTEasy is a JBoss.org project aimed at providing productivity frameworks for developing client and server RESTful applications and services

RESTEasy 1k Dec 23, 2022
This is a Playwright Java Framework written in POM

Playwright Java Framework Main Objective of this Framework is to Build an Automation Framework that can be scalable, Easily Configurable and Ready to

AutoInfra 11 Oct 17, 2022
A Java Framework for Building Bots on Facebook's Messenger Platform.

Racter A Java Framework for Building Bots on Facebook's Messenger Platform. Documentation Installation To add a dependency using Maven, use the follow

Ahmed 18 Dec 19, 2022