Closed4

ターミナルで ChatGPT とお話するための #golang コード

SpiegelSpiegel

思うところあって,そろそろ真面目に ChatGPT で遊ぼうと思っている。

手始めに Go で何か書けないかなぁと公式ドキュメントを眺めてみたら,以下のパッケージが紹介されていた。

https://github.com/sashabaranov/go-openai

これを使えば OpenAI API のかなりの部分を網羅できるみたい。早速これでターミナル上でチャットっぽいやりとりをするコードを組んでみよう。こんな感じでどうだろう。

sample.go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"
    "os/signal"

    readline "github.com/nyaosorg/go-readline-ny"
    openai "github.com/sashabaranov/go-openai"
)

func main() {
    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
    defer cancel()

    client := openai.NewClient("your-api-key")
    msgs := []openai.ChatCompletionMessage{}
    editor := readline.Editor{
        Prompt: func() (int, error) { return fmt.Print("\n> ") },
    }
    for {
        text, err := editor.ReadLine(ctx)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            return
        }
        if text == "q" || text == "quit" {
            break
        }
        msgs = append(msgs, openai.ChatCompletionMessage{Role: openai.ChatMessageRoleUser, Content: text})
        resp, err := client.CreateChatCompletion(
            ctx,
            openai.ChatCompletionRequest{Model: openai.GPT3Dot5Turbo, Messages: msgs},
        )
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            return
        }
        fmt.Printf("\n\n%v\n", resp.Choices[0].Message.Content)
        msgs = append(msgs, resp.Choices[0].Message)
    }

    if err := json.NewEncoder(os.Stdout).Encode(msgs); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
}

your-api-key の部分には実際の API キーをセットしてね(API キーの取得方法は適当にググってください)。

起動すると > というプロンプトが表示されるので何か文章を入力する。上手く行けば何か返事が返ってくるはず。またプロンプトが表示されるのでそのまま対話を継続できる。

プロンプトが表示されてる状態で q または quit と入力すれば対話が終了し,それまでの内容を JSON 形式で出力する。

ちなみに入力部分は github.com/nyaosorg/go-readline-ny パッケージを使っている。同じ作者の NYAGOS でも使われているやつで,標準の text/scanner より簡単・高機能なのでオススメである。

SpiegelSpiegel

というわけで,さっそく試してみる:

$ go run sample.go

> スタックトレースをログに出力する際のセキュリティ上の問題点を教えてください。

スタックトレースには、ソフトウェアの実行中に発生した例外を追跡するために使用される詳細な情報が含まれています。 これには、メソッド呼び出しの順序、引数、戻り値、および実行場所が含まれています。 しかし、スタックトレースをログに出力する場合、セキュリティ上の問題があります。

  1. 機密情報の漏洩:スタックトレースには、クラス名、メソッド名、変数名、およびその値など、プライバシーに関する情報が含まれています。これらの情報がログに残されていると、攻撃者が情報を収集し、悪用する可能性があります。

  2. 脆弱性の暴露:スタックトレースには、アプリケーションの脆弱性が含まれている場合があり、攻撃者に攻撃するための情報を提供する可能性があります。 攻撃者がこの情報を利用して攻撃することができます。

  3. 特定のアプリケーションの攻撃:攻撃者がスタックトレースを取得することで、ターゲットの特定のアプリケーションに対して特定の攻撃を実行できます。スタックトレースは、アプリケーションに関する詳細情報を提供するため、攻撃者が分析するための情報源になります。

以上の理由から、スタックトレースをログに出力する際は、注意が必要であり、機密性、プライバシー、およびセキュリティの問題が発生しないように、ログレベルを上げたり、スタックトレースの一部の情報を非表示にする機能を使用することがあります。

> なるほど。では Java の printStackTrace メソッドを使うのはよくないのでしょうか

printStackTrace メソッドを使ってスタックトレースを出力すること自体は、悪いことではありません。しかし、そのままログに出力する場合、上記のようなセキュリティ上の問題が発生することがあります。また、スタックトレースはプログラムのデバッグに役立つ情報ですが、エンドユーザーにとっては不要な情報であることが多く、出力する必要がない場合もあります。

そのため、スタックトレースを出力する場合は、以下の提案があります。

  • ログレベルを上げて、重要度の高い例外のみスタックトレースを出力するようにします。
  • スタックトレースのスクリーニング機能を使用して、機密情報や脆弱性を含む部分を非表示にします。
  • 具体的な例外情報やエラーメッセージをログに出力するだけで、スタックトレースは出力しないようにします。
  • プロダクション環境においては、スタックトレースを完全に無効化することも検討できます。

追加で主張したいことがありましたら、おっしゃってください。

> printStackTrace メソッドは PrintWriter を引数にすることが出来ます。これを使って出力を制御するのはどうでしょうか。

はい、printStackTrace メソッドの引数に PrintWriter を指定することにより、出力先を制御することができます。そのため、ログにスタックトレースを出力する場合は、PrintWriter を使用することで、例外出力の制御を行うことができます。

次のように、PrintWriter を使用してスタックトレースをログに出力する方法があります。

try {
   // some code that may throw an exception
} catch (Exception e) {
   StringWriter sw = new StringWriter();
   PrintWriter pw = new PrintWriter(sw);
   e.printStackTrace(pw);
   logger.error(sw.toString());
}

ここで、StringWriter および PrintWriter は、スタックトレースを文字列に変換してlogger にログ出力するために使用されます。

この方法を使用することで、ログにスタックトレースを出力する際の制御性が向上し、機密情報や脆弱性を含む部分をスクリーニングすることも可能になります。ただし、スタックトレースを完全に無効化することも検討するべきであるということには変わりありません。

> ありがとうございます。これまでの回答に関するオンラインの参考文献を教えてください。

以下に、このテーマに関連するオンラインのリソースをいくつかご紹介します。

これらのリソースは、Javaでのログと例外の扱いに関する概要を把握するのに役立ちます。

> q
[{"role":"user","content":"スタックトレースを...

最後の JSON 出力は端折っている。ごめんペコン。

いや,スタックトレース情報を出力する際のセキュリティ上の問題をまとめないといけなかったのだが,文章を考えてくれるだけでも有り難いものである。あとはこれをプレゼン用に清書すればいいかな。

ちなみに最初の方で「『である』調で書いて」と指示すると一応「である」調で書いてくれるのだが,語尾だけ取ってつけたような妙な表現になるので諦めた。「である」調って苦手なのかね>ChatGPT

このスクラップは2023/03/28にクローズされました