Blog

OWASP dependency-check を利用して脆弱性のある Java ライブラリに依存していないか確認する

依存しているライブラリに脆弱性がある場合、それを検出できると嬉しい。

OWASP dependency-check の gradle プラグインを入れると、簡単に検出が可能となる。

設定は以下のようであり、非常に容易である。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath('org.owasp:dependency-check-gradle:3.2.1')
    }
}
apply plugin: 'org.owasp.dependencycheck'

check.dependsOn dependencyCheckAnalyze

// https://jeremylong.github.io/DependencyCheck/dependency-check-gradle/configuration.html
dependencyCheck {
    // Threshold value to fail build
    // https://www.first.org/cvss/specification-document
    // 0.1-3.9=low 4.0-6.9=medium 7.0-8.9=High 9.0-10.0=Critical
    failBuildOnCVSS=0
}

failBuildOnCVSS の値を設定することで、CVSS Score が 7.0 以上、つまり脆弱性としての評価が High 以上のもんのの場合は FAIL する、というようなルールを設定することが可能だ。

例えば、spring boot 1.5.0 に依存している場合は以下のような出力になる。

$ ./gradlew check
:dependencyCheckAnalyze
Verifying dependencies for project demo
Checking for updates and analyzing vulnerabilities for dependencies
Generating report for project demo
Found 24 vulnerabilities in project demo


One or more dependencies were identified with known vulnerabilities:

spring-boot-starter-security-1.5.0.RELEASE.jar (org.springframework.boot:spring-boot-starter-security:1.5.0.RELEASE, cpe:/a:pivotal_software:spring_boot:1.5.0, cpe:/a:pivotal_software:spring_security:1.5.0) : CVE-2017-8046, CVE-2018-1196
spring-boot-starter-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-starter:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-aop-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-aop:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-security-config-4.2.1.RELEASE.jar (org.springframework.security:spring-security-config:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-security-web-4.2.1.RELEASE.jar (org.springframework.security:spring-security-web:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-boot-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-boot-autoconfigure-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-autoconfigure:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-boot-starter-logging-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-starter-logging:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-core-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-core:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-beans-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-beans:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-security-core-4.2.1.RELEASE.jar (org.springframework.security:spring-security-core:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-context-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-context:4.3.6.RELEASE) : CVE-2018-1199
spring-expression-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-expression:4.3.6.RELEASE) : CVE-2018-1199
spring-web-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-web:4.3.6.RELEASE) : CVE-2018-1199
logback-classic-1.1.9.jar (cpe:/a:logback:logback:1.1.9, ch.qos.logback:logback-classic:1.1.9) : CVE-2017-5929
logback-core-1.1.9.jar (cpe:/a:logback:logback:1.1.9, ch.qos.logback:logback-core:1.1.9) : CVE-2017-5929


See the dependency-check report for more details.


:dependencyCheckAnalyze FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':dependencyCheckAnalyze'.
>

  Dependency-Analyze Failure:
  One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '0.0': CVE-2017-5929, CVE-2017-8046, CVE-2017-4995, CVE-2018-1199, CVE-2018-1196
  See the dependency-check report for more details.



* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 6.561 secs

report は HTML で出力される(JSON, CSV 等の形式も指定可能)。 実際のレポートはこのような感じになる → https://jeremylong.github.io/DependencyCheck/general/SampleReport.html

ant/maven のタスクも用意されている。

このブログについて

まあなんか、そんなにブログに書くこともないというか、うーん。 このブログ、Ruby で書かれているんですが、Ruby で書かれていると、変更しづらくて。。

いずれ Ruby 以外の言語で書き直したいと思ってて時々ちょいちょい書き直してるけど、リリースまでは至らずに消してる。 そんな日々です。

GraalVM の native-image で HelloWorld してみる

目的

https://medium.com/graalvm/instant-netty-startup-using-graalvm-native-image-generation-ed6f14ff7692 によると、graalvm を利用すると Java Application の起動が圧倒的に高速になるようだ。実際にどの程度高速化されるのかを Hello World Application を手元で実行してみることにより体感する。

実験

$ javac Hello.java
$ java Hello
Hello
$ time java Hello
Hello
java Hello  0.08s user 0.02s system 102% cpu 0.097 total
$ ~/Downloads/graalvm-1.0.0-rc1/Contents/Home/bin/native-image Hello
Build on Server(pid: 55301, port: 26681)*
   classlist:     918.63 ms
       (cap):   1,817.31 ms
       setup:   2,839.95 ms
  (typeflow):   3,744.88 ms
   (objects):   2,570.67 ms
  (features):      43.37 ms
    analysis:   6,469.76 ms
    universe:     281.22 ms
     (parse):   1,153.94 ms
    (inline):   1,592.75 ms
   (compile):  10,151.29 ms
     compile:  13,517.63 ms
       image:   2,241.96 ms
       write:   1,410.72 ms
     [total]:  27,753.54 ms
$ ls -lah
total 5.0M
drwxr-xr-x   5 tokuhirom staff  160 May 23 11:14 ./
drwxr-xr-x 121 tokuhirom staff 3.8K May 23 11:11 ../
-rw-r--r--   1 tokuhirom staff  401 May 23 11:13 Hello.class
-rw-r--r--   1 tokuhirom staff  111 May 23 11:13 Hello.java
-rwxr-xr-x   1 tokuhirom staff 5.0M May 23 11:14 hello*
$ time ./hello
Hello
./hello  0.00s user 0.00s system 65% cpu 0.011 total
$ file ./hello
./hello: Mach-O 64-bit executable x86_64

結論/考察

結論からいうと、Hello World レベルのプログラムでも起動が高速化されている。ファイルサイズは 5MB 程度。 この速度ならば、日常的に利用する command line application を Java で記述することも現実的といえる。

commons-math3 の二次元行列用の型についてのメモ

BlockRealMatrix

キャッシュフレンドリーで速いらしい。

Array2DRowRealMatrix

double[][] で表現されるひじょうに素直な実装

DiagonalMatrix

対角行列用の型。対角行列を扱うならコレを使うとメモリを節約できたりする。

OpenMapRealMatrix

open addressed map ベースの実装。sparse な行列ならコレを使うとよい。

初期化コストに関するベンチマーク

雑ですが。。

    @Test
    fun bench() {
        val tries = 10000000

        val startA = System.currentTimeMillis()
        for (i in 0 until tries) {
            Array2DRowRealMatrix(11, 11)
        }
        val endA = System.currentTimeMillis()
        val startB = System.currentTimeMillis()
        for (i in 0 until tries) {
            BlockRealMatrix(11, 11)
        }
        val endB = System.currentTimeMillis()
        println("A=${endA-startA} B=${endB-startB}")
    }

で、

A=6992 B=2435

みたいな感じ。

commons-math3 の BetaDistribution を利用する場合は rng をキャッシュする

commons-math3 の BetaDistribution を利用する場合、alpha/beta が変わるたびに new BetaDistribution( alpha, beta) とかしてはいけない。 new BetaDistribution(rng, alpha, beta, 1.0) は ``new BetaDistribution(new Well19937c(), alpha, beta)` ということになるからだ。

new Well19937c() は乱数生成系だが、この初期化処理は重いので、キャッシュしないと非常に処理が重くなる。

nuxtjs で bootstrap 使いたい

nuxt.js で twitter bootstrap を有効にする方法

https://bootstrap-vue.js.org/docs/#nuxt-js を参考にやれば良い。

npm i bootstrap-vue --save

して、nuxt.config.js に以下を追加。

{
  modules: [
    'bootstrap-vue/nuxt',
  ]
}

Host の canonical name を取得するワンライナー

python -c "import socket; import sys; print(socket.getaddrinfo(sys.argv[1], 0, socket.AF_INET, 0, socket.IPPROTO_TCP, socket.AI_CANONNAME)[0][3])" YOUR_HOST_NAME

spring boot2 で redis 使った session を使う方法

https://docs.spring.io/spring-session/docs/2.0.2.RELEASE/reference/html5/guides/boot-redis.html ここを参考にすると良いです

lombok 1.16.20 以後で @lombok.Value された値を Jackson で deserialize しようとするとエラーになる

@lombok.Value された bean に対して ObjectMapper で deserialize した場合、以下のようなエラーがある。

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `MyGreatResponse` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (String)"{"FOO":"BAR"}"; line: 1, column: 2]

これは lombok 1.16.20 で以下の変更が入ったためである。

https://projectlombok.org/changelog

lombok 1.16.20 で、immutable 厨したいときは lombok.config に以下のように記述すべし。

lombok.anyConstructor.addConstructorProperties= true

spring boot 2 で prometheus を actuator で export させるやり方

    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("io.micrometer:micrometer-registry-prometheus")

を build.gradle に追加

management:
  endpoints:
    web:
      exposure:
        include: info,health,prometheus

を設定に追加。

【追記】 management.endpiont.prometheus.enabled のデフォルト値は true なので指定する必要なし

Ruby の deserializer の速度比較

ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin16]

require 'msgpack'
require 'benchmark'
require 'json'

# https://github.com/fluent/fluentd/blob/master/lib/fluent/plugin/parser_ltsv.rb
class LTSV
  def self.parse(text)
    r = {}
    text.split("\t").each do |pair|
      key, value = pair.split(":", 2)
      r[key] = value
    end
    r
  end

  def self.generate(data)
    data.map {|k,v| k+":"+v }.join("\t")
  end
end

src = Hash[(1..1000).map{|i| i.to_s}.each_slice(2).to_a]

msgpk = src.to_msgpack
json = JSON.generate(src)
ltsv = LTSV.generate(src)

iterations = 100_000

puts "msgpk=#{msgpk.length} bytes"
puts "json=#{json.length} bytes"
puts "ltsv=#{ltsv.length} bytes"
puts ""

Benchmark.bm(10) do |x|
  x.report('msgpack') do
    iterations.times { MessagePack.unpack(msgpk) }
  end
  x.report('json') do
    iterations.times { JSON.parse(json) }
  end
  x.report('ltsv') do
    iterations.times { LTSV.parse(ltsv) }
  end
end
msgpk=3896 bytes
json=5894 bytes
ltsv=3892 bytes

                 user     system      total        real
msgpack     11.390000   0.050000  11.440000 ( 11.509034)
json        31.690000   0.140000  31.830000 ( 32.131092)
ltsv        36.870000   0.270000  37.140000 ( 37.986320)

そんな感じで。

Capturing grafana dashboard and post it to LINE group using LINE Notify

I want to post a screenshot of the Grafana dashboard to LINE Notify.

Grafana distribution includes PhantomJS( Alerting feature uses PhantomJS. https://github.com/grafana/grafana/blob/master/pkg/services/alerting/notifier.go#L96 ).

Then, you can take a snapshot using curl command.

You need to rewrite Grafana dashboard URL. Insert /render prefix for your dashboard URL. For example, when your dashboard URL is http://grafana.example.com/dashboard/db/my-service, PhantomJS URL is http://grafana.example.com/render/dashboard/db/my-service.

If you want to hide a header, add ?kiosk query parameter for the URL.

Grafana supports API keys and Basic Auth. With curl, you can call it like e.g. curl -u 'YOUR_EMAIL:YOUR_PASSWORD' GRAFANA_URL.

It's summarized as follows:

curl  -u 'YOUR_EMAIL:YOUR_PASSWORD' http://grafana.example.com/render/dashboard/db/my-service?kiosk -o img.png

Post to LINE Notify

You can upload dashboard image to LINE Notify using curl command.

curl -X POST https://notify-api.line.me/api/notify 
       -H 'Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN' 
       -F 'message=test' 
       -F '[email protected]'

See https://engineering.linecorp.com/ja/blog/detail/94 for more details.

SEE ALSO

http://moznion.hatenadiary.com/entry/2016/09/03/004038

rust のベンチマーク取る時は `cargo build --release` しなくてはならない

[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <[email protected]>"]

[dependencies]
futures = "0.1.14"
hyper = "0.11"

extern crate hyper;
extern crate futures;
use futures::future::Future;

use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};

struct HelloWorld;
const PHRASE: &'static str = "Hello, World!";

impl Service for HelloWorld {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;

    type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;

    fn call(&self, _req: Request) -> Self::Future {
        Box::new(futures::future::ok(
                    Response::new()
                    .with_header(ContentLength(PHRASE.len() as u64))
                    .with_body(PHRASE)
                    ))
    }
}

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();
    let server = Http::new().bind(&addr, || Ok(HelloWorld)).unwrap();
    server.run().unwrap();
}

のようなシンプルな hyper の本家サイトから取ってきたコードを実行した場合について考える。

普通に cargo build すると依存ライブラリ含めてデバッグビルドされるのでとにかく遅い。

仮想マシンのローカルから適当にベンチマーク取った場合以下のようになる。

[tokuhirom@dev2 httpd]$ wrk --latency http://localhost:3000
Running 10s test @ http://localhost:3000
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.65ms    1.03ms  21.86ms   89.72%
    Req/Sec     3.16k   718.96     4.04k    61.50%
  Latency Distribution
     50%    1.33ms
     75%    1.61ms
     90%    2.70ms
     99%    5.65ms
  63230 requests in 10.05s, 5.37MB read
Requests/sec:   6293.04
Transfer/sec:    546.95KB

cargo build --release した Release ビルドだと以下のようになり、めちゃくちゃ差が出る。

[tokuhirom@dev2 httpd]$ wrk --latency http://localhost:3000
Running 10s test @ http://localhost:3000
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   353.40us  428.95us  12.69ms   98.36%
    Req/Sec    14.56k     4.55k   20.66k    45.77%
  Latency Distribution
     50%  249.00us
     75%  393.00us
     90%  538.00us
     99%    0.94ms
  291181 requests in 10.10s, 24.71MB read
Requests/sec:  28828.72
Transfer/sec:      2.45MB

C や C++ の場合には、ライブラリコードは yum で入れたものとかを使う事が多く、そこまでアプリケーションコードが debug build かどうかに神経質にならなくてもわりとそれなりにパフォーマンスに差は出にくいが、cargo の場合、依存ライブラリも debug build してるっぽいんでめっちゃ差がでるっぽさ。

ちなみに rust の http server 実装は実質的に hyper 一択かつ、hyper は express-worker 的な機構もないので自前でthread をなんとかしない限り CPU を使い切るのは難しそう。

-Dsun.net.inetaddr.ttl=1 するよりも networkaddress.cache.ttl 使おう

jvm はデフォルトで DNS cache が 30sec かかっている。これはやや長過ぎると web service では判断されるケースが多い。 そこで、通常ではこれを短く設定して本番環境のデプロイをしている場合が多い。 手元のあるコードでは -Dsun.net.inetaddr.ttl=1 が指定されていたが、現在では -Dnetworkaddress.cache.ttl=1 を利用すべき。

https://www.glamenv-septzen.net/view/1346 このページが挙動について詳しい。 基本的に sun.net.inetaddr.ttl は歴史的経緯で指定されている名称であり、現代では networkaddress.cache.ttl を利用する方が良い。

tableau server の "Query View Image" API のデフォルトキャッシュ期間は長い

http://onlinehelp.tableau.com/v10.3/api/rest_api/en-us/help.htm#REST/rest_api_ref.htm%23Query_View_Image%3FTocPath%3DAPI%2520Reference%7C_____68

If you make multiple requests for an image, subsequent calls return a cached version of the image. This means that the returned image might not include the latest changes to the view. To decrease the amount of time that an image is cached, use tabadmin to reduce the value of the vizportal.rest_api.view_image.max_age setting. For more information, see tabadmin set options in the Tableau Server help.

で、これの default value は 720min=12hours なので、とにかくキャッシュが長い。

なんか cache 破棄させるほうほうもありそうなものだが、いまいちよくわからないので、とりあえずデフォルトのキャッシュ時間を短くして運用しましょうかというよう話になった。

homebrew との付き合い方を見直す 2017

というわけで現在利用してるのは以下。

その他、brew cask で入れているものが以下。

spring boot 2 にしたら `springBoot { executable = true }` が通らない

bootJar {
    launchScript()
}

にしましょう。

spring boot 2 にしたら InvalidConfigurationPropertyNameException が発生するとき

spring boot 2 だと @ConfigurationProperties("fooBar") みたいなのを指定することができなくなる。 @ConfigurationProperties("foo-bar") なら通るので、変更必要。

spring boot2 にしたら `bootRun { systemProperty 'spring.profiles.active', 'local' } }` が動かないよって場合

spring-boot 2 では以下の書き方は許容されなくなった。

bootRun {
  systemProperty 'spring.profiles.active', 'local'
}

以下のように変更する。

bootRun {
	execSpec {
		systemProperty 'com.example.foo', 'bar'
	}
}

https://twitter.com/ankinson/status/918498381768658944 ref. https://docs.spring.io/spring-boot/docs/2.0.0.M5/gradle-plugin/reference/html/#running-your-application

/dev/urandom を fork して読み込んでもいいんかって話

tokuhirom /dev/urandom って開いたまま fork して読み込んだらまずいんですっけ?
親 process で /dev/urandom 開いたまま、fork してそれぞれの子プロセスで読んだら同じの取れる?
kazuho 同じの取れないですね
一般論なファイルについてもそう
つまり、open file descriptionは同一なので、readしたらその中にあるfile offsetは進むので (edited)