mirror of
https://github.com/nushell/nushell.git
synced 2025-04-22 20:28:22 +02:00
# Description Update some comments and fix potential security issue: SQL Injection in DELETE statements: The code constructs SQL queries by interpolating the $key variable directly into the string. If a key contains malicious input could lead to SQL injection. Need to use parameterized queries or escaping.
210 lines
5.7 KiB
Plaintext
210 lines
5.7 KiB
Plaintext
# kv module
|
|
#
|
|
# use std-rfc/kv *
|
|
#
|
|
# Easily store and retrieve key-value pairs
|
|
# in a pipeline.
|
|
#
|
|
# A common request is to be able to assign a
|
|
# pipeline result to a variable. While it's
|
|
# not currently possible to use a "let" statement
|
|
# within a pipeline, this module provides an
|
|
# alternative. Think of each key as a variable
|
|
# that can be set and retrieved.
|
|
|
|
# Stores the pipeline value for later use
|
|
#
|
|
# If the key already exists, it is updated to the new value provided.
|
|
@example "Store the list of files in the home directory" {
|
|
ls ~ | kv set "home snapshot"
|
|
}
|
|
@example "Store a number" {
|
|
kv set foo 5
|
|
}
|
|
@example "Update a number" {
|
|
kv set foo { $in + 1 }
|
|
}
|
|
export def "kv set" [
|
|
key: string
|
|
value_or_closure?: any
|
|
--return (-r): string # Whether and what to return to the pipeline output
|
|
--universal (-u) # Store the key-value pair in a universal database
|
|
] {
|
|
# Pipeline input is preferred, but prioritize
|
|
# parameter if present. This allows $in to be
|
|
# used in the parameter if needed.
|
|
let input = $in
|
|
|
|
# If passed a closure, execute it
|
|
let arg_type = ($value_or_closure | describe)
|
|
let value = match $arg_type {
|
|
closure => { $input | do $value_or_closure }
|
|
_ => ($value_or_closure | default $input)
|
|
}
|
|
|
|
# Store values as nuons for type-integrity
|
|
let kv_pair = {
|
|
session: '' # Placeholder
|
|
key: $key
|
|
value: ($value | to nuon)
|
|
}
|
|
|
|
let db_open = (db_setup --universal=$universal)
|
|
try {
|
|
# Delete the existing key if it does exist
|
|
do $db_open | query db "DELETE FROM std_kv_store WHERE key = :key" --params { key: $key }
|
|
}
|
|
|
|
match $universal {
|
|
true => { $kv_pair | into sqlite (universal_db_path) -t std_kv_store }
|
|
false => { $kv_pair | stor insert -t std_kv_store }
|
|
}
|
|
|
|
# The value that should be returned from `kv set`
|
|
# By default, this is the input to `kv set`, even if
|
|
# overridden by a positional parameter.
|
|
# This can also be:
|
|
# input: (Default) The pipeline input to `kv set`, even if
|
|
# overridden by a positional parameter. `null` if no
|
|
# pipeline input was used.
|
|
# ---
|
|
# value: If a positional parameter was used for the value, then
|
|
# return it, otherwise return the input (whatever was set).
|
|
# If the positional was a closure, return the result of the
|
|
# closure on the pipeline input.
|
|
# ---
|
|
# all: The entire contents of the existing kv table are returned
|
|
match ($return | default 'input') {
|
|
'all' => (kv list --universal=$universal)
|
|
'a' => (kv list --universal=$universal)
|
|
'value' => $value
|
|
'v' => $value
|
|
'input' => $input
|
|
'in' => $input
|
|
'i' => $input
|
|
_ => {
|
|
error make {
|
|
msg: "Invalid --return option"
|
|
label: {
|
|
text: "Must be 'all'/'a', 'value'/'v', or 'input'/'in'/'i'"
|
|
span: (metadata $return).span
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Retrieves a stored value by key
|
|
#
|
|
# Counterpart of "kv set". Returns null if the key is not found.
|
|
@example "Retrieve a stored value" {
|
|
kv get foo
|
|
}
|
|
export def "kv get" [
|
|
key: string # Key of the kv-pair to retrieve
|
|
--universal (-u) # Whether to use the universal db
|
|
] {
|
|
let db_open = (db_setup --universal=$universal)
|
|
do $db_open
|
|
# Hack to turn a SQLiteDatabase into a table
|
|
| $in.std_kv_store | wrap temp | get temp
|
|
| where key == $key
|
|
# Should only be one occurrence of each key in the stor
|
|
| get -i value.0
|
|
| match $in {
|
|
# Key not found
|
|
null => null
|
|
# Key found
|
|
_ => { from nuon }
|
|
}
|
|
}
|
|
|
|
# List the currently stored key-value pairs
|
|
#
|
|
# Returns results as the Nushell value rather than the stored nuon.
|
|
export def "kv list" [
|
|
--universal (-u) # Whether to use the universal db
|
|
] {
|
|
let db_open = (db_setup --universal=$universal)
|
|
|
|
do $db_open | $in.std_kv_store? | each {|kv_pair|
|
|
{
|
|
key: $kv_pair.key
|
|
value: ($kv_pair.value | from nuon )
|
|
}
|
|
}
|
|
}
|
|
|
|
# Returns and removes a key-value pair
|
|
export def --env "kv drop" [
|
|
key: string # Key of the kv-pair to drop
|
|
--universal (-u) # Whether to use the universal db
|
|
] {
|
|
let db_open = (db_setup --universal=$universal)
|
|
|
|
let value = (kv get --universal=$universal $key)
|
|
|
|
try {
|
|
do $db_open
|
|
# Hack to turn a SQLiteDatabase into a table
|
|
| query db "DELETE FROM std_kv_store WHERE key = :key" --params { key: $key }
|
|
}
|
|
|
|
if $universal and ($env.NU_KV_UNIVERSALS? | default false) {
|
|
hide-env $key
|
|
}
|
|
|
|
$value
|
|
}
|
|
|
|
def universal_db_path [] {
|
|
$env.NU_UNIVERSAL_KV_PATH?
|
|
| default (
|
|
$nu.data-dir | path join "std_kv_variables.sqlite3"
|
|
)
|
|
}
|
|
|
|
def db_setup [
|
|
--universal # Whether to use the universal db
|
|
] : nothing -> closure {
|
|
try {
|
|
match $universal {
|
|
true => {
|
|
# Ensure universal sqlite db and table exists
|
|
let uuid = (random uuid)
|
|
let dummy_record = {
|
|
session: ''
|
|
key: $uuid
|
|
value: ''
|
|
}
|
|
$dummy_record | into sqlite (universal_db_path) -t std_kv_store
|
|
open (universal_db_path) | query db "DELETE FROM std_kv_store WHERE key = :key" --params { key: $uuid }
|
|
}
|
|
false => {
|
|
# Create the stor table if it doesn't exist
|
|
stor create -t std_kv_store -c {session: str, key: str, value: str} | ignore
|
|
}
|
|
}
|
|
}
|
|
|
|
# Return the correct closure for opening on-disk vs. in-memory
|
|
match $universal {
|
|
true => {{|| open (universal_db_path)}}
|
|
false => {{|| stor open}}
|
|
}
|
|
}
|
|
|
|
# This hook can be added to $env.config.hooks.pre_execution to enable
|
|
# "universal variables" similar to the Fish shell. Adding, changing, or
|
|
# removing a universal variable will immediately update the corresponding
|
|
# environment variable in all running Nushell sessions.
|
|
export def "kv universal-variable-hook" [] {
|
|
{||
|
|
kv list --universal
|
|
| transpose -dr
|
|
| load-env
|
|
|
|
$env.NU_KV_UNIVERSALS = true
|
|
}
|
|
}
|