Re: simple prob?

2021-07-02 Thread Phi Debian
Ha ok, I got it, I was focused on our interactive session. Surely a script
running on a UID behalf would be ill-advised to interpret (in the sense of
shell evaluation) an arbitrary shell expression.

On Fri, Jul 2, 2021 at 6:06 PM Greg Wooledge  wrote:

> On Fri, Jul 02, 2021 at 05:45:23PM +0200, Phi Debian wrote:
> > Regarding the code injection I am not sure I got it.
> >
> > If you are sitting at a prompt, why would you trick
> >
> > unicorn:~$ njobs_ref 'x[0$(date>&2)]'
> >
> > when you could simply type
> > unicorn:~$ date
> >
> > I assume protected script/source (the ones you can't write into), are
> wise
> > enough not to run command based on user input, in short I guess no
> > protected script are doing thing like read in; eval $in :) that is the
> > simplest code injection :) and then would never let you have a chance to
> > enter 'x[0$(date>&2)]' at any time.
>
> For functions that you've written exclusively for personal use, it's
> not an immediate concern.  It's more of a thing that you want to be
> aware of for the future.
>
> Where it becomes important is when you're writing scripts for other
> people to use, or which run as different user accounts, or with
> different privileges.
>
> The classic example of this is a script that's run by a web server in a
> CGI environment, which accepts query parameters from the end user.  If
> one of those query parameters is used in an unsafe way, it can execute
> undesired commands on the web server.
>
> Of course, there are *many* other places that shell scripts are used,
> such as booting an operating system, starting various services, and
> so on.  In some of these cases, there is no external input being read,
> or the external inputs are "trusted" files owned and edited only by
> the system admin (root).  But in other cases, untrusted input may be
> read.
>
> So, there's merit in adopting a proactive strategy to shell script
> security.  Maintaining a slightly paranoid mindset can help you spot
> potential security holes and possibly avoid disasters.
>
>


Re: simple prob?

2021-07-02 Thread Greg Wooledge
On Fri, Jul 02, 2021 at 05:45:23PM +0200, Phi Debian wrote:
> Regarding the code injection I am not sure I got it.
> 
> If you are sitting at a prompt, why would you trick
> 
> unicorn:~$ njobs_ref 'x[0$(date>&2)]'
> 
> when you could simply type
> unicorn:~$ date
> 
> I assume protected script/source (the ones you can't write into), are wise
> enough not to run command based on user input, in short I guess no
> protected script are doing thing like read in; eval $in :) that is the
> simplest code injection :) and then would never let you have a chance to
> enter 'x[0$(date>&2)]' at any time.

For functions that you've written exclusively for personal use, it's
not an immediate concern.  It's more of a thing that you want to be
aware of for the future.

Where it becomes important is when you're writing scripts for other
people to use, or which run as different user accounts, or with
different privileges.

The classic example of this is a script that's run by a web server in a
CGI environment, which accepts query parameters from the end user.  If
one of those query parameters is used in an unsafe way, it can execute
undesired commands on the web server.

Of course, there are *many* other places that shell scripts are used,
such as booting an operating system, starting various services, and
so on.  In some of these cases, there is no external input being read,
or the external inputs are "trusted" files owned and edited only by
the system admin (root).  But in other cases, untrusted input may be
read.

So, there's merit in adopting a proactive strategy to shell script
security.  Maintaining a slightly paranoid mindset can help you spot
potential security holes and possibly avoid disasters.



Re: simple prob?

2021-07-02 Thread Phi Debian
Ha yes I lost sight of <(jobs -t), I think it is a good improvement to the
challenge :)

Regarding the local t, you right, I missed to precise that any function
that do output parameters via nameref should namespace all their locals and
check the namespace that is indeed a bit combersome, that's why I tend to
stay away from that :) yet it was asked..

Here I typed to fast (challenged by providing the shortest answer :) ) and
throwed  away my basics principles :)

Regarding the code injection I am not sure I got it.

If you are sitting at a prompt, why would you trick

unicorn:~$ njobs_ref 'x[0$(date>&2)]'

when you could simply type
unicorn:~$ date

I assume protected script/source (the ones you can't write into), are wise
enough not to run command based on user input, in short I guess no
protected script are doing thing like read in; eval $in :) that is the
simplest code injection :) and then would never let you have a chance to
enter 'x[0$(date>&2)]' at any time.

In all case since doing output parameter require some kind of name spacing
check it would reject input of the form 'x[0$(date>&2)]'

I guess a typical output parameter function should ressemble something like
this

function foo
{ [[ ! "$1" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]   &&
  echo "Invalid parameter name '$1'" >&2 &&
  return 1
  [ "${1:0:4}" = "foo_" ] && echo "Namespace collision '$1' " >&2 &&
  return 1

  typeset -n foo_out="$1"
  foo_out="value"
}

This one reject the bad 'x[0$(date>&2)]' only accept scalar variable names
as output parameter and reject scalar in the foo() namespace.



On Fri, Jul 2, 2021 at 2:01 PM Greg Wooledge  wrote:

> On Fri, Jul 02, 2021 at 09:09:34AM +0200, Phi Debian wrote:
> > PW$ function njobs
> > > { [ "$1" != "n" ] && typeset -n n="$1"
> > >typeset -a t ; readarray t <<<"$(jobs)" ;n=${#t[@]}
> > > }
>
> <<<$() is a poor imitation of < <() which is what you really want.
>
> readarray -t t < <(jobs); n=${#t[@]}
>
> Combining <<< and $() only gives an approximation of the same result
> (the command substitution strips off all trailing newline characters,
> and then the here-string syntax appends one newline), and it's less
> efficient, because it involves slurping the entire input into memory,
> then writing it out to a temporary file, then opening the temporary
> file for input and unlinking it, then reading it back in a second time.
>
> Using < <() avoids the newline alterations and the temporary file.
>
> (Note: in some sufficiently new versions of bash, there may not be a
> temporary file in some cases.  But it's still not the best solution,
> as it still involves storing and reading the whole output multiple times.)
>
>
> Stepping back a moment, you're using the name reference version of the
> "pass an output variable by reference" strategy.  This requires bash 4.3,
> which is reasonable, and it requires some additional sanity checks which
> you did not show.
>
> What's really interesting to me is that you did a *partial* sanity check,
> refusing to create a circular name reference if the user passed "n"
> as their output variable name.  But you forgot to check for "t", which is
> another local variable you're using.  Also, in Linda's original example,
> the output variable was literally named "n", so choosing that as your
> name reference and explicitly disallowing it is a really spiteful choice.
>
> Finally, you didn't do any sanity checking of the output variable name
> (beyond comparing it to one of your two local variable names), so your
> function is susceptible to the same code injection attacks we discussed
> earlier in the thread.
>
> unicorn:~$ njobs_ref() { typeset -n n="$1"; n=42; }
> unicorn:~$ njobs_ref 'x[0$(date>&2)]'
> Fri Jul  2 07:54:49 EDT 2021
>
> As I mentioned a few days ago, all variants of the "pass a variable name
> by reference" method are basically equivalent to each other, and all
> of them need input sanity checking in order to avoid code injections.
> (Some of the variants avoid *some* flavors of code injection, but none of
> them avoid this one.)
>
>


Re: simple prob?

2021-07-02 Thread Greg Wooledge
On Fri, Jul 02, 2021 at 09:09:34AM +0200, Phi Debian wrote:
> PW$ function njobs
> > { [ "$1" != "n" ] && typeset -n n="$1"
> >typeset -a t ; readarray t <<<"$(jobs)" ;n=${#t[@]}
> > }

<<<$() is a poor imitation of < <() which is what you really want.

readarray -t t < <(jobs); n=${#t[@]}

Combining <<< and $() only gives an approximation of the same result
(the command substitution strips off all trailing newline characters,
and then the here-string syntax appends one newline), and it's less
efficient, because it involves slurping the entire input into memory,
then writing it out to a temporary file, then opening the temporary
file for input and unlinking it, then reading it back in a second time.

Using < <() avoids the newline alterations and the temporary file.

(Note: in some sufficiently new versions of bash, there may not be a
temporary file in some cases.  But it's still not the best solution,
as it still involves storing and reading the whole output multiple times.)


Stepping back a moment, you're using the name reference version of the
"pass an output variable by reference" strategy.  This requires bash 4.3,
which is reasonable, and it requires some additional sanity checks which
you did not show.

What's really interesting to me is that you did a *partial* sanity check,
refusing to create a circular name reference if the user passed "n"
as their output variable name.  But you forgot to check for "t", which is
another local variable you're using.  Also, in Linda's original example,
the output variable was literally named "n", so choosing that as your
name reference and explicitly disallowing it is a really spiteful choice.

Finally, you didn't do any sanity checking of the output variable name
(beyond comparing it to one of your two local variable names), so your
function is susceptible to the same code injection attacks we discussed
earlier in the thread.

unicorn:~$ njobs_ref() { typeset -n n="$1"; n=42; }
unicorn:~$ njobs_ref 'x[0$(date>&2)]'
Fri Jul  2 07:54:49 EDT 2021

As I mentioned a few days ago, all variants of the "pass a variable name
by reference" method are basically equivalent to each other, and all
of them need input sanity checking in order to avoid code injections.
(Some of the variants avoid *some* flavors of code injection, but none of
them avoid this one.)



Re: simple prob?

2021-07-02 Thread Phi Debian
On Fri, Jul 2, 2021 at 11:15 AM Alex fxmbsw7 Ratchev 
wrote:

> good debugging, yea i missed the n # count char, i type stupidly from a
> cell foun, ..
> and yea leftover arr ekements exceots when u just use the first one as n
> i just wanted to show shorter code
>

Yes I like those little one liners when other goes long way with multi
fork/exec etc  :), just fun challenges :)


Re: simple prob?

2021-07-02 Thread Alex fxmbsw7 Ratchev
good debugging, yea i missed the n # count char, i type stupidly from a
cell foun, ..
and yea leftover arr ekements exceots when u just use the first one as n
i just wanted to show shorter code

i also made somewhen a which_pid_exited resolver etc

peace

On Fri, Jul 2, 2021, 11:10 Phi Debian  wrote:

>
>
> On Fri, Jul 2, 2021 at 9:24 AM Alex fxmbsw7 Ratchev 
> wrote:
>
>> jobsn=( $( jobs -p ) ) jobsn=${jobsn[@]}
>>
>
> This give
> PW$ jobsn=( $( jobs -p ) ) jobsn=${jobsn[@]}
> PW$ echo $jobsn
> 3644 3645 3646 3647
>
> I guess you meant  jobsn=${#jobsn[@]}
>  ^ You missed the '#'
>
> Yet there are some left over
> PW$ jobsn=( $( jobs -p ) ) jobsn=${#jobsn[@]}
> PW$ echo ${jobsn[@]}
> 4 3645 3646 3647
>
> So it is not clean, and the OP want a function with a named output arg.
>
> Yet to elaborate on your technic may be this one is a little cleaner
>
> PW$ jobsn=$(printf '%c' $(jobs -p)) jobsn=${#jobsn} ; echo $jobsn
> 4
>


Re: simple prob?

2021-07-02 Thread Phi Debian
On Fri, Jul 2, 2021 at 9:24 AM Alex fxmbsw7 Ratchev 
wrote:

> jobsn=( $( jobs -p ) ) jobsn=${jobsn[@]}
>

This give
PW$ jobsn=( $( jobs -p ) ) jobsn=${jobsn[@]}
PW$ echo $jobsn
3644 3645 3646 3647

I guess you meant  jobsn=${#jobsn[@]}
 ^ You missed the '#'

Yet there are some left over
PW$ jobsn=( $( jobs -p ) ) jobsn=${#jobsn[@]}
PW$ echo ${jobsn[@]}
4 3645 3646 3647

So it is not clean, and the OP want a function with a named output arg.

Yet to elaborate on your technic may be this one is a little cleaner

PW$ jobsn=$(printf '%c' $(jobs -p)) jobsn=${#jobsn} ; echo $jobsn
4


Re: simple prob?

2021-07-02 Thread Alex fxmbsw7 Ratchev
jobsn=( $( jobs -p ) ) jobsn=${jobsn[@]}

On Fri, Jul 2, 2021, 09:10 Phi Debian  wrote:

> On Tue, Jun 29, 2021 at 10:23 PM L A Walsh  wrote:
>
> > I hope a basic question isn't too offtopic.
> > Say I have some number of jobs running:
> >
> > >  jobs|wc -l
> > 3
> > ---
> >
> > Would like to pass a varname to njobs to store the answer in, like:
> >
> > So I can run:
> >
> > >  njobs n
> > echo "$n"
> > 3
> >
> >
> This a a double question 'how to', and I see no bash bugs here.
>
> The 2 questions are
> - how do I pass a variable name as an output argument to a function ('n' in
> your 'jobs n' example)
> - how to set a variable in a sub command? that is no doable, a sub command
> can't return variable to its parent, so you obviously have to do things
> diffrently.
>
> A simple 2 liners, solve all this, with no code injection blah
>
>
> PW$ jobs
> [1]   Running sleep 111 &
> [2]   Running sleep 111 &
> [3]-  Running sleep 111 &
> [4]+  Running sleep 111 &
>
> PW$
>
> PW$ function njobs
> > { [ "$1" != "n" ] && typeset -n n="$1"
> >typeset -a t ; readarray t <<<"$(jobs)" ;n=${#t[@]}
> > }
>
> PW$ njobs n ; echo $n
> 4
>
> # explanations (you may skip here)
> #===
> [ "$1" != "n" ] && typeset -n n="$1"
> This make sure the given output variable name is a valid SHELL identifier,
> providing anything not valid in "$1" will break there.
> This also enforce that the given $1 output variable name doesn't match our
> own local nameref name, if it match we don't do our nameref, and re-use the
> upper scope output variable name, that by definition is a valid variable
> name if we got that far.
>
> typeset -a t
> define a local array that we will fill, being local mean the booking is
> done at function return
>
> readarray t <<<"$(jobs)" ;
> Fill the array with your command you want to count lines for.
>
> n=${#t[@]}
> Fill the output variable
>
> All is safe, all is clean, no 'apparent' temp file, no sub command :)
>
> Shell programing is fun :)
>


Re: simple prob?

2021-07-02 Thread Phi Debian
On Tue, Jun 29, 2021 at 10:23 PM L A Walsh  wrote:

> I hope a basic question isn't too offtopic.
> Say I have some number of jobs running:
>
> >  jobs|wc -l
> 3
> ---
>
> Would like to pass a varname to njobs to store the answer in, like:
>
> So I can run:
>
> >  njobs n
> echo "$n"
> 3
>
>
This a a double question 'how to', and I see no bash bugs here.

The 2 questions are
- how do I pass a variable name as an output argument to a function ('n' in
your 'jobs n' example)
- how to set a variable in a sub command? that is no doable, a sub command
can't return variable to its parent, so you obviously have to do things
diffrently.

A simple 2 liners, solve all this, with no code injection blah


PW$ jobs
[1]   Running sleep 111 &
[2]   Running sleep 111 &
[3]-  Running sleep 111 &
[4]+  Running sleep 111 &

PW$

PW$ function njobs
> { [ "$1" != "n" ] && typeset -n n="$1"
>typeset -a t ; readarray t <<<"$(jobs)" ;n=${#t[@]}
> }

PW$ njobs n ; echo $n
4

# explanations (you may skip here)
#===
[ "$1" != "n" ] && typeset -n n="$1"
This make sure the given output variable name is a valid SHELL identifier,
providing anything not valid in "$1" will break there.
This also enforce that the given $1 output variable name doesn't match our
own local nameref name, if it match we don't do our nameref, and re-use the
upper scope output variable name, that by definition is a valid variable
name if we got that far.

typeset -a t
define a local array that we will fill, being local mean the booking is
done at function return

readarray t <<<"$(jobs)" ;
Fill the array with your command you want to count lines for.

n=${#t[@]}
Fill the output variable

All is safe, all is clean, no 'apparent' temp file, no sub command :)

Shell programing is fun :)


Re: simple prob made into a security tragedy...oh well.... ;^/

2021-07-01 Thread Greg Wooledge
On Thu, Jul 01, 2021 at 02:12:10AM -0700, L A Walsh wrote:
> What are you talking about?
> 
>  njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }
> 
> I don't see any arrays, let alone indexed.

The arrays were in the calls.  We demonstrated a few cases already.
Since you clearly weren't paying attention, here they are again.

First, the classic code injection:

unicorn:~$ njobs 'x[0$(date>&2)]'
Thu Jul  1 07:12:35 EDT 2021

Second, a user who wants to (legitimately) use an existing array, with
a space in the key:

unicorn:~$ declare -A jobs
unicorn:~$ njobs 'jobs[first time]'
bash: printf: `jobs[first': not a valid identifier

Third, a user who wants to (legitimately) use an existing array, where
the indexed reference is accidentally treated as a glob:

unicorn:~$ touch jobsx
unicorn:~$ njobs 'jobs[sixth]'
unicorn:~$ declare -p jobs jobsx
declare -A jobs
declare -- jobsx="0
"

Fourth, a user who accidentally or maliciously passes additional printf
options:

unicorn:~$ njobs 'foo -v bar'
unicorn:~$ declare -p foo bar
bash: declare: foo: not found
declare -- bar="0
"

Quoting "$1" in your code would stop three of these four examples from
doing the wrong thing.  It makes no difference in the first case.


Looking at the Subject: header reminds me of:

  43. A "quick" or "simple" question will be neither.

What you perceive as a "simple prob" (and for the love of glob, can you at
least spell out "problem" correctly?!) is absolutely NOT simple.  It's the
tip of an exploding carcinogenic iceberg full of frozen soul-eating
demons.  Everyone knows about this iceberg.  There are buoys all around
it warning sailors to stay away.  All the navigational maps have marked
this territory as too dangerous.

Despite this, you've decided it would be a great idea to have a little
picnic on top of it.

And people don't understand why we're so cranky.



Re: simple prob made into a security tragedy...oh well.... ;^/

2021-07-01 Thread L A Walsh

On 2021/06/29 19:11, Eli Schwartz wrote:


This is a ridiculous argument and you know it. You, personally, are
writing code which does not get used in security contexts, which is your
right. This in no way means that refusing to quote variables which
"cannot be word-split" stops *any* security errors. The "illegal input"
was not related to the security bypass (as Greg points out, removing the
space prevents word splitting and executes the same security bypass code).

Your response should have been:
  


More likely "is", if I needed security I wouldn't likely write in
a script language, but more like with audit, w/Biba integrity and
Bell-LaPadula sensitivity models that we planned to port to linux, I'd have
written it in 'C'.
Trix or Trusted IRIX was certified, for C2+ under the then, orange
book standard.  Even had a 128-bit luid, which later implementers
changed to a less parallel 'loginuid', mainly for auditing.

I'd been presenting sgi's security plan at the linux security conference in
France, as well as some presentation in London. It seems I was good at
explaining what had been a confusing security model in the place of
my then manager.  I wasn't good at politics, but my manager prided himself
on his bookshelf copy of Machiavelli's, 'The Prince' as having everything
a manager needed to know...  among other things, for him to be able to
put a sensitivity+integrity Policy, 'SMACK' in the linux kernel.


Instead you are arguing in bad faith... 

---
   You are arguing about a 1-liner that took unfiltered output
from locate to search for keywords.  You wanna work that up into
bad faith, good luck.

your code is flawed, it doesn't
correctly handle indexed arrays with spaces in the key and doesn't
forbid them either.
  

What are you talking about?

 njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }

I don't see any arrays, let alone indexed.



  

This won't protect against all code injections, of course;

---
   It does in the target environment.  The key is to look at the
security policy requirements and environment before going off and making
assumptions about "faith" that might bounce back when used for
design issues relating to a 1-line search expression.




Re: simple prob?

2021-06-30 Thread Chet Ramey

On 6/29/21 5:05 PM, L A Walsh wrote:


So...hmmm...how is it that jobs picks up the right answer in what
would seem to be a subshell?  Special cased?


Yes.

--
``The lyf so short, the craft so long to lerne.'' - Chaucer
 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRUc...@case.eduhttp://tiswww.cwru.edu/~chet/



Re: simple prob?

2021-06-30 Thread L A Walsh

On 2021/06/29 16:51, Greg Wooledge wrote:

On Tue, Jun 29, 2021 at 04:29:05PM -0700, L A Walsh wrote:
  

njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }



  

   Which is detected as "illegal input" and disallowed.  If you don't enable
some security errors, they can't be as easily introduced.



Are you *still* insisting that your failure to quote is a SECURITY
FEATURE?

Come *on*!
  


   In this case, not quoting was deliberately intended as variable
names wouldn't need it.  Any security consideration was purely
secondary.  I'm an avid quoter where it is needed, but I no longer
quote for the sake of quoting as I once did.  In a similar manner
I try to not overuse parentheses, just for the sake of it.

   As I stated before, my scripts are most often for myself.  If I needed
security, I'd probably write in a compiled language rather than a
scripting one.

unicorn:~$ njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }
unicorn:~$ njobs 'x[0$(date>&2)]'
Tue Jun 29 19:49:16 EDT 2021

All I had to do was remove the space.  You're not even trying.

Your failure to quote is simply a failure.  If you want to prevent
code injection attacks, you need to sanity-check the input.

There is no other way.

  




Re: simple prob?

2021-06-29 Thread Greg Wooledge
On Tue, Jun 29, 2021 at 10:11:31PM -0400, Eli Schwartz wrote:
> On 6/29/21 7:29 PM, L A Walsh wrote:
> >>> njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }

> It also rejects this perfectly reasonable code, which I arbitrarily
> decided not to support because if you want this you're weird:
> 
> $ declare -A jobs
> $ njobs 'jobs[first run]'
> error: invalid characters [ ] found in var 'jobs[first run]'
> hacker detected...

It's not the weirdest thing I've seen.  Without your validation:

unicorn:~$ declare -A jobs
unicorn:~$ njobs 'jobs[first]'

All cool, right?  Uh

unicorn:~$ touch jobss
unicorn:~$ njobs 'jobs[second]'
unicorn:~$ declare -p jobs
declare -A jobs=([first]=$'0\n' )
unicorn:~$ declare -p jobss
declare -- jobss="0
"

Failure to quote strikes again.  What if we fix it?

unicorn:~$ nqjobs() { printf ${1:+-v "$1"} "%s\n" "$(jobs |wc -l)"; }
unicorn:~$ nqjobs 'jobs[third]'
unicorn:~$ nqjobs 'jobs[sixth]'
unicorn:~$ declare -p jobs
declare -A jobs=([sixth]=$'0\n' [first]=$'0\n' [third]=$'0\n' )

Now it works even if there's a file which matches the glob pattern.

> Well, the diagnostic jumped to a conclusion that is probably false.

Your validation is too strict in this case.  Right idea, wrong
implementation.  Of course, one might argue that allowing an array with
an index is a dangerous precedent, and stick with your strict validation
on principle.  That's a design decision, and I could see either one
being correct depending on the goals.

Oh, and by the way, there's at least one more category of errors that
failure-to-quote is susceptible to:

unicorn:~$ unset foo bar
unicorn:~$ njobs 'foo -v bar'
unicorn:~$ declare -p foo bar
bash: declare: foo: not found
declare -- bar="0
"

Without quotes, we get printf -v foo -v bar '%s\n' "$(jobs|wc -l)"
with the result shown.

With quotes, we get:

unicorn:~$ nqjobs 'foo -v bar'
bash: printf: `foo -v bar': not a valid identifier

It's not robust against code injection attacks (we need actual validation
for that), but at least it gives a sensible error message in some common
cases.

tl;dr: When in doubt, quote it.  There is a REASON we beat this into
people's heads.



Re: simple prob?

2021-06-29 Thread Eli Schwartz
On 6/29/21 7:29 PM, L A Walsh wrote:
> On 2021/06/29 15:49, Greg Wooledge wrote:
>> On Tue, Jun 29, 2021 at 02:58:28PM -0700, L A Walsh wrote:
>>  
>>> njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }
>>>
>>> Using that with your input:
>>>
>>> njobs 'x[0$(date >&2)]'
>>>
>>> bash: printf: `x[0$(date': not a valid identifier
>>>     
>>
>> This is because you didn't quote "$1".
> 
>    $1 should never be quoted -- it is an identifier, and as such
> cannot contain a space.  By quoting it, you are allowing inputs that
> would otherwise be filtered out because they are not valid variable
> names.


You do no filtering right now.

Here's how you filter:

njobs() {
if [[ $(id -un) = lindawalsh ]]; then
# Linda agrees to never misuse this function or let
# anyone else run code on her computer which does
:
elif [[ $1 = *[^a-zA-Z0-9_]* ]]; then
echo "error: invalid characters ${1//[a-zA-Z0-9_]} found in var
${1@Q}" >&2
echo "hacker detected..." >&2
# can be exit 13 if used in a script
return 1
fi

printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)";
}


Now, since I am not Linda and I am a big dummy when it comes to code
reuse and I *am* concerned I might stupidly use this code on input
provided by my friend the huge practical joker, I get this:

$ njobs 'x[0$(date >&2)]'
error: invalid characters [$( >&)] found in var 'x[0$(date >&2)]'
hacker detected...

Of course, I don't bother to quote $1 -- I cherish my right to not quote
that variable. But I know it's safe to do so -- if it has word splitting
tokens, they will raise an error.

Greg's fixed example is still caught by my code (unless the code is
running as the Linda login account):

$ njobs 'x[0$(date>&2)]'
error: invalid characters [$(>&)] found in var 'x[0$(date>&2)]'
hacker detected...

It also rejects this perfectly reasonable code, which I arbitrarily
decided not to support because if you want this you're weird:

$ declare -A jobs
$ njobs 'jobs[first run]'
error: invalid characters [ ] found in var 'jobs[first run]'
hacker detected...


Well, the diagnostic jumped to a conclusion that is probably false.


>>   Since you only ever tested
>> the cases where $1 was a valid variable name
> 
>    It is only designed to work with $1 being an optional, valid variable
> name.
> Anything else should fail.  There are times when putting quotes around a
> var will enable problems.
>> , you never ran into that particular result... until now.
>>   
> 
>    I never ran into "invalid input" because I didn't program it to
> accept anything other than a variable name there.


You did program it to accept anything other than a variable name there.
You then made a "gentleman's agreement" to not *use* anything other than
a variable name there.

But "use" is a different word than "accept".


>> As you can see, the unquoted $1 underwent word splitting, so you're
>> effectively running printf -v 'x[0$(date' '>&2)]' '%s\n' "...".
>>   
> 
>    Which is detected as "illegal input" and disallowed.  If you don't
> enable
> some security errors, they can't be as easily introduced.  I assert that
> you
> should never put quotes around something that is supposed to be a
> variable name
> since valid variable names can not be word-split.  Many of the items on
> your
> website about bash cautions, are because bash disallows some sketchy
> constructs.
> That's not a bash-caveat, but a bash feature!


This is a ridiculous argument and you know it. You, personally, are
writing code which does not get used in security contexts, which is your
right. This in no way means that refusing to quote variables which
"cannot be word-split" stops *any* security errors. The "illegal input"
was not related to the security bypass (as Greg points out, removing the
space prevents word splitting and executes the same security bypass code).

Your response should have been:

"""
I assert that you should never put quotes around something that is
supposed to be a variable name since valid variable names can not be
word-split and quoting them implies you would like to handle them as
variable names anyway.

Quotes aren't a security feature. I don't care about security features,
because I don't misuse the code and try to hack my own system. But if
you do want security features, you should detect anything that isn't a
valid variable name and raise an error, then go ahead and use the
variables unquoted.
"""

And hey, I'd even agree with you on that. But I'm also an overly
restrictive person who grumps at people trying to set:

declare -A jobs=(["first run"]=$'0\n' )

Instead you are arguing in bad faith that "That's not a bash-caveat, but
a bash feature!" When strictly speaking your code is flawed, it doesn't
correctly handle indexed arrays with spaces in the key and doesn't
forbid them either.


>> This won't protect against all code injections, of course; only the
>> ones that contain a whitespace character.
>>   
> 
>    

Re: simple prob?

2021-06-29 Thread Greg Wooledge
On Tue, Jun 29, 2021 at 04:29:05PM -0700, L A Walsh wrote:
> > > njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }

>Which is detected as "illegal input" and disallowed.  If you don't enable
> some security errors, they can't be as easily introduced.

Are you *still* insisting that your failure to quote is a SECURITY
FEATURE?

Come *on*!

unicorn:~$ njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }
unicorn:~$ njobs 'x[0$(date>&2)]'
Tue Jun 29 19:49:16 EDT 2021

All I had to do was remove the space.  You're not even trying.

Your failure to quote is simply a failure.  If you want to prevent
code injection attacks, you need to sanity-check the input.

There is no other way.



Re: simple prob?

2021-06-29 Thread L A Walsh

On 2021/06/29 15:49, Greg Wooledge wrote:

On Tue, Jun 29, 2021 at 02:58:28PM -0700, L A Walsh wrote:
  

njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }

Using that with your input:

njobs 'x[0$(date >&2)]'

bash: printf: `x[0$(date': not a valid identifier



This is because you didn't quote "$1".


   $1 should never be quoted -- it is an identifier, and as such
cannot contain a space.  By quoting it, you are allowing inputs that
would otherwise be filtered out because they are not valid variable
names.


  Since you only ever tested
the cases where $1 was a valid variable name


   It is only designed to work with $1 being an optional, valid 
variable name.

Anything else should fail.  There are times when putting quotes around a
var will enable problems.

, you never ran into that particular result... until now.
  


   I never ran into "invalid input" because I didn't program it to
accept anything other than a variable name there.


As you can see, the unquoted $1 underwent word splitting, so you're
effectively running printf -v 'x[0$(date' '>&2)]' '%s\n' "...".
  


   Which is detected as "illegal input" and disallowed.  If you don't 
enable

some security errors, they can't be as easily introduced.  I assert that you
should never put quotes around something that is supposed to be a 
variable name

since valid variable names can not be word-split.  Many of the items on your
website about bash cautions, are because bash disallows some sketchy 
constructs.

That's not a bash-caveat, but a bash feature!



This won't protect against all code injections, of course; only the
ones that contain a whitespace character.
  


   Nothing protect against "all" 'X' or "everything", especially when 
talking
about security.  Security is best handled as a layered approach with 
different

layers protecting against different things.  There's no such thing as a
'Security Magic Bullet'.  Just because NOTHING protects against "ALL" 
[anything] is
not a reason to take extreme action. In fact realization that NOTHING is 
perfect
is one of the better defenses against over-reacting in response to 
supposed security

flaws.







Re: simple prob?

2021-06-29 Thread Greg Wooledge
On Tue, Jun 29, 2021 at 02:58:28PM -0700, L A Walsh wrote:
> njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }
> 
> Using that with your input:
> 
> njobs 'x[0$(date >&2)]'
> 
> bash: printf: `x[0$(date': not a valid identifier

This is because you didn't quote "$1".  Since you only ever tested
the cases where $1 was a valid variable name, you never ran into that
particular result... until now.

As you can see, the unquoted $1 underwent word splitting, so you're
effectively running printf -v 'x[0$(date' '>&2)]' '%s\n' "...".

This won't protect against all code injections, of course; only the
ones that contain a whitespace character.



Re: simple prob?

2021-06-29 Thread L A Walsh

On 2021/06/29 14:02, Greg Wooledge wrote:

declare, printf -v, local -n, eval -- they're mostly equivalent. Some
of them may prevent *some* possible code injections, but none of them
prevent *all* possible code injections.

unicorn:~$ njobs2() { printf -v "$1" %s 42; }
unicorn:~$ njobs2 'x[0$(date >&2)]'
Tue Jun 29 17:00:29 EDT 2021

  

That's not what I see in my version:

njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }

Using that with your input:

njobs 'x[0$(date >&2)]'

bash: printf: `x[0$(date': not a valid identifier

Perhaps some solutions provide more resistance to problems than
others.

FWIW, I would be using 'njobs' in a script where I'm giving it
the input. 


No matter which one of these you choose, you still have to sanity-check
the input.  Or else declare that you do not care if the user shoots their
own foot off 

The user has no access to internal functions inside a script. Though I
do take many precautions against my future self(ves).






Re: simple prob?

2021-06-29 Thread Greg Wooledge
On Tue, Jun 29, 2021 at 02:05:46PM -0700, L A Walsh wrote:
>That would be 'me', so I'm going to rule out malicious
> code injection! :-), but that's also why I used printf to
> write the output, the worst that could happen is some varname
> is overwritten with the answer, no?

Sadly, this is not correct.  You can still get code injections with
printf -v.

unicorn:~$ njobs2() { printf -v "$1" %s 42; }
unicorn:~$ njobs2 'x[0$(date >&2)]'
Tue Jun 29 17:00:29 EDT 2021

> Simpler -- don't use a variable:
> 
>   njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }

You're adding a newline to the variable.  That may not be what you want.

> So...hmmm...how is it that jobs picks up the right answer in what
> would seem to be a subshell?  Special cased?

The subshell inherits the job list from its parent -- it just can't
*wait* for any of those jobs, since it isn't their parent.  But it can
still count them.



Re: simple prob?

2021-06-29 Thread Kerin Millar
On Tue, 29 Jun 2021 17:02:16 -0400
Greg Wooledge  wrote:

> On Tue, Jun 29, 2021 at 09:47:30PM +0100, Kerin Millar wrote:
> > On Tue, 29 Jun 2021 16:35:28 -0400
> > Greg Wooledge  wrote:
> > 
> > > unicorn:~$ njobs() { local _n=$(jobs | wc -l); eval "$1=\$_n"; }
> > > unicorn:~$ njobs walsh
> > > unicorn:~$ echo "$walsh"
> > > 3
> > > 
> > > Now you just need to add sanity-checking on the argument of njobs, to
> > > avoid whatever code injection the malicious caller wants to perform.
> > 
> > I can't fathom the switch to eval there. Why not printf -v "$1" %s "$_n", 
> > for example? It even rejects invalid identifiers.
> 
> declare, printf -v, local -n, eval -- they're mostly equivalent.  Some
> of them may prevent *some* possible code injections, but none of them
> prevent *all* possible code injections.
> 
> unicorn:~$ njobs2() { printf -v "$1" %s 42; }
> unicorn:~$ njobs2 'x[0$(date >&2)]'
> Tue Jun 29 17:00:29 EDT 2021
> 
> No matter which one of these you choose, you still have to sanity-check
> the input.  Or else declare that you do not care if the user shoots their
> own foot off (which is a valid stance as long as your code is never used
> in a context where the user can elevate their privileges/capabilites).
> 

I see. Thanks.

-- 
Kerin Millar



Re: simple prob?

2021-06-29 Thread L A Walsh

On 2021/06/29 13:35, Greg Wooledge wrote:

unicorn:~$ njobs() { local _n=$(jobs | wc -l); eval "$1=\$_n"; }

---
   ARG...I thought about that and rejected it because I
thought the "jobs|wc-l" would be in a sub-shell and not pickup
the background jobs!  Double arg, this works as well:
sjobs() { local j;read j< <(jobs|wc -l);  
printf ${1:+-v $1} "%s\n" "$j"; }


About the only thing that doesn't work are variations on
jobs|wc-l|read n -- since if read is in current process jobs doesn't
pick up the children, but if jobs is in current process, then read
isn't, and 'n' is lost.

Of course I was focusing on -s/-u lastpipe to provide some variation
but that was the only variation that would seem to be guaranteed not
to work.  Oi!

Now you just need to add sanity-checking on the argument of njobs, to
avoid whatever code injection the malicious caller wants to perform.
  


   That would be 'me', so I'm going to rule out malicious
code injection! :-), but that's also why I used printf to
write the output, the worst that could happen is some varname
is overwritten with the answer, no?


And maybe consider adding "make sure $1 isn't _n" to the list of your
sanity checks, since the caller can't be permitted to use that particular
variable namhe.
  


Simpler -- don't use a variable:

  njobs() { printf ${1:+-v $1} "%s\n" "$(jobs |wc -l)"; }

---
The use of the builtin expansion off of the parameter has the side
benefit of printing the answer to output if no var is given.

So...hmmm...how is it that jobs picks up the right answer in what
would seem to be a subshell?  Special cased?




unicorn:~$ njobs _n
unicorn:~$ echo "$_n"


(At least it didn't go into an infinite loop!)

  




Re: simple prob?

2021-06-29 Thread L A Walsh

On 2021/06/29 13:35, Eli Schwartz wrote:

Well, if you don't think this is a bug in bash, but something you need
help figuring out, maybe you'd prefer to use the "help-bash" list?
  


   Actually

  - 
The original message was received at Tue, 29 Jun 2021 13:06:34 -0700

The following addresses had permanent fatal errors -

   (reason: 550-Callout verification failed:)
---
I had it backwardsoh well.  I did try there first!





Re: simple prob?

2021-06-29 Thread Greg Wooledge
On Tue, Jun 29, 2021 at 09:47:30PM +0100, Kerin Millar wrote:
> On Tue, 29 Jun 2021 16:35:28 -0400
> Greg Wooledge  wrote:
> 
> > unicorn:~$ njobs() { local _n=$(jobs | wc -l); eval "$1=\$_n"; }
> > unicorn:~$ njobs walsh
> > unicorn:~$ echo "$walsh"
> > 3
> > 
> > Now you just need to add sanity-checking on the argument of njobs, to
> > avoid whatever code injection the malicious caller wants to perform.
> 
> I can't fathom the switch to eval there. Why not printf -v "$1" %s "$_n", for 
> example? It even rejects invalid identifiers.

declare, printf -v, local -n, eval -- they're mostly equivalent.  Some
of them may prevent *some* possible code injections, but none of them
prevent *all* possible code injections.

unicorn:~$ njobs2() { printf -v "$1" %s 42; }
unicorn:~$ njobs2 'x[0$(date >&2)]'
Tue Jun 29 17:00:29 EDT 2021

No matter which one of these you choose, you still have to sanity-check
the input.  Or else declare that you do not care if the user shoots their
own foot off (which is a valid stance as long as your code is never used
in a context where the user can elevate their privileges/capabilites).



Re: simple prob?

2021-06-29 Thread Kerin Millar
On Tue, 29 Jun 2021 16:35:28 -0400
Greg Wooledge  wrote:

> On Tue, Jun 29, 2021 at 01:21:44PM -0700, L A Walsh wrote:
> > I hope a basic question isn't too offtopic.
> 
> More of a help-bash question, in my opinion, but close enough.
> 
> > Say I have some number of jobs running:
> > 
> > >  jobs|wc -l
> > 3
> 
> OK.
> 
> > Would like to pass a varname to njobs to store the answer in, like:
> > >  njobs n
> > echo "$n"
> > 3
> 
> "How do I pass a value back from a function to its caller, you know,
> like a function in any other programming language can do" is one of the
> holy grails of shell programming.  There is no sane answer.  You appear
> to be going down the "pass a variable name by reference" path, so:
> 
> unicorn:~$ jobs | wc -l
> 3
> unicorn:~$ njobs() { local _n=$(jobs | wc -l); eval "$1=\$_n"; }
> unicorn:~$ njobs walsh
> unicorn:~$ echo "$walsh"
> 3
> 
> Now you just need to add sanity-checking on the argument of njobs, to
> avoid whatever code injection the malicious caller wants to perform.

I can't fathom the switch to eval there. Why not printf -v "$1" %s "$_n", for 
example? It even rejects invalid identifiers.

-- 
Kerin Millar



Re: simple prob?

2021-06-29 Thread Eli Schwartz
On 6/29/21 4:21 PM, L A Walsh wrote:
> I hope a basic question isn't too offtopic.


Well, if you don't think this is a bug in bash, but something you need
help figuring out, maybe you'd prefer to use the "help-bash" list?


> Say I have some number of jobs running:
> 
>>  jobs|wc -l
> 3
> ---
> in a function (have tried shopt -s/-u lastpipe; neither way worked)
> njobs() {
> jobs |wc -l
> }
>>  njobs
> 3
> 
> Would like to pass a varname to njobs to store the answer in, like:
> njobs() {
> jobs|wc -l
> #magic puts val in $v
> printf {$1:+-v $1} "%s\n" "$v"


This is obviously broken, because {$1:+expand} isn't how bash works. Try
swapping the "{" and the "$"...


> }
> 
> So I can run:
> 
>>  njobs n
> echo "$n"
> 3
> 
> ---
> How can I put the output into '$v'
> *without* using a temporary file?
> 
> This seems so basic, yet its eluding me.
> Could someone throw me a clue-stick?
> Tnx!


What's wrong with the obvious

v=$(jobs | wc -l)


> p.s. - a trivial util func producing jobs:
> 
> resleep() { alias my="declare " int="my -i "
> int n=${1:-3} t=${2:-99}; echo "$n jobs @ ${t}s:"
> while ((0 < n--)); do sleep "$t" & done; }
> 
> 
> 
> 
> 


-- 
Eli Schwartz
Arch Linux Bug Wrangler and Trusted User



OpenPGP_signature
Description: OpenPGP digital signature


Re: simple prob?

2021-06-29 Thread Greg Wooledge
On Tue, Jun 29, 2021 at 01:21:44PM -0700, L A Walsh wrote:
> I hope a basic question isn't too offtopic.

More of a help-bash question, in my opinion, but close enough.

> Say I have some number of jobs running:
> 
> >  jobs|wc -l
> 3

OK.

> Would like to pass a varname to njobs to store the answer in, like:
> >  njobs n
> echo "$n"
> 3

"How do I pass a value back from a function to its caller, you know,
like a function in any other programming language can do" is one of the
holy grails of shell programming.  There is no sane answer.  You appear
to be going down the "pass a variable name by reference" path, so:

unicorn:~$ jobs | wc -l
3
unicorn:~$ njobs() { local _n=$(jobs | wc -l); eval "$1=\$_n"; }
unicorn:~$ njobs walsh
unicorn:~$ echo "$walsh"
3

Now you just need to add sanity-checking on the argument of njobs, to
avoid whatever code injection the malicious caller wants to perform.

And maybe consider adding "make sure $1 isn't _n" to the list of your
sanity checks, since the caller can't be permitted to use that particular
variable name.

unicorn:~$ njobs _n
unicorn:~$ echo "$_n"


(At least it didn't go into an infinite loop!)



simple prob?

2021-06-29 Thread L A Walsh

I hope a basic question isn't too offtopic.
Say I have some number of jobs running:


 jobs|wc -l

3
---
in a function (have tried shopt -s/-u lastpipe; neither way worked)
njobs() {
jobs |wc -l
}

 njobs

3

Would like to pass a varname to njobs to store the answer in, like:
njobs() {
jobs|wc -l
#magic puts val in $v
printf {$1:+-v $1} "%s\n" "$v"
}

So I can run:


 njobs n

echo "$n"
3

---
How can I put the output into '$v'
*without* using a temporary file?

This seems so basic, yet its eluding me.
Could someone throw me a clue-stick? 


Tnx!

p.s. - a trivial util func producing jobs:

resleep() { alias my="declare " int="my -i "
int n=${1:-3} t=${2:-99}; echo "$n jobs @ ${t}s:"
while ((0 < n--)); do sleep "$t" & done; }