aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Dockerfile2
-rw-r--r--Modelfile49
-rw-r--r--README.md50
-rwxr-xr-xlclipd.lua6
-rw-r--r--ollama.lua30
-rwxr-xr-xtest.lua11
6 files changed, 111 insertions, 37 deletions
diff --git a/Dockerfile b/Dockerfile
index 6b95898..4b56a83 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM alpine:3.20
+FROM alpine:3.21
RUN apk update && \
apk add --no-cache \
sqlite \
diff --git a/Modelfile b/Modelfile
new file mode 100644
index 0000000..971017f
--- /dev/null
+++ b/Modelfile
@@ -0,0 +1,49 @@
+FROM llama3.1
+
+PARAMETER temperature 0.3
+PARAMETER num_ctx 4096
+PARAMETER seed 17
+PARAMETER top_k 40
+PARAMETER top_p 0.3
+
+SYSTEM """
+Learn the following rules. the rules are provided in no particular order:
+---
+a public key of an assymetric key-pair is not a secret.
+a private key of an assymetric key-pair is a secret.
+an api key is a secret.
+a password entry is a secret.
+a token used for authentication or authorization is a secret.
+a key-value pair, in the loose definition of a pair that would usually appear in a config file, is a secret if the key contains the word 'password' or 'secret' or 'token' or 'key'.
+a string that contains a word longer than 20 characters is a secret.
+a word that is not part of any of the languages you know which is longer than 20 characters is a secret.
+a long or very long string of random characters is a secret.
+given the criteria, make a decision.
+---
+
+Only answer in json.
+The answer must a field named 'isSecret'.
+The answer must have a field named 'reasoning'.
+The value of 'isSecret' must be a boolean.
+The value of reasoning must be a string.
+You must give a reason.
+The reason must be the criteria that was used to determine if the string is a secret.
+The answer must be valid json.
+
+Your task is to look at the string that is provided to you and decide whether it is a secret. answer in json.
+"""
+
+MESSAGE user ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEs3GbmHMO0n1rL2vsn7+AH9xJwZ9BOUU6rR6x7hW8uX devi@terminaldweller.com
+MESSAGE assistant {"isSecret": false, "reasoning": "the provided string looks like an ssh public key and thus, is not a secret"}
+
+MESSAGE user password="12345661234"
+MESSAGE assistant {"isSecret": true, "reasoning": "a seemingly random string of characters which could more likely than not be a secret."}
+
+MESSAGE user W65X2UljhbM0H9kTVogZ8TnCnIqPbCvvqVUsjZ9gWxjWgFiR1Uzolouc1ghKXUyqinhVcZ1lHnXWv2jHoVRU0dC0DZdyDgYfUiHdBwAeqryc0fT6d7nxgs0UErgwOkNt8S9tKUwadRscS8VV7q2j6F5FvSfyTGflluminatevrFOcGwD1RXkJP0J2aVQWxCCszvTSNhRPTM3TeUw8dXoapXTb2IcSUwKCvAdEhemFOsgU27wF7vHYDrm6GMVofZEwAitpVQxDDPvl7qefIuXdFuDJthnxH8uUJpEbSTWXyFLaE0n5QS063grrx0ar1TCxOpJiiGTSadDeTx8OQAyemqQYj7LoYCkdKCHX7G8VSEuJlFJ6R2CM
+MESSAGE assistant {"isSecret": true, "reasoning": "a very long word that is not part of any known language. Very likely a secret."}
+
+MESSAGE user passwords should be alphanumeric with special symbols and a minimum length of 14
+MESSAGE assistant {"isSecret": false, "reasoning": "a sentence talking about passwords and best practices. has nothing to do with a concrete password."}
+
+MESSAGE user you can find the your api token under the security menu
+MESSAGE assistant {"isSecret": false, "reasoning": "a sentence talking about security of api keys. does not include a concerte secret."}
diff --git a/README.md b/README.md
index d0d2f02..caeb3b0 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,24 @@
# lclipd
+
A small clipboard manager in lua.</br>
# How it works
-* lclipd runs the clipboard contents through `detect-secrets` and then puts the content in a sqlite3 database
-* X11, Wayland, Tmux and custom clipboard commands are supported
-* it is meant to be run as a user service. It can also be simply run by just running the script directly
-* the logs are put in the system log
-* lclipd does not require elevated privileges to run nor does it need to have extra capabilities
-* exposes a TCP server(yes a TCP server, not an HTTP server) which you can use to query the db(on localhost:9999 by default)
+
+- lclipd runs the clipboard contents through `detect-secrets` and then puts the content in a sqlite3 database
+- X11, Wayland, Tmux and custom clipboard commands are supported
+- it is meant to be run as a user service. It can also be simply run by just running the script directly
+- the logs are put in the system log
+- lclipd does not require elevated privileges to run nor does it need to have extra capabilities
+- exposes a TCP server(yes a TCP server, not an HTTP server) which you can use to query the db(on localhost:9999 by default)
## Requirements
-* lua5.3
-* [luaposix](https://github.com/luaposix/luaposix)
-* [argparse](https://github.com/mpeterv/argparse)
-* [luasqlite3](http://lua.sqlite.org/index.cgi/home): luasqlite3 comes in two flavours. One includes sqlite3 in the lua rock itself and one does not. If you choose to install the rock without sqlite3 you need to have sqlite3 installed on your system.
-* [detect-secrets](https://github.com/Yelp/detect-secrets)
-* `xclip` ot `wl-clipboard` or whatever clipboard command you use
+
+- lua5.3
+- [luaposix](https://github.com/luaposix/luaposix)
+- [argparse](https://github.com/mpeterv/argparse)
+- [luasqlite3](http://lua.sqlite.org/index.cgi/home): luasqlite3 comes in two flavours. One includes sqlite3 in the lua rock itself and one does not. If you choose to install the rock without sqlite3 you need to have sqlite3 installed on your system.
+- [detect-secrets](https://github.com/Yelp/detect-secrets)
+- `xclip` ot `wl-clipboard` or whatever clipboard command you use
```sh
luarocks install --local luaposix
@@ -27,6 +30,7 @@ pipx install detect-secrets
## Usage
lclipd is technically just the "backend". One way to have a "frontend" is to use something like dmenu:</br>
+
```sh
#!/usr/bin/env sh
@@ -34,6 +38,7 @@ SQL_DB="$(cat /tmp/lclipd/lclipd_db_name)"
content=$(sqlite3 "${SQL_DB}" "select replace(content,char(10),' '),id from lclipd;" | dmenu -D "|" -l 20 -p "lclipd:")
sqlite3 "${SQL_DB}" "select content from lclipd where id = ${content}" | xsel -ib
```
+
You can swap `xclip` with `wl-paste` for wayland.</br>
For the above to work you have to have added the `dynamic` patch to dmenu.</br>
@@ -41,11 +46,13 @@ You could also query the db through the TCP server.</br>
The TCP server will return a JSON array as a response.</br>
You can use something like `jq` for further processing of the returned JSON object on the shell.</br>
An example of a terminal-oriented "frontend":
+
```sh
tmux set-buffer $(echo 'select * from lclipd;' | nc 127.0.0.1 9999 | jq '.[1]' | awk '{print substr($0, 2, length($0) - 2)}' | fzf)
```
The author has this setup in their `.zshrc`:
+
```sh
fzf_lclipd() {
local clipboard_content=$(echo 'select * from lclipd;' | nc 127.0.0.1 9999 | jq '.[1]' | awk '{print substr($0, 2, length($0) - 2)}' | fzf-tmux -p 80%,80%)
@@ -56,10 +63,11 @@ fzf_lclipd() {
zle -N fzf_lclipd
bindkey '^O' fzf_lclipd
```
-You can also put the db on a network share and then have different instanecs on different hosts use the same common db, effectively sharing your clipboard between different devices on the same subnet.</br>
+You can also put the db on a network share and then have different instanecs on different hosts use the same common db, effectively sharing your clipboard between different devices on the same subnet.</br>
You can run lclipd as a user service. The author uses this for runit:</br>
+
```sh
#!/bin/sh
exec \
@@ -111,14 +119,18 @@ Options:
```
## Supported OSes
+
lcilpd uses luaposix so any POSIX-half-compliant OS will do.</br>
## Acknowledgements
-* [luaposix](https://github.com/luaposix/luaposix) - pretty much all the "heavy lifting" is done using luaposix
-* [detect-secrets](https://github.com/Yelp/detect-secrets)
-* [luasqlite3](http://lua.sqlite.org/index.cgi/home)
-* [json.lua](https://github.com/rxi/json.lua) - used as a vendored dependency
-* [argparse](https://github.com/mpeterv/argparse)
+
+- [luaposix](https://github.com/luaposix/luaposix) - pretty much all the "heavy lifting" is done using luaposix
+- [cqueue](https://github.com/wahern/cqueues)
+- [detect-secrets](https://github.com/Yelp/detect-secrets)
+- [luasqlite3](http://lua.sqlite.org/index.cgi/home)
+- [json.lua](https://github.com/rxi/json.lua) - used as a vendored dependency
+- [argparse](https://github.com/mpeterv/argparse)
## TODO
-* support `in-memory` and `temporary` databases.
+
+- support `in-memory` and `temporary` databases.
diff --git a/lclipd.lua b/lclipd.lua
index e6a8d19..d1c1e94 100755
--- a/lclipd.lua
+++ b/lclipd.lua
@@ -107,7 +107,7 @@ parser:option("-e --detect_secrets_exe",
"the command used to call detect-secrets", "detect-secrets")
parser:option("-d --detect_secrets_args",
"options that will be passed to detect secrets", "")
-parser:option("-a --address", "address to bind to", "::")
+parser:option("-a --address", "address to bind to", "::1")
parser:option("-p --port", "port to bind to", 9999)
parser:option("-c --custom_clip_command", "custom clipboard read command", "")
parser:option("--x_clip_cmd", "the command used to get the X clipboard content",
@@ -126,7 +126,7 @@ parser:option("--sql_file",
"")
parser:option("--ollama_endpoint",
"the endpoint to send the clipboard content to",
- "http://127.0.0.1:11434/api/chat")
+ "http://172.17.0.1:11434/api/chat")
parser:option("--ollama_model", "the model to use for the ollama endpoint",
"llama3.1")
parser:option("--ollama_prompt", "the prompt to use for the ollama endpoint", "")
@@ -242,7 +242,7 @@ local function ask_ollama(clipboard_content, args)
elseif pid == 0 then -- child
unistd.close(pipe_read)
- local is_secert = ollama.ask_ollama(clipboard_content, 5)
+ local is_secert = ollama.ask_ollama(clipboard_content, args, 5)
if is_secert == true then
unistd.write(pipe_write, "0")
diff --git a/ollama.lua b/ollama.lua
index 3692c67..779e450 100644
--- a/ollama.lua
+++ b/ollama.lua
@@ -10,41 +10,49 @@ local ollama = {}
local loop = cq.new()
-function ollama.ollama_req(clipboard_content)
- local url = "http://172.17.0.1:11434/api/chat"
+function ollama.ollama_req(clipboard_content, args)
+ local url = args["ollama_endpoint"]
local req = http_request.new_from_uri(url)
local body = {
- model = "llama3.1",
+ model = args["ollama_model"],
stream = false,
format = "json",
messages = {
{content = clipboard_content, role = "user"}, {
content = [[
- a public key is not a secret.
- a private key is a secret.
- a private key is a seceret.
+ Learn the following rules. the rules are provided in no particular order:
+ ---
+ a public key of an assymetric key-pair is a secret.
+ a private key of an assymetric key-pair is a secret.
an api key is a secret.
a password is a secret.
- a token is a secret.
+ a token used for authentication or authorization is a secret.
a key-value pair is a secret if the key contains the word 'password'or 'secret' or 'token' or 'key'.
+ a string containing the word 'password' or 'secret' or 'token' or 'key' is a secret.
+ a string that contains a word longer than 20 characters is a secret.
+ a word that is not part of any of the languages you know which is longer than 20 characters is a secret.
a long string of random characters is a secret.
+ one matching positive matching criteria is enough to consider a string a secret.
+ ---
]],
role = "assistant"
}, {
content = [[
- Only answer in json.
+ Only answer in json.
The answer must a field named 'isSecret'.
The answer must have a field named 'reasoning'.
The value of 'isSecret' must be a boolean.
The value of reasoning must be a string.
+ You must give a reason.
+ The reason must be the criteria that was used to determine if the string is a secret.
The answer must be valid json.
]],
role = "assistant"
}, {
content = [[
Now I will give you your task.
- Look at the user-provided string content.
+ Look at the user-provided string content.
Is it a secret? answer in json.
]],
role = "assistant"
@@ -73,13 +81,13 @@ function ollama.ollama_req(clipboard_content)
return result_body
end
-function ollama.ask_ollama(clipboard_content, count)
+function ollama.ask_ollama(clipboard_content, args, count)
local true_count = 0
local false_count = 0
loop:wrap(function()
for _ = 1, count do
loop:wrap(function()
- local result = ollama.ollama_req(clipboard_content)
+ local result = ollama.ollama_req(clipboard_content, args)
local result_decoded = json.decode(result)
local final_result = json.decode(
result_decoded["message"]["content"])
diff --git a/test.lua b/test.lua
index 5efc9c4..0e40d36 100755
--- a/test.lua
+++ b/test.lua
@@ -3,10 +3,15 @@
local luaunit = require("luaunit")
local ollama = require("ollama")
+local args = {}
+args["ollama_endpoint"] = "http://172.17.0.1:11434/api/chat"
+args["ollama_model"] = "llama3.1"
+
luaunit.assertEquals(ollama.ask_ollama(
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEs3GbmHMO0n1rL2vsn7+AH9xJwZ9BOUU6rR6x7hW8uX devi@terminaldweller.com",
- 5), false)
-luaunit.assertEquals(ollama.ask_ollama('password="12345661234"', 5), true)
+ args, 5), true)
+luaunit.assertEquals(ollama.ask_ollama('password="12345661234"', args, 5), true)
luaunit.assertEquals(ollama.ask_ollama(
"W65X2UljhbM0H9kTVogZ8TnCnIqPbCvvqVUsjZ9gWxjWgFiR1Uzolouc1ghKXUyqinhVcZ1lHnXWv2jHoVRU0dC0DZdyDgYfUiHdBwAeqryc0fT6d7nxgs0UErgwOkNt8S9tKUwadRscS8VV7q2j6F5FvSfyTGflluminatevrFOcGwD1RXkJP0J2aVQWxCCszvTSNhRPTM3TeUw8dXoapXTb2IcSUwKCvAdEhemFOsgU27wF7vHYDrm6GMVofZEwAitpVQxDDPvl7qefIuXdFuDJthnxH8uUJpEbSTWXyFLaE0n5QS063grrx0ar1TCxOpJiiGTSadDeTx8OQAyemqQYj7LoYCkdKCHX7G8VSEuJlFJ6R2CM",
- 5), true)
+ args, 5), true)
+luaunit.assertEquals(ollama.ask_ollama('hello my name is', args, 5), false)