Skip to content

Commit

Permalink
Security updates: Create new security module, use that module in the …
Browse files Browse the repository at this point in the history
…http & xenia modules, and remove requirement for web sessions; refactor Realm API and DB-based realm implementation
  • Loading branch information
cpurdy committed Nov 19, 2024
1 parent 564aa24 commit cac2299
Show file tree
Hide file tree
Showing 68 changed files with 5,055 additions and 2,735 deletions.
4 changes: 2 additions & 2 deletions doc/bnf.x
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ ArgumentList
"(" Arguments-opt ")"

Arguments
Argument
Arguments "," Argument
Argument ","-opt
Arguments "," Argument ","-opt

Argument
NamedArgument-opt ArgumentExpression
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ xdk-json = { group = "org.xtclang", name = "lib-json", version.ref = "xdk" }
xdk-jsondb = { group = "org.xtclang", name = "lib-jsondb", version.ref = "xdk" }
xdk-net = { group = "org.xtclang", name = "lib-net", version.ref = "xdk" }
xdk-oodb = { group = "org.xtclang", name = "lib-oodb", version.ref = "xdk" }
xdk-sec = { group = "org.xtclang", name = "lib-sec", version.ref = "xdk" }
xdk-web = { group = "org.xtclang", name = "lib-web", version.ref = "xdk" }
xdk-webauth = { group = "org.xtclang", name = "lib-webauth", version.ref = "xdk" }
xdk-xenia = { group = "org.xtclang", name = "lib-xenia", version.ref = "xdk" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4035,7 +4035,7 @@ protected void layerOnProp(
if (propBase == null && propContrib.isOverride())
{
log(errs, Severity.ERROR, VE_PROPERTY_OVERRIDE_NO_SPEC,
typeContrib.getValueString(), propContrib.getName());
typeContrib.removeAccess().getValueString(), propContrib.getName());
}

// the property is stored both by its absolute (fully qualified) ID and its nested
Expand Down
23 changes: 14 additions & 9 deletions javatools/src/main/java/org/xvm/compiler/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1855,7 +1855,7 @@ List<AstNode> parseConditionList()
{
List<AstNode> list = new ArrayList<>(4);
list.add(parseCondition(false));
while (match(Id.COMMA) != null)
while (match(Id.COMMA) != null && !peek(Id.R_PAREN))
{
list.add(parseCondition(false));
}
Expand All @@ -1864,14 +1864,14 @@ List<AstNode> parseConditionList()

AstNode parseCondition(boolean fNegated)
{
if (!fNegated && peek(Id.NOT) != null)
if (!fNegated && peek(Id.NOT))
{
// test for a negated conditional assignment
Token tokNot = peek();
Token tokNot = null;
AssignmentStatement stmtAsn = null;
try (SafeLookAhead attempt = new SafeLookAhead())
{
expect(Id.NOT);
tokNot = expect(Id.NOT);
if (match(Id.L_PAREN) != null)
{
AstNode stmtPeek = parseCondition(true);
Expand Down Expand Up @@ -2440,12 +2440,17 @@ MultipleLValueStatement peekMultiVariableInitializer()
listLVals.add(LVal);
}

Token comma = match(Id.COMMA);
if (!fFirst && match(Id.R_PAREN) != null)
{
return new MultipleLValueStatement(listLVals);
}

expect(Id.COMMA);
if (comma == null)
{
// comma is required
expect(Id.COMMA);
}
}
}

Expand Down Expand Up @@ -5463,8 +5468,8 @@ List<TypeExpression> parseParameterTypeList(boolean required)
* "(" Arguments-opt ")"
*
* Arguments
* Argument
* Arguments "," Argument
* Argument ","-opt
* Arguments "," Argument ","-opt
*
* Argument
* NamedArgument-opt ArgumentExpression
Expand Down Expand Up @@ -5880,7 +5885,7 @@ protected Token peek()
*
* @return true iff the next token matches
*/
protected Boolean peek(Token.Id id)
protected boolean peek(Token.Id id)
{
return peek().getId() == id;
}
Expand All @@ -5891,7 +5896,7 @@ protected Boolean peek(Token.Id id)
*
* @return true iff the next token is either an identifier or the "_" token
*/
protected Boolean peekNameOrAny()
protected boolean peekNameOrAny()
{
Token.Id id = peek().getId();
return id == Id.IDENTIFIER || id == Id.ANY;
Expand Down
1 change: 1 addition & 0 deletions javatools_bridge/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {
xtcModule(libs.xdk.crypto)
xtcModule(libs.xdk.json)
xtcModule(libs.xdk.net)
xtcModule(libs.xdk.sec)
xtcModule(libs.xdk.web)
}

Expand Down
199 changes: 140 additions & 59 deletions lib_ecstasy/src/main/x/ecstasy/text/String.x
Original file line number Diff line number Diff line change
Expand Up @@ -310,17 +310,21 @@ const String
* @param entrySeparator the character that separates each entry from the next entry in the
* the sequence of "map entries" represented by the String
* @param whitespace a function that identifies white space to strip off of keys and values
* @param valueQuote a function that identifies an opening balanced quote of a quoted value
*
* @return an array of Strings
* @return a map from `String` keys to `String` values
*/
Map<String!, String!> splitMap(Char kvSeparator ='=',
Char entrySeparator =',',
function Boolean(Char) whitespace = ch -> ch.isWhitespace()) {
Map<String!, String!> splitMap(
Char kvSeparator = '=',
Char entrySeparator = ',',
function Boolean(Char) whitespace = ch -> ch.isWhitespace(),
function Boolean(Char) valueQuote = _ -> False,
) {
if (size == 0) {
return [];
}

return new StringMap(this, kvSeparator, entrySeparator, whitespace);
return new StringMap(this, kvSeparator, entrySeparator, whitespace, valueQuote);
}

/**
Expand All @@ -329,10 +333,13 @@ const String
* sequential, e.g. a call to `get(k)` in the Map will return the value from the first entry
* with that key.
*/
protected static const StringMap(String data,
Char kvSep,
Char entrySep,
function Boolean(Char) whitespace)
protected static const StringMap(
String data,
Char kvSep,
Char entrySep,
function Boolean(Char) whitespace,
function Boolean(Char) valueQuote,
)
implements Map<String, String>
extends maps.KeyBasedMap<String, String> {

Expand All @@ -343,15 +350,52 @@ const String
@Lazy Int size.calc() = keyIterator().count();

@Override
Boolean empty.get() = False;
@Lazy Boolean empty.get() = keyIterator().next();

@Override
Boolean contains(String key) = find(key);

@Override
conditional String get(String key) {
if ((Int keyStart, Int sepOffset, Int valueEnd) := find(key)) {
return True, valueEnd > sepOffset+1 ? data[sepOffset >..< valueEnd].trim(whitespace) : "";
if (Int delimOffset := find(key)) {
Int length = data.size;
if (delimOffset >= length || data[delimOffset] == entrySep) {
return True, "";
}

// skip leading white space in the value
Int valStart = delimOffset + 1;
while (valStart < length && whitespace(data[valStart])) {
++valStart;
}

// it's possible that the entire value was whitespace
if (valStart >= length || data[valStart] == entrySep) {
return True, "";
}

// check for a quoted value
if (valueQuote(data[valStart])) {
Char quote = data[valStart++];
Int valStop = valStart;
while (valStop < length && data[valStop] != quote) {
++valStop;
}
return True, data[valStart..<valStop];
}

// otherwise, just scan until the entry separator is encountered
Int valStop = valStart;
while (valStop < length && data[valStop] != entrySep) {
++valStop;
}

// discard trailing whitespace
--valStop; // move backwards from the delim to the last character in the value
while (whitespace(data[valStop])) {
--valStop;
}
return True, data[valStart..valStop];
}
return False;
}
Expand All @@ -367,90 +411,127 @@ const String
if (offset >= length) {
return False;
}

// find the end of the entry
Int endEntry = length;
endEntry := data.indexOf(entrySep, offset);

// the delimiter between key and value is optional (i.e. value assumed
// to be "")
Int endKey = endEntry;
if (endKey := data.indexOf(kvSep, offset), endKey > endEntry) {
endKey = endEntry;
Int keyStart = offset;
Int keyEnd = offset;
while (offset < length) {
Char ch = data[offset++];
if (ch == kvSep) {
offset = skipValue(offset);
break;
} else if (ch == entrySep) {
break;
} else {
++keyEnd;
}
}

String key = data[offset ..< endKey].trim(whitespace);
offset = endEntry + 1;

return True, key;
return True, data[keyStart..<keyEnd].trim(whitespace);
}
};

/**
* Internal helper to find a "key in a map" that is actually in the underlying String.
*
* @param key the key to find
*
* @return True iff the key was found
* @return (conditional) the offset of the delimiter following the key (which may be OOB)
*/
protected conditional (Int keyStart, Int sepOffset, Int valueEnd) find(String key) {
protected conditional Int find(String key) {
String data = data;
Int length = data.size;
Int offset = 0;
Int keyLength = key.size;
Int keyOffset = 0;
EachEntry: while (offset + keyLength <= length) {
keyOffset = offset;

NextEntry: while (offset + keyLength <= length) {
// skip leading whitespace
while (whitespace(data[offset])) {
if (++offset >= length) {
return False;
}
}

// first, verify that the key would even fit
if (offset + keyLength > length) {
return False;
}

// match the key, character by character
Boolean match = True;
for (Char keyChar : key) {
if (offset >= length) {
return False;
}

Char mapChar = data[offset++];
if (mapChar != keyChar) {
if (mapChar == entrySep) {
continue EachEntry;
match = False;
--offset;
break;
}
}

// finish whatever remains of the key and the white space after it, up until the kv
// separator or the entry separator is encountered (or there are no more chars)
while (offset < length) {
Char ch = data[offset];
if (ch == entrySep) {
if (match) {
// key is followed immediately by the entry separator, so value is blank
return True, offset;
} else {
// wasn't a match: no value to skip, so try again to find the key
++offset;
continue NextEntry;
}
}

if (ch == kvSep) {
if (match) {
// key is followed immediately by the key separator, so value is next
return True, offset;
} else {
match = False;
// wasn't a match: skip the value, then try again to find the key
++offset;
break;
}
}
}

while (offset < length && whitespace(data[offset])) {
if (match && !whitespace(ch)) {
match = False;
}
++offset;
}

if (offset >= length) {
// key is at the very end, with no delimiter
return match, keyOffset, length, length;
}

Int sepOffset = offset;
Char sepChar = data[offset++];
if (match && sepChar == entrySep) {
// key is followed immediately by the entry separator, so value is blank
return True, keyOffset, sepOffset, sepOffset;
// we've passed the end of the data, so there is no following value, but we
// might have found the key; either way, the search is done
return match, offset;
}

// find the separator offset
while (offset < length && data[offset] != entrySep) {
++offset;
}
offset = skipValue(offset);
}

if (match && sepChar == kvSep) {
// we did find the key, and now we have found the end of the value
return True, keyOffset, sepOffset, offset;
}
return False;
}

/**
* @param the offset of the first character of a value in the current k/v pair, i.e. one
* character past the `kvSep`
*
* @return the offset of the first character of a key in the next k/v pair (or the first
* index past the end of the string)
*/
protected Int skipValue(Int offset) {
String data = data;
Int length = data.size;
while (offset < length && whitespace(data[offset])) {
++offset;
}

return False;
// skip past the quoted value (if the value is quoted)
if (offset < length && valueQuote(data[offset])) {
Char quote = data[offset++];
while (offset < length && data[offset++] != quote) {}
}

// skip everything else until one character past the entry separator
while (offset < length && data[offset++] != entrySep) {}
return offset;
}
}

Expand Down
3 changes: 3 additions & 0 deletions lib_jsondb/src/main/x/jsondb/Client.x
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,9 @@ service Client<Schema extends RootSchema> {
return newTx;
}

@Override
Connection clone() = this.Client.catalog.createConnection(dbUser).as(Connection);

@Override
void close(Exception? e = Null) {
super(e);
Expand Down
Loading

0 comments on commit cac2299

Please sign in to comment.