Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Esran
Apr 28, 2008

Paul MaudDib posted:

Oracle finally backed off those changes a few weeks ago, back to CDDL iirc. I assume at this point the damage is done though.

Big companies where the CTO golfs with the Oracle rep, yeah Oracle tactics work there, but I can’t imagine they got any significant number of home users to sign up for a $200/year license for freaking Java.

As I understand it, that wasn't their goal. Since somewhere around Java 11, the OpenJDK has had all the bits and pieces like JFR or Mission Control that used to be exclusive to the Oracle build. Since there is no longer anything special about the Oracle build compared to any other OpenJDK build, they switched to providing their build under the more restrictive license requiring a support contract. I think the idea was if you don't pay Oracle for support, there is no reason to download their build, and you should just grab one of the free builds instead.

The changes they made by open-sourcing the parts that used to be reserved for the Oracle build was a good thing. Not sure how they managed to gently caress up communication on this so badly.

Adbot
ADBOT LOVES YOU

Esran
Apr 28, 2008
Uh, so this isn't great.

https://twitter.com/dzikoysk/status/1469091718867951618

https://www.lunasec.io/docs/blog/log4j-zero-day/

If you don't disable the feature, log4j allows log statements like logger.info("some text") to contain placeholders which log4j will interpret. One of the placeholders allows you to invoke JNDI, which means you can get the logging system to try loading code from remote servers. This means attackers just have to get you to log some string they provide, and they can load code into your process.

Esran
Apr 28, 2008

Hippie Hedgehog posted:

Yeah, at my workplace, they officially consider "any version below 2.15.0" to be vulnerable. This particular jndi/ldap exploit I think only applies above .. .2.0.9-beta2? Or something. But apparently there are numerous other unpatched vulnerabilities in log4j 1.X, so...

The JNDI part was introduced in 2.0-beta9 (https://issues.apache.org/jira/browse/LOG4J2-313), so pretty much every 2.x version is vulnerable.

1.x is not vulnerable according to https://github.com/apache/logging-log4j2/pull/608#issuecomment-990494126, at least not in the same way, but see the discussion at https://github.com/apache/logging-log4j2/pull/608#issuecomment-991723301.

Regarding other vulnerabilities in 1.x, I'm only aware of these two https://snyk.io/vuln/maven%3Alog4j%3Alog4j.

Esran
Apr 28, 2008

ExcessBLarg! posted:

That isn't exactly the problem. Like, this is always bad:
code:
log.info(untrusted);
whereas this is intended to be OK:
code:
log.info("User-passed data: {}", untrusted);
The problem is that in the latter example log4j still does property interpretation on the untrusted data. It should never do that. It should just take an arbitrary sequence of wide characters and mangle them however needs to be done to safely be printed to a log file.

That's true, but I don't think that's a very important distinction. As I understand it, only one component in Log4j (PatternAppender) even supports using lookups inside
code:
logger.info(untrusted)
I think the intent of
code:
logger.info("{}", arg)
wasn't to make logging more secure, it was to help Log4j avoid a bunch of string concatenation unless info-logging is enabled. I haven't seen anything in the documentation recommending the format-string form for security reasons at least.

Even if the format-string form didn't do property interpretation, it is still a gaping security hole in practice if a developer doing
code:
logger.info("hello " + world)
opens you up to injection where
code:
logger.info("hello {}", world)
doesn't. That would slip through review too easily I think. It would also be a problem for other JVM languages like Scala, where string interpolation exists as a language feature. People in that language will just do
code:
logger.info(s"hello $world")
and it would make log4j unusable there if doing that created a vulnerability for them.

I think from discussion on the mailing list, they are coming to the (IMO right) decision that allowing interpolation in logger input strings is too risky and does not provide much value, so they're removing that feature. Placeholders will only work in formats you specify in the logging config, which is much harder for an attacker to replace.

Esran fucked around with this message at 20:00 on Dec 13, 2021

Esran
Apr 28, 2008
Yes, agreed about loading arbitrary classes. The problem is that JNDI is a really powerful tool with an innocent-looking interface. The code in log4j2 basically just looks like this:
code:
InitialContext ctx = new InitialContext();
ctx.lookup(jndiName)
The guy who added it said he needed it to look up which application in an application server (e.g. Tomcat) the logs were coming from (it used to be common to run many applications in the same JVM), which is exposed via JNDI. Unless you know in advance that JNDI can do wild poo poo like loading code from other servers if you pass it the wrong name, I can see how this code could make it past review unscathed.

Esran
Apr 28, 2008
I think it came as a surprise to everyone (including some of the log4j devs lol :v:) that there would even be a security reason to use the interpolation form, since I don't think many people were aware that you could do placeholders in log messages in the first place.

Esran
Apr 28, 2008
Since the sample code should work out of the box, and it works on Java 11 and not 17, I think it's probably due to JOGL not being completely Java 17 compatible yet. You're more likely to get help from the JOGL devs at https://forum.jogamp.org/jogl-f782158.html, this thread seems to be discussing it https://forum.jogamp.org/JOGL-2-4-and-Java-17-report-td4041572.html.

One of the changes in Java 17 that I think is likely to affect them is https://openjdk.java.net/jeps/403, which forbids access to internal JDK classes unless you add extra JVM flags at compile time and run time.

Esran
Apr 28, 2008
I think that depends on the project and isn't a language-wide thing. Some projects are nice and straightforward, and you can go find the main method and read your way in from there. Others use frameworks like Spring, meaning you have to understand Spring or the whole thing just looks like magic. That type of project is very beginner unfriendly.

Regarding how Maven works, I think a decent way to understand it is that Maven is largely based on conventions, so there's a lot of configuration that isn't visible unless you override it in your pom.xml. For example, Maven "knows about" some standard directories it will look for code in. See https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html.

Maven builds are made up of some fixed phases that run in a fixed order, see https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference. You can run these phases from the terminal by doing "mvn phaseName". The phases are preconfigured to do certain things, e.g. compiling the Java files present in the standard directories. Individual builds can then adjust what the phases do in the pom.xml if they need something else. This means that most pom files don't contain very much customization, because a lot of projects fit into the standard phases. This also means Maven builds tend to be much more uniform across projects than builds defined in other build tools. The default build behaviors can be found on the Maven site, and are a bit of a learning cliff, but since they are unlikely to change between projects, you only have to learn them once.

I think the other thing to know about Maven is that it's essentially a plugin runner. Most of the things done by a Maven build are done by plugins, which are hooked to the build phases, either by default or explicitly in the pom.xml. So when Maven runs the compile phase, it will by default invoke the compiler plugin https://maven.apache.org/plugins/maven-compiler-plugin/, which compiles the code. Each plugin can be configured in the pom.xml if you need it to do something other than what it's doing.

Esran
Apr 28, 2008
Gradle and Maven make different tradeoffs, I don't think Gradle is strictly better in all cases. The "Maven uses XML, ew" argument isn't even true anymore, since you can write Maven poms in YAML and other languages as well via https://github.com/takari/polyglot-maven.

Regarding whether Gradle or Maven is more inscrutable, I think you could lean either way as a developer, but I think Maven definitely has an advantage when it comes to tools understanding the build. It is a lot easier for IDEs to integrate with Maven than Gradle, because understanding a Maven build is a matter of parsing an XML file, while understanding a Gradle files means executing a Groovy script, which contains both declarative and imperative parts, and trying to understand what it does.

I think the main issue with Maven is that I think there are better alternatives these days, but I'd say the same is true of Gradle. I think the build model used by tools like Bazel or Pants is both easier to understand for a beginner, and more robust for large projects.

In Bazel the build is structured into a set of targets, a la Makefiles. All targets must declare exactly their inputs and outputs and the build is sandboxed to ensure no undeclared dependencies to avoid "it works on my machine". Because there are no "it works on my machine" issues, build results can be cached with high granularity and even shared across hosts. The declarative parts of the build files (the structure of the project) is separated from the imperative parts (e.g. code to invoke the compiler), which makes IDE integrations easier.

Bazel is much less magical than either Maven or Gradle, since you have to tell it everything it should do. It's more work to set up and maintain, but I feel like the resulting build is easier to understand, and for large codebases it performs better too. Largely never having to do a "make clean" is pretty nice as well.

Esran
Apr 28, 2008

Hippie Hedgehog posted:

To be fair, Gradle has several of those good properties that you like in Bazel, like reproducible builds and great staleness checks.

Sounds good, it's been a few years since I worked with Gradle, so the tool has probably improved since I used it. It's great that it does a good job with incremental builds. I think there's still a bit of a gap between Gradle and Bazel though, since Bazel runs build targets in a sandbox, which prevents access to any files you didn't declare as dependencies of the current target. This ensures that a build target is a pure function from declared inputs to declared outputs. That makes it a lot easier to ensure that features like incremental builds (and incremental tests), remote caching and remote execution behave correctly, since you can be sure that a build output or test result can be reused if the declared inputs didn't change.

I think the Gradle people have discussed adding a sandboxed mode for the same reasons.

Adbot
ADBOT LOVES YOU

Esran
Apr 28, 2008

Ihmemies posted:

Thanks, I found out about the Java8's Comparator comparing but I was too dense to understand how to use it. I will try again :v:

I figure you've probably discovered how these work by now, but in case you haven't, Comparator.comparing is actually pretty straight forward.

The method signature is

code:
static <T, U extends Comparable<? super U>>
Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)
This looks like a mess, but focus on the keyExtractor parameter. If you look at the definition of Function, the two types involved are T (the input) and U (the output). So keyExtractor is a function that takes a T and returns a U, and that U must implement Comparable.

The idea of the comparing method is that you can take a collection of things that don't implement Comparable and compare them anyway by extracting something from them that does implement Comparable.

So let's say I have a list of Humans, and I want to compare them by height. Rather than writing the Comparator by hand, I can do

code:
Comparator<Human> humanComparator = Comparator.comparing(human -> human.getHeight())
and get a Comparator that extracts the height (an Integer, which implements Comparable) from a Human. The height is then used for comparisons when you want to compare Humans. In this case my function's type T is Human, and my return type U is Integer.

One surprise you might encounter when using methods with generic parameters like this is that the left hand side of the assignment is important. Notice that it doesn't say anywhere on the right hand side that you're working with the Human type. If you were to just write something like
code:
Comparator.comparing(human -> human.getHeight())
it would not compile, because you haven't told Java which type the "human" parameter has.

The reason it works anyway when you do the assignment is because Java's compiler will look at the left hand side and see that you're making a Comparator<Human>, and so it infers that Comparator.comparing must return a Comparator<Human>, and therefore the "human" parameter must be a Human. This is called "target typing", and it also works for method parameters.

So you can do things like

code:
myHumanStream
.sorted(Comparator.comparing(human -> human.getHeight()))
The compiler knows that you're calling sorted on a Stream of Human, so it knows that the Comparator returned by the comparing function must be a Comparator<Human>, which it then uses to infer that the type of the "human" parameter is Human.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply